///|
/// This package is based on the Go implementation found here:
/// https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/crypto/sha1/sha1.go
/// which has the copyright notice:
/// Copyright 2009 The Go Authors. All rights reserved.
/// Use of this source code is governed by a BSD-style
/// license that can be found in the LICENSE file.
// The size of a sha1 checksum in bytes.
let size = 20
///|
/// The blocksize of sha1 in bytes.
let block_size = 64
///|
let chunk = 64
///|
let init0 = 0x67452301U
///|
let init1 = 0xEFCDAB89U
///|
let init2 = 0x98BADCFEU
///|
let init3 = 0x10325476U
///|
let init4 = 0xC3D2E1F0U
///|
/// `Digest` represents the partial evaluation of a checksum.
struct Digest {
h : FixedArray[UInt] // 5
x : FixedArray[Byte] // chunk
mut nx : Int
mut len : UInt64
}
///|
/// `Digest::new` returns a new, reset Digest, ready to sum.
pub fn Digest::new() -> Digest {
{
h: FixedArray::from_array([init0, init1, init2, init3, init4]),
x: FixedArray::make(chunk, b'\x00'),
nx: 0,
len: 0,
}
}
///|
/// `check_sum` returns the final sha1sum as a hex string.
pub fn Digest::check_sum(self : Digest) -> String {
let digest = self.sum()
let result = Buffer(size_hint=2 * size)
for b in digest {
result.write_char_utf16le(to_hex((b.to_int() >> 4) & 0xf))
result.write_char_utf16le(to_hex(b.to_int() & 0xf))
}
result.contents().to_unchecked_string()
}
///|
/// `sum` returns the final sha1sum as a `FixedArray[Byte]`.
fn Digest::sum(self : Digest) -> FixedArray[Byte] {
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
let tmp = FixedArray::make(64 + 8, b'\x00')
tmp[0] = b'\x80'
let t = if self.len % 64 < 56 {
(56UL - self.len % 64).to_int()
} else {
(64UL + 56UL - self.len % 64).to_int()
}
// Length in bits.
let len = self.len << 3
be_put_uint64(tmp, t, len) // append length in bits
for i = 0; i < t + 8; i = i + 1 {
guard! self.write_byte(tmp[i]) is None
}
// The previous write ensures that a whole number of
// blocks (i.e. a multiple of 64 bytes) have been hashed.
if self.nx != 0 {
panic()
}
// Generate digest
let digest = FixedArray::make(size, b'\x00')
be_put_uint32(digest, 0, self.h[0])
be_put_uint32(digest, 4, self.h[1])
be_put_uint32(digest, 8, self.h[2])
be_put_uint32(digest, 12, self.h[3])
be_put_uint32(digest, 16, self.h[4])
digest
}
///|
/// `reset` resets a digest for re-use.
pub fn Digest::reset(self : Digest) -> Unit {
self.h[0] = init0
self.h[1] = init1
self.h[2] = init2
self.h[3] = init3
self.h[4] = init4
self.nx = 0
self.len = 0
}
///|
let _trait : &@io.Writer = Digest::new()
///|
/// `write` writes a slice of bytes to the digest.
pub impl @io.Writer for Digest with fn write(self, buf) {
let mut count = 0
for b in buf {
guard self.write_byte(b) is None else { break }
count += 1
}
(count, None)
}
///|
/// `write_byte` writes a byte to the digest.
pub impl @io.ByteWriter for Digest with fn write_byte(self, b) {
self.len += 1
self.x[self.nx] = b
self.nx += 1
if self.nx == block_size {
self.block_generic()
self.nx = 0
}
None
}
///|
let _K0 = 0x5A827999U
///|
let _K1 = 0x6ED9EBA1U
///|
let _K2 = 0x8F1BBCDCU
///|
let _K3 = 0xCA62C1D6U
///|
fn Digest::block_generic(self : Digest) -> Unit {
let w = FixedArray::make(16, 0U)
//
let p = self.x
for i = 0; i < 16; i = i + 1 {
let j = i * 4
w[i] = (p[j].to_uint() << 24) |
(p[j + 1].to_uint() << 16) |
(p[j + 2].to_uint() << 8) |
p[j + 3].to_uint()
}
//
let mut a = self.h[0]
let mut b = self.h[1]
let mut c = self.h[2]
let mut d = self.h[3]
let mut e = self.h[4]
// Each of the four 20-iteration rounds
// differs only in the computation of f and
// the choice of K (_K0, _K1, etc).
for i = 0; i < 16; i = i + 1 {
let f = (b & c) | (b.lnot() & d)
let t = rotl(a, 5) + f + e + w[i & 0xf] + _K0
let tmp = (t, a, rotl(b, 30), c, d)
a = tmp.0
b = tmp.1
c = tmp.2
d = tmp.3
e = tmp.4
}
for i = 16; i < 20; i = i + 1 {
let tmp = w[(i - 3) & 0xf] ^
w[(i - 8) & 0xf] ^
w[(i - 14) & 0xf] ^
w[i & 0xf]
w[i & 0xf] = rotl(tmp, 1)
let f = (b & c) | (b.lnot() & d)
let t = rotl(a, 5) + f + e + w[i & 0xf] + _K0
let tmp = (t, a, rotl(b, 30), c, d)
a = tmp.0
b = tmp.1
c = tmp.2
d = tmp.3
e = tmp.4
}
for i = 20; i < 40; i = i + 1 {
let tmp = w[(i - 3) & 0xf] ^
w[(i - 8) & 0xf] ^
w[(i - 14) & 0xf] ^
w[i & 0xf]
w[i & 0xf] = rotl(tmp, 1)
let f = b ^ c ^ d
let t = rotl(a, 5) + f + e + w[i & 0xf] + _K1
let tmp = (t, a, rotl(b, 30), c, d)
a = tmp.0
b = tmp.1
c = tmp.2
d = tmp.3
e = tmp.4
}
for i = 40; i < 60; i = i + 1 {
let tmp = w[(i - 3) & 0xf] ^
w[(i - 8) & 0xf] ^
w[(i - 14) & 0xf] ^
w[i & 0xf]
w[i & 0xf] = rotl(tmp, 1)
let f = ((b | c) & d) | (b & c)
let t = rotl(a, 5) + f + e + w[i & 0xf] + _K2
let tmp = (t, a, rotl(b, 30), c, d)
a = tmp.0
b = tmp.1
c = tmp.2
d = tmp.3
e = tmp.4
}
for i = 60; i < 80; i = i + 1 {
let tmp = w[(i - 3) & 0xf] ^
w[(i - 8) & 0xf] ^
w[(i - 14) & 0xf] ^
w[i & 0xf]
w[i & 0xf] = rotl(tmp, 1)
let f = b ^ c ^ d
let t = rotl(a, 5) + f + e + w[i & 0xf] + _K3
let tmp = (t, a, rotl(b, 30), c, d)
a = tmp.0
b = tmp.1
c = tmp.2
d = tmp.3
e = tmp.4
}
// save state
self.h[0] += a
self.h[1] += b
self.h[2] += c
self.h[3] += d
self.h[4] += e
}
///|
fn be_put_uint32(b : FixedArray[Byte], offset : Int, value : UInt) -> Unit {
b[offset + 3] = (value & 0xff).to_byte()
b[offset + 2] = ((value >> 8) & 0xff).to_byte()
b[offset + 1] = ((value >> 16) & 0xff).to_byte()
b[offset] = ((value >> 24) & 0xff).to_byte()
}
///|
fn be_put_uint64(b : FixedArray[Byte], offset : Int, value : UInt64) -> Unit {
be_put_uint32(b, offset + 4, (value & 0xffffffff).to_uint())
be_put_uint32(b, offset, ((value >> 32) & 0xffffffff).to_uint())
}
///|
fn rotl(x : UInt, r : Int) -> UInt {
(x << r) | (x >> (32 - r))
}
///|
fn to_hex(v : Int) -> Char {
if v < 10 {
Int::unsafe_to_char(v + b'0'.to_int())
} else {
Int::unsafe_to_char(v - 10 + b'a'.to_int())
}
}