crypto md5

pull/1181/head
joe-conigliaro 2019-07-16 22:20:51 +10:00 committed by Alexander Medvednikov
parent 8c516bec4f
commit 9c586e7e92
6 changed files with 302 additions and 13 deletions

View File

@ -0,0 +1,143 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
// Package md5 implements the MD5 hash algorithm as defined in RFC 1321.
// MD5 is cryptographically broken and should not be used for secure
// applications.
// Adapted from: https://github.com/golang/go/blob/master/src/crypto/md5
module md5
import math
import encoding.binary
const (
// The size of an MD5 checksum in bytes.
Size = 16
// The blocksize of MD5 in bytes.
BlockSize = 64
)
const (
Init0 = 0x67452301
Init1 = 0xEFCDAB89
Init2 = 0x98BADCFE
Init3 = 0x10325476
)
// Digest represents the partial evaluation of a checksum.
struct Digest {
mut:
s []u32
x []byte
nx int
len u64
}
fn (d mut Digest) reset() {
d.s = [u32(0); 4]
d.x = [byte(0); BlockSize]
d.s[0] = u32(Init0)
d.s[1] = u32(Init1)
d.s[2] = u32(Init2)
d.s[3] = u32(Init3)
d.nx = 0
d.len = u64(0)
}
// New returns a new hash.Hash computing the MD5 checksum.
pub fn new() *Digest {
mut d := &Digest{}
d.reset()
return d
}
pub fn (d mut Digest) write(p []byte) ?int {
nn := p.len
d.len += u64(nn)
if d.nx > 0 {
n := int(math.min(f64(d.x.len), f64(p.len)))
for i:=0; i<n; i++ {
d.x.set(i+d.nx, p[i])
}
d.nx += n
if d.nx == BlockSize {
block_generic(d, d.x)
d.nx = 0
}
if n >= p.len {
p = []byte
} else {
p = p.right(n)
}
}
if p.len >= BlockSize {
n := p.len &~ (BlockSize - 1)
block_generic(d, p.left(n))
if n >= p.len {
p = []byte
} else {
p = p.right(n)
}
}
if p.len > 0 {
d.nx = int(math.min(f64(d.x.len), f64(p.len)))
for i:=0; i<d.nx; i++ {
d.x.set(i, p[i])
}
}
return nn
}
pub fn (d &Digest) sum(b_in mut []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
mut d0 := *d
hash := d0.checksum()
for b in hash {
b_in << b
}
return *b_in
}
pub fn (d mut Digest) checksum() []byte {
// Append 0x80 to the end of the message and then append zeros
// until the length is a multiple of 56 bytes. Finally append
// 8 bytes representing the message length in bits.
//
// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
// tmp := [1 + 63 + 8]byte{0x80}
mut tmp := [byte(0); 1 + 63 + 8]
tmp[0] = 0x80
pad := (55 - int(d.len)) % 64 // calculate number of padding bytes
binary.little_endian_put_u64(tmp.right(1+pad), u64(d.len<<u64(3))) // append length in bits
d.write(tmp.left(1+pad+8))
// The previous write ensures that a whole number of
// blocks (i.e. a multiple of 64 bytes) have been hashed.
if d.nx != 0 {
panic('d.nx != 0')
}
digest := [byte(0); Size]
binary.little_endian_put_u32(digest, d.s[0])
binary.little_endian_put_u32(digest.right(4), d.s[1])
binary.little_endian_put_u32(digest.right(8), d.s[2])
binary.little_endian_put_u32(digest.right(12), d.s[3])
return digest
}
// Sum returns the MD5 checksum of the data.
// pub fn Sum(data []byte) [Size]byte {
pub fn sum(data []byte) []byte {
mut d := new()
d.write(data)
return d.checksum()
}
pub fn (d &Digest) size() int { return Size }
pub fn (d &Digest) block_size() int { return BlockSize }

View File

@ -0,0 +1,9 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import crypto.md5
fn test_crypto_md5() {
assert md5.sum('this is a md5 checksum.'.bytes()).hex() == '6FB421FF99036547655984DA12973431'
}

View File

@ -0,0 +1,129 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
// This is a generic implementation with no arch optimizations
module md5
import math.bits
import encoding.binary
fn block_generic(dig &Digest, p []byte) {
// load state
mut a := dig.s[0]
mut b := dig.s[1]
mut c := dig.s[2]
mut d := dig.s[3]
for i := 0; i <= p.len-BlockSize; i += BlockSize {
mut q := p.right(i)
q = q.left(BlockSize)
// save current state
aa := a
bb := b
cc := c
dd := d
// load input block
x0 := binary.little_endian_u32(q.right(4*0x0))
x1 := binary.little_endian_u32(q.right(4*0x1))
x2 := binary.little_endian_u32(q.right(4*0x2))
x3 := binary.little_endian_u32(q.right(4*0x3))
x4 := binary.little_endian_u32(q.right(4*0x4))
x5 := binary.little_endian_u32(q.right(4*0x5))
x6 := binary.little_endian_u32(q.right(4*0x6))
x7 := binary.little_endian_u32(q.right(4*0x7))
x8 := binary.little_endian_u32(q.right(4*0x8))
x9 := binary.little_endian_u32(q.right(4*0x9))
xa := binary.little_endian_u32(q.right(4*0xa))
xb := binary.little_endian_u32(q.right(4*0xb))
xc := binary.little_endian_u32(q.right(4*0xc))
xd := binary.little_endian_u32(q.right(4*0xd))
xe := binary.little_endian_u32(q.right(4*0xe))
xf := binary.little_endian_u32(q.right(4*0xf))
// round 1
a = b + bits.rotate_left_32((((c^d)&b)^d)+a+x0+u32(0xd76aa478), 7)
d = a + bits.rotate_left_32((((b^c)&a)^c)+d+x1+u32(0xe8c7b756), 12)
c = d + bits.rotate_left_32((((a^b)&d)^b)+c+x2+u32(0x242070db), 17)
b = c + bits.rotate_left_32((((d^a)&c)^a)+b+x3+u32(0xc1bdceee), 22)
a = b + bits.rotate_left_32((((c^d)&b)^d)+a+x4+u32(0xf57c0faf), 7)
d = a + bits.rotate_left_32((((b^c)&a)^c)+d+x5+u32(0x4787c62a), 12)
c = d + bits.rotate_left_32((((a^b)&d)^b)+c+x6+u32(0xa8304613), 17)
b = c + bits.rotate_left_32((((d^a)&c)^a)+b+x7+u32(0xfd469501), 22)
a = b + bits.rotate_left_32((((c^d)&b)^d)+a+x8+u32(0x698098d8), 7)
d = a + bits.rotate_left_32((((b^c)&a)^c)+d+x9+u32(0x8b44f7af), 12)
c = d + bits.rotate_left_32((((a^b)&d)^b)+c+xa+u32(0xffff5bb1), 17)
b = c + bits.rotate_left_32((((d^a)&c)^a)+b+xb+u32(0x895cd7be), 22)
a = b + bits.rotate_left_32((((c^d)&b)^d)+a+xc+u32(0x6b901122), 7)
d = a + bits.rotate_left_32((((b^c)&a)^c)+d+xd+u32(0xfd987193), 12)
c = d + bits.rotate_left_32((((a^b)&d)^b)+c+xe+u32(0xa679438e), 17)
b = c + bits.rotate_left_32((((d^a)&c)^a)+b+xf+u32(0x49b40821), 22)
// round 2
a = b + bits.rotate_left_32((((b^c)&d)^c)+a+x1+u32(0xf61e2562), 5)
d = a + bits.rotate_left_32((((a^b)&c)^b)+d+x6+u32(0xc040b340), 9)
c = d + bits.rotate_left_32((((d^a)&b)^a)+c+xb+u32(0x265e5a51), 14)
b = c + bits.rotate_left_32((((c^d)&a)^d)+b+x0+u32(0xe9b6c7aa), 20)
a = b + bits.rotate_left_32((((b^c)&d)^c)+a+x5+u32(0xd62f105d), 5)
d = a + bits.rotate_left_32((((a^b)&c)^b)+d+xa+u32(0x02441453), 9)
c = d + bits.rotate_left_32((((d^a)&b)^a)+c+xf+u32(0xd8a1e681), 14)
b = c + bits.rotate_left_32((((c^d)&a)^d)+b+x4+u32(0xe7d3fbc8), 20)
a = b + bits.rotate_left_32((((b^c)&d)^c)+a+x9+u32(0x21e1cde6), 5)
d = a + bits.rotate_left_32((((a^b)&c)^b)+d+xe+u32(0xc33707d6), 9)
c = d + bits.rotate_left_32((((d^a)&b)^a)+c+x3+u32(0xf4d50d87), 14)
b = c + bits.rotate_left_32((((c^d)&a)^d)+b+x8+u32(0x455a14ed), 20)
a = b + bits.rotate_left_32((((b^c)&d)^c)+a+xd+u32(0xa9e3e905), 5)
d = a + bits.rotate_left_32((((a^b)&c)^b)+d+x2+u32(0xfcefa3f8), 9)
c = d + bits.rotate_left_32((((d^a)&b)^a)+c+x7+u32(0x676f02d9), 14)
b = c + bits.rotate_left_32((((c^d)&a)^d)+b+xc+u32(0x8d2a4c8a), 20)
// round 3
a = b + bits.rotate_left_32((b^c^d)+a+x5+u32(0xfffa3942), 4)
d = a + bits.rotate_left_32((a^b^c)+d+x8+u32(0x8771f681), 11)
c = d + bits.rotate_left_32((d^a^b)+c+xb+u32(0x6d9d6122), 16)
b = c + bits.rotate_left_32((c^d^a)+b+xe+u32(0xfde5380c), 23)
a = b + bits.rotate_left_32((b^c^d)+a+x1+u32(0xa4beea44), 4)
d = a + bits.rotate_left_32((a^b^c)+d+x4+u32(0x4bdecfa9), 11)
c = d + bits.rotate_left_32((d^a^b)+c+x7+u32(0xf6bb4b60), 16)
b = c + bits.rotate_left_32((c^d^a)+b+xa+u32(0xbebfbc70), 23)
a = b + bits.rotate_left_32((b^c^d)+a+xd+u32(0x289b7ec6), 4)
d = a + bits.rotate_left_32((a^b^c)+d+x0+u32(0xeaa127fa), 11)
c = d + bits.rotate_left_32((d^a^b)+c+x3+u32(0xd4ef3085), 16)
b = c + bits.rotate_left_32((c^d^a)+b+x6+u32(0x04881d05), 23)
a = b + bits.rotate_left_32((b^c^d)+a+x9+u32(0xd9d4d039), 4)
d = a + bits.rotate_left_32((a^b^c)+d+xc+u32(0xe6db99e5), 11)
c = d + bits.rotate_left_32((d^a^b)+c+xf+u32(0x1fa27cf8), 16)
b = c + bits.rotate_left_32((c^d^a)+b+x2+u32(0xc4ac5665), 23)
// round 4
a = b + bits.rotate_left_32((c^(b|~d))+a+x0+u32(0xf4292244), 6)
d = a + bits.rotate_left_32((b^(a|~c))+d+x7+u32(0x432aff97), 10)
c = d + bits.rotate_left_32((a^(d|~b))+c+xe+u32(0xab9423a7), 15)
b = c + bits.rotate_left_32((d^(c|~a))+b+x5+u32(0xfc93a039), 21)
a = b + bits.rotate_left_32((c^(b|~d))+a+xc+u32(0x655b59c3), 6)
d = a + bits.rotate_left_32((b^(a|~c))+d+x3+u32(0x8f0ccc92), 10)
c = d + bits.rotate_left_32((a^(d|~b))+c+xa+u32(0xffeff47d), 15)
b = c + bits.rotate_left_32((d^(c|~a))+b+x1+u32(0x85845dd1), 21)
a = b + bits.rotate_left_32((c^(b|~d))+a+x8+u32(0x6fa87e4f), 6)
d = a + bits.rotate_left_32((b^(a|~c))+d+xf+u32(0xfe2ce6e0), 10)
c = d + bits.rotate_left_32((a^(d|~b))+c+x6+u32(0xa3014314), 15)
b = c + bits.rotate_left_32((d^(c|~a))+b+xd+u32(0x4e0811a1), 21)
a = b + bits.rotate_left_32((c^(b|~d))+a+x4+u32(0xf7537e82), 6)
d = a + bits.rotate_left_32((b^(a|~c))+d+xb+u32(0xbd3af235), 10)
c = d + bits.rotate_left_32((a^(d|~b))+c+x2+u32(0x2ad7d2bb), 15)
b = c + bits.rotate_left_32((d^(c|~a))+b+x9+u32(0xeb86d391), 21)
// add saved state
a += aa
b += bb
c += cc
d += dd
}
// save state
dig.s[0] = a
dig.s[1] = b
dig.s[2] = c
dig.s[3] = d
}

View File

@ -3,16 +3,17 @@
// that can be found in the LICENSE file.
// Package sha1 implements the SHA-1 hash algorithm as defined in RFC 3174.
//
// SHA-1 is cryptographically broken and should not be used for secure
// applications.
// Adapted from: https://github.com/golang/go/blob/master/src/crypto/sha1
module sha1
import math
import encoding.binary
const(
// The size of a SHA-1 checksum in bytes.
Size = 20
@ -68,7 +69,7 @@ pub fn (d mut Digest) write(p []byte) ?int {
}
d.nx += n
if d.nx == Chunk {
block(d, d.x)
block_generic(d, d.x)
d.nx = 0
}
if n >= p.len {
@ -79,7 +80,7 @@ pub fn (d mut Digest) write(p []byte) ?int {
}
if p.len >= Chunk {
n := p.len &~ (Chunk - 1)
block(d, p.left(n))
block_generic(d, p.left(n))
if n >= p.len {
p = []byte
} else {
@ -98,14 +99,14 @@ pub fn (d mut Digest) write(p []byte) ?int {
pub fn (d &Digest) sum(b_in mut []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
mut d0 := *d
hash := d0.check_sum()
hash := d0.checksum()
for b in hash {
b_in << b
}
return *b_in
}
fn (d mut Digest) check_sum() []byte {
fn (d mut Digest) checksum() []byte {
mut len := d.len
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
mut tmp := [byte(0); 64]
@ -136,10 +137,9 @@ fn (d mut Digest) check_sum() []byte {
// Sum returns the SHA-1 checksum of the data.
pub fn sum(data []byte) []byte {
mut d := Digest{}
d.reset()
mut d := new()
d.write(data)
return d.check_sum()
return d.checksum()
}
pub fn (d &Digest) size() int { return Size }

View File

@ -1,5 +1,9 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
import crypto.sha1
fn test_crypto_sha1() {
assert sha1.sum('This is a sha1 hash.'.bytes()).hex() == '6FF5FA4D5166D5C2576FE56ED1EC2D5AB0FDF936'
assert sha1.sum('This is a sha1 checksum.'.bytes()).hex() == 'E100D74442FAA5DCD59463B808983C810A8EB5A1'
}

View File

@ -1,3 +1,9 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
// This is a generic implementation with no arch optimizations
module sha1
import math.bits
@ -9,7 +15,7 @@ const (
_K3 = 0xCA62C1D6
)
fn block(dig &Digest, p []byte) {
fn block_generic(dig &Digest, p []byte) {
mut w := [u32(0); 16]
mut h0 := dig.h[0]
mut h1 := dig.h[1]
@ -73,13 +79,11 @@ fn block(dig &Digest, p []byte) {
w[i&0xf] = u32(tmp<<u32(1)) | u32(tmp>>u32(32-1))
f := ((b | c) & d) | (b & c)
t := bits.rotate_left_32(a, 5) + f + e + w[i&0xf] + u32(_K2)
e = d
d = c
c = bits.rotate_left_32(b, 30)
b = a
a = t
i++
}
for i < 80 {