diff --git a/CHANGELOG.md b/CHANGELOG.md index ff96738..1f90b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Optimize `Ripemd160` (#20). + +### Fixed + +- `Ripemd160` and `Hmac` Digests now guard against accidentally writing more data after calling `sum()`. + ## [0.2.0] - 2026-04-22 ### Changed diff --git a/bench/ripemd160.bench.mo b/bench/ripemd160.bench.mo new file mode 100644 index 0000000..4667e65 --- /dev/null +++ b/bench/ripemd160.bench.mo @@ -0,0 +1,65 @@ +import Array "mo:core/Array"; +import Nat8 "mo:core/Nat8"; + +import Bench "mo:bench-helper"; + +import Ripemd160 "../src/Ripemd160"; + +module { + public func init() : Bench.V1 { + let schema : Bench.Schema = { + name = "RIPEMD-160"; + description = "RIPEMD-160 one-shot hash across message sizes, plus incremental digest"; + rows = ["hash (one-shot)", "Digest (incremental, 64-byte chunks)"]; + cols = ["len 0", "len 32", "len 64", "len 256", "len 1024"]; + }; + + let datas : [[Nat8]] = [ + [], + Array.tabulate(32, func i { Nat8.fromNat((i * 3 + 1) % 256) }), + Array.tabulate(64, func i { Nat8.fromNat((i * 7 + 5) % 256) }), + Array.tabulate(256, func i { Nat8.fromNat((i * 11 + 13) % 256) }), + Array.tabulate(1024, func i { Nat8.fromNat((i * 17 + 9) % 256) }), + ]; + + // Pre-split each input into 64-byte chunks for the incremental row. + let chunkedDatas : [[[Nat8]]] = Array.tabulate<[[Nat8]]>( + datas.size(), + func(ci) { + let data = datas[ci]; + let total = data.size(); + if (total == 0) { + []; + } else { + let nChunks = (total + 63) / 64; + Array.tabulate<[Nat8]>( + nChunks, + func(k) { + let start = k * 64; + let end = if (start + 64 < total) { start + 64 } else { total }; + Array.tabulate(end - start, func(j) { data[start + j] }); + }, + ); + }; + }, + ); + + func run(ri : Nat, ci : Nat) { + switch (ri) { + case (0) { + ignore Ripemd160.hash(datas[ci]); + }; + case (1) { + let d = Ripemd160.Digest(); + for (chunk in chunkedDatas[ci].values()) { + d.write(chunk); + }; + ignore d.sum(); + }; + case (_) {}; + }; + }; + + Bench.V1(schema, run); + }; +}; diff --git a/src/Hmac.mo b/src/Hmac.mo index 2f2f1b8..a9c9c2f 100644 --- a/src/Hmac.mo +++ b/src/Hmac.mo @@ -15,8 +15,18 @@ module { create : () -> Digest; }; + // HMAC instance. + // + // NOTE: An Hmac is one-shot. Calling `sum()` finalizes and consumes the + // instance: `closed` is set, and any subsequent call to `sum()` or + // `writeArray()` will trap. To compute another HMAC, construct a new Hmac. public type Hmac = { + // Append `data` to the message being authenticated. Traps if the Hmac + // has already been finalized via `sum()`. writeArray : ([Nat8]) -> (); + // Finalize the HMAC and return the tag. Consumes the Hmac: the instance + // becomes closed, and any further call to `sum()` or `writeArray()` + // will trap. Callers must create a new Hmac for another computation. sum : () -> Blob; }; @@ -48,6 +58,7 @@ module { let outerDigest : Digest = digestFactory.create(); let innerPad : Nat8 = 0x36; let outerPad : Nat8 = 0x5c; + var closed = false; do { let blockSize = digestFactory.blockSize; @@ -99,10 +110,13 @@ module { }; public func writeArray(data : [Nat8]) { + assert (not closed); innerDigest.writeArray(data); }; public func sum() : Blob { + assert (not closed); + closed := true; let innerHash = innerDigest.sum().toArray(); outerDigest.writeArray(innerHash); outerDigest.sum(); diff --git a/src/Ripemd160.mo b/src/Ripemd160.mo index ef14479..4300474 100644 --- a/src/Ripemd160.mo +++ b/src/Ripemd160.mo @@ -1,8 +1,9 @@ -import Nat "mo:core/Nat"; +import Nat8 "mo:core/Nat8"; +import Nat16 "mo:core/Nat16"; +import Nat32 "mo:core/Nat32"; import Nat64 "mo:core/Nat64"; import VarArray "mo:core/VarArray"; - -import Common "Common"; +import Prim "mo:prim"; module { // Hash the given array and return finalized result. @@ -13,9 +14,26 @@ module { }; public class Digest() { + // Persistent chaining state: 5 Nat32 words (h0..h4). + // Stored inline in a [var Nat32] so per-block updates do not allocate. private let s : [var Nat32] = VarArray.repeat(0, 5); - private let buf : [var Nat8] = VarArray.repeat(0, 64); - private var bytes : Nat64 = 0; + + // Decoded message schedule for the block currently being assembled. + // Each slot holds one little-endian 32-bit word; bytes are folded in + // by writeByte and full 4-byte words are written wholesale by the + // fast path in write(). Stored inline in a [var Nat32] so updates + // inside the hot loop do not allocate. + private let msg : [var Nat32] = VarArray.repeat(0, 16); + + // Number of bytes accumulated into the current (partial) block, 0..63. + // Nat16 fits unboxed in mutable storage, avoiding heap traffic per byte. + private var i_msg : Nat16 = 0; + + // Number of complete 64-byte blocks already absorbed. + private var n_blocks : Nat64 = 0; + + // Flag to close Digest after call to sum() + private var closed = false; private func initialize() { s[0] := 0x67452301; @@ -23,175 +41,43 @@ module { s[2] := 0x98BADCFE; s[3] := 0x10325476; s[4] := 0xC3D2E1F0; + i_msg := 0; + n_blocks := 0; + // msg slots are overwritten before being read, so no clear needed here. }; initialize(); public func reset() { - bytes := 0; initialize(); + closed := false; }; - private func f1(x : Nat32, y : Nat32, z : Nat32) : Nat32 { - x ^ y ^ z; - }; - - private func f2(x : Nat32, y : Nat32, z : Nat32) : Nat32 { - (x & y) | (^x & z); - }; - - private func f3(x : Nat32, y : Nat32, z : Nat32) : Nat32 { - (x | ^y) ^ z; - }; - - private func f4(x : Nat32, y : Nat32, z : Nat32) : Nat32 { - (x & z) | (y & ^z); - }; - - private func f5(x : Nat32, y : Nat32, z : Nat32) : Nat32 { - x ^ (y | ^z); - }; - - private func rol(x : Nat32, i : Nat32) : Nat32 { - (x << i) | (x >> (32 - i)); - }; - - private func round( - a : Nat32, - _b : Nat32, - c : Nat32, - _d : Nat32, - e : Nat32, - f : Nat32, - x : Nat32, - k : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - (rol(a +% f +% x +% k, r) +% e, rol(c, 10)); - }; - - private func r11( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f1(b, c, d), x, 0, r); - }; - - private func r21( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f2(b, c, d), x, 0x5A827999, r); - }; - - private func r31( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f3(b, c, d), x, 0x6ED9EBA1, r); - }; - - private func r41( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f4(b, c, d), x, 0x8F1BBCDC, r); + private func rol(x : Nat32, r : Nat32) : Nat32 { + (x << r) | (x >> (32 - r)); }; - private func r51( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f5(b, c, d), x, 0xA953FD4E, r); - }; - - private func r12( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f5(b, c, d), x, 0x50A28BE6, r); - }; - - private func r22( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f4(b, c, d), x, 0x5C4DD124, r); - }; - - private func r32( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f3(b, c, d), x, 0x6D703EF3, r); - }; + // Process the 16-word block currently held in msg, updating s. + // The round structure is the standard RIPEMD-160 two-line schedule + // with all 160 rounds inlined to avoid per-round tuple allocations. + private func transform() { + let w0 = msg[0]; + let w1 = msg[1]; + let w2 = msg[2]; + let w3 = msg[3]; + let w4 = msg[4]; + let w5 = msg[5]; + let w6 = msg[6]; + let w7 = msg[7]; + let w8 = msg[8]; + let w9 = msg[9]; + let w10 = msg[10]; + let w11 = msg[11]; + let w12 = msg[12]; + let w13 = msg[13]; + let w14 = msg[14]; + let w15 = msg[15]; - private func r42( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f2(b, c, d), x, 0x7A6D76E9, r); - }; - - private func r52( - a : Nat32, - b : Nat32, - c : Nat32, - d : Nat32, - e : Nat32, - x : Nat32, - r : Nat32, - ) : (Nat32, Nat32) { - round(a, b, c, d, e, f1(b, c, d), x, 0, r); - }; - - // Perform a RIPEMD-160 transformation, processing a 64-byte chunk. - private func transform(chunk : [Nat8], offset : Nat) { var a1 : Nat32 = s[0]; var b1 : Nat32 = s[1]; var c1 : Nat32 = s[2]; @@ -202,508 +88,191 @@ module { var c2 : Nat32 = c1; var d2 : Nat32 = d1; var e2 : Nat32 = e1; - let w0 : Nat32 = Common.readLE32(chunk, offset); - let w1 : Nat32 = Common.readLE32(chunk, offset + 4); - let w2 : Nat32 = Common.readLE32(chunk, offset + 8); - let w3 : Nat32 = Common.readLE32(chunk, offset + 12); - let w4 : Nat32 = Common.readLE32(chunk, offset + 16); - let w5 : Nat32 = Common.readLE32(chunk, offset + 20); - let w6 : Nat32 = Common.readLE32(chunk, offset + 24); - let w7 : Nat32 = Common.readLE32(chunk, offset + 28); - let w8 : Nat32 = Common.readLE32(chunk, offset + 32); - let w9 : Nat32 = Common.readLE32(chunk, offset + 36); - let w10 : Nat32 = Common.readLE32(chunk, offset + 40); - let w11 : Nat32 = Common.readLE32(chunk, offset + 44); - let w12 : Nat32 = Common.readLE32(chunk, offset + 48); - let w13 : Nat32 = Common.readLE32(chunk, offset + 52); - let w14 : Nat32 = Common.readLE32(chunk, offset + 56); - let w15 : Nat32 = Common.readLE32(chunk, offset + 60); - - var temp : (Nat32, Nat32) = r11(a1, b1, c1, d1, e1, w0, 11); - a1 := temp.0; - c1 := temp.1; - temp := r12(a2, b2, c2, d2, e2, w5, 8); - a2 := temp.0; - c2 := temp.1; - temp := r11(e1, a1, b1, c1, d1, w1, 14); - e1 := temp.0; - b1 := temp.1; - temp := r12(e2, a2, b2, c2, d2, w14, 9); - e2 := temp.0; - b2 := temp.1; - temp := r11(d1, e1, a1, b1, c1, w2, 15); - d1 := temp.0; - a1 := temp.1; - temp := r12(d2, e2, a2, b2, c2, w7, 9); - d2 := temp.0; - a2 := temp.1; - temp := r11(c1, d1, e1, a1, b1, w3, 12); - c1 := temp.0; - e1 := temp.1; - temp := r12(c2, d2, e2, a2, b2, w0, 11); - c2 := temp.0; - e2 := temp.1; - temp := r11(b1, c1, d1, e1, a1, w4, 5); - b1 := temp.0; - d1 := temp.1; - temp := r12(b2, c2, d2, e2, a2, w9, 13); - b2 := temp.0; - d2 := temp.1; - temp := r11(a1, b1, c1, d1, e1, w5, 8); - a1 := temp.0; - c1 := temp.1; - temp := r12(a2, b2, c2, d2, e2, w2, 15); - a2 := temp.0; - c2 := temp.1; - temp := r11(e1, a1, b1, c1, d1, w6, 7); - e1 := temp.0; - b1 := temp.1; - temp := r12(e2, a2, b2, c2, d2, w11, 15); - e2 := temp.0; - b2 := temp.1; - temp := r11(d1, e1, a1, b1, c1, w7, 9); - d1 := temp.0; - a1 := temp.1; - temp := r12(d2, e2, a2, b2, c2, w4, 5); - d2 := temp.0; - a2 := temp.1; - temp := r11(c1, d1, e1, a1, b1, w8, 11); - c1 := temp.0; - e1 := temp.1; - temp := r12(c2, d2, e2, a2, b2, w13, 7); - c2 := temp.0; - e2 := temp.1; - temp := r11(b1, c1, d1, e1, a1, w9, 13); - b1 := temp.0; - d1 := temp.1; - temp := r12(b2, c2, d2, e2, a2, w6, 7); - b2 := temp.0; - d2 := temp.1; - temp := r11(a1, b1, c1, d1, e1, w10, 14); - a1 := temp.0; - c1 := temp.1; - temp := r12(a2, b2, c2, d2, e2, w15, 8); - a2 := temp.0; - c2 := temp.1; - temp := r11(e1, a1, b1, c1, d1, w11, 15); - e1 := temp.0; - b1 := temp.1; - temp := r12(e2, a2, b2, c2, d2, w8, 11); - e2 := temp.0; - b2 := temp.1; - temp := r11(d1, e1, a1, b1, c1, w12, 6); - d1 := temp.0; - a1 := temp.1; - temp := r12(d2, e2, a2, b2, c2, w1, 14); - d2 := temp.0; - a2 := temp.1; - temp := r11(c1, d1, e1, a1, b1, w13, 7); - c1 := temp.0; - e1 := temp.1; - temp := r12(c2, d2, e2, a2, b2, w10, 14); - c2 := temp.0; - e2 := temp.1; - temp := r11(b1, c1, d1, e1, a1, w14, 9); - b1 := temp.0; - d1 := temp.1; - temp := r12(b2, c2, d2, e2, a2, w3, 12); - b2 := temp.0; - d2 := temp.1; - temp := r11(a1, b1, c1, d1, e1, w15, 8); - a1 := temp.0; - c1 := temp.1; - temp := r12(a2, b2, c2, d2, e2, w12, 6); - a2 := temp.0; - c2 := temp.1; - - temp := r21(e1, a1, b1, c1, d1, w7, 7); - e1 := temp.0; - b1 := temp.1; - temp := r22(e2, a2, b2, c2, d2, w6, 9); - e2 := temp.0; - b2 := temp.1; - temp := r21(d1, e1, a1, b1, c1, w4, 6); - d1 := temp.0; - a1 := temp.1; - temp := r22(d2, e2, a2, b2, c2, w11, 13); - d2 := temp.0; - a2 := temp.1; - temp := r21(c1, d1, e1, a1, b1, w13, 8); - c1 := temp.0; - e1 := temp.1; - temp := r22(c2, d2, e2, a2, b2, w3, 15); - c2 := temp.0; - e2 := temp.1; - temp := r21(b1, c1, d1, e1, a1, w1, 13); - b1 := temp.0; - d1 := temp.1; - temp := r22(b2, c2, d2, e2, a2, w7, 7); - b2 := temp.0; - d2 := temp.1; - temp := r21(a1, b1, c1, d1, e1, w10, 11); - a1 := temp.0; - c1 := temp.1; - temp := r22(a2, b2, c2, d2, e2, w0, 12); - a2 := temp.0; - c2 := temp.1; - temp := r21(e1, a1, b1, c1, d1, w6, 9); - e1 := temp.0; - b1 := temp.1; - temp := r22(e2, a2, b2, c2, d2, w13, 8); - e2 := temp.0; - b2 := temp.1; - temp := r21(d1, e1, a1, b1, c1, w15, 7); - d1 := temp.0; - a1 := temp.1; - temp := r22(d2, e2, a2, b2, c2, w5, 9); - d2 := temp.0; - a2 := temp.1; - temp := r21(c1, d1, e1, a1, b1, w3, 15); - c1 := temp.0; - e1 := temp.1; - temp := r22(c2, d2, e2, a2, b2, w10, 11); - c2 := temp.0; - e2 := temp.1; - temp := r21(b1, c1, d1, e1, a1, w12, 7); - b1 := temp.0; - d1 := temp.1; - temp := r22(b2, c2, d2, e2, a2, w14, 7); - b2 := temp.0; - d2 := temp.1; - temp := r21(a1, b1, c1, d1, e1, w0, 12); - a1 := temp.0; - c1 := temp.1; - temp := r22(a2, b2, c2, d2, e2, w15, 7); - a2 := temp.0; - c2 := temp.1; - temp := r21(e1, a1, b1, c1, d1, w9, 15); - e1 := temp.0; - b1 := temp.1; - temp := r22(e2, a2, b2, c2, d2, w8, 12); - e2 := temp.0; - b2 := temp.1; - temp := r21(d1, e1, a1, b1, c1, w5, 9); - d1 := temp.0; - a1 := temp.1; - temp := r22(d2, e2, a2, b2, c2, w12, 7); - d2 := temp.0; - a2 := temp.1; - temp := r21(c1, d1, e1, a1, b1, w2, 11); - c1 := temp.0; - e1 := temp.1; - temp := r22(c2, d2, e2, a2, b2, w4, 6); - c2 := temp.0; - e2 := temp.1; - temp := r21(b1, c1, d1, e1, a1, w14, 7); - b1 := temp.0; - d1 := temp.1; - temp := r22(b2, c2, d2, e2, a2, w9, 15); - b2 := temp.0; - d2 := temp.1; - temp := r21(a1, b1, c1, d1, e1, w11, 13); - a1 := temp.0; - c1 := temp.1; - temp := r22(a2, b2, c2, d2, e2, w1, 13); - a2 := temp.0; - c2 := temp.1; - temp := r21(e1, a1, b1, c1, d1, w8, 12); - e1 := temp.0; - b1 := temp.1; - temp := r22(e2, a2, b2, c2, d2, w2, 11); - e2 := temp.0; - b2 := temp.1; - - temp := r31(d1, e1, a1, b1, c1, w3, 11); - d1 := temp.0; - a1 := temp.1; - temp := r32(d2, e2, a2, b2, c2, w15, 9); - d2 := temp.0; - a2 := temp.1; - temp := r31(c1, d1, e1, a1, b1, w10, 13); - c1 := temp.0; - e1 := temp.1; - temp := r32(c2, d2, e2, a2, b2, w5, 7); - c2 := temp.0; - e2 := temp.1; - temp := r31(b1, c1, d1, e1, a1, w14, 6); - b1 := temp.0; - d1 := temp.1; - temp := r32(b2, c2, d2, e2, a2, w1, 15); - b2 := temp.0; - d2 := temp.1; - temp := r31(a1, b1, c1, d1, e1, w4, 7); - a1 := temp.0; - c1 := temp.1; - temp := r32(a2, b2, c2, d2, e2, w3, 11); - a2 := temp.0; - c2 := temp.1; - temp := r31(e1, a1, b1, c1, d1, w9, 14); - e1 := temp.0; - b1 := temp.1; - temp := r32(e2, a2, b2, c2, d2, w7, 8); - e2 := temp.0; - b2 := temp.1; - temp := r31(d1, e1, a1, b1, c1, w15, 9); - d1 := temp.0; - a1 := temp.1; - temp := r32(d2, e2, a2, b2, c2, w14, 6); - d2 := temp.0; - a2 := temp.1; - temp := r31(c1, d1, e1, a1, b1, w8, 13); - c1 := temp.0; - e1 := temp.1; - temp := r32(c2, d2, e2, a2, b2, w6, 6); - c2 := temp.0; - e2 := temp.1; - temp := r31(b1, c1, d1, e1, a1, w1, 15); - b1 := temp.0; - d1 := temp.1; - temp := r32(b2, c2, d2, e2, a2, w9, 14); - b2 := temp.0; - d2 := temp.1; - temp := r31(a1, b1, c1, d1, e1, w2, 14); - a1 := temp.0; - c1 := temp.1; - temp := r32(a2, b2, c2, d2, e2, w11, 12); - a2 := temp.0; - c2 := temp.1; - temp := r31(e1, a1, b1, c1, d1, w7, 8); - e1 := temp.0; - b1 := temp.1; - temp := r32(e2, a2, b2, c2, d2, w8, 13); - e2 := temp.0; - b2 := temp.1; - temp := r31(d1, e1, a1, b1, c1, w0, 13); - d1 := temp.0; - a1 := temp.1; - temp := r32(d2, e2, a2, b2, c2, w12, 5); - d2 := temp.0; - a2 := temp.1; - temp := r31(c1, d1, e1, a1, b1, w6, 6); - c1 := temp.0; - e1 := temp.1; - temp := r32(c2, d2, e2, a2, b2, w2, 14); - c2 := temp.0; - e2 := temp.1; - temp := r31(b1, c1, d1, e1, a1, w13, 5); - b1 := temp.0; - d1 := temp.1; - temp := r32(b2, c2, d2, e2, a2, w10, 13); - b2 := temp.0; - d2 := temp.1; - temp := r31(a1, b1, c1, d1, e1, w11, 12); - a1 := temp.0; - c1 := temp.1; - temp := r32(a2, b2, c2, d2, e2, w0, 13); - a2 := temp.0; - c2 := temp.1; - temp := r31(e1, a1, b1, c1, d1, w5, 7); - e1 := temp.0; - b1 := temp.1; - temp := r32(e2, a2, b2, c2, d2, w4, 7); - e2 := temp.0; - b2 := temp.1; - temp := r31(d1, e1, a1, b1, c1, w12, 5); - d1 := temp.0; - a1 := temp.1; - temp := r32(d2, e2, a2, b2, c2, w13, 5); - d2 := temp.0; - a2 := temp.1; - - temp := r41(c1, d1, e1, a1, b1, w1, 11); - c1 := temp.0; - e1 := temp.1; - temp := r42(c2, d2, e2, a2, b2, w8, 15); - c2 := temp.0; - e2 := temp.1; - temp := r41(b1, c1, d1, e1, a1, w9, 12); - b1 := temp.0; - d1 := temp.1; - temp := r42(b2, c2, d2, e2, a2, w6, 5); - b2 := temp.0; - d2 := temp.1; - temp := r41(a1, b1, c1, d1, e1, w11, 14); - a1 := temp.0; - c1 := temp.1; - temp := r42(a2, b2, c2, d2, e2, w4, 8); - a2 := temp.0; - c2 := temp.1; - temp := r41(e1, a1, b1, c1, d1, w10, 15); - e1 := temp.0; - b1 := temp.1; - temp := r42(e2, a2, b2, c2, d2, w1, 11); - e2 := temp.0; - b2 := temp.1; - temp := r41(d1, e1, a1, b1, c1, w0, 14); - d1 := temp.0; - a1 := temp.1; - temp := r42(d2, e2, a2, b2, c2, w3, 14); - d2 := temp.0; - a2 := temp.1; - temp := r41(c1, d1, e1, a1, b1, w8, 15); - c1 := temp.0; - e1 := temp.1; - temp := r42(c2, d2, e2, a2, b2, w11, 14); - c2 := temp.0; - e2 := temp.1; - temp := r41(b1, c1, d1, e1, a1, w12, 9); - b1 := temp.0; - d1 := temp.1; - temp := r42(b2, c2, d2, e2, a2, w15, 6); - b2 := temp.0; - d2 := temp.1; - temp := r41(a1, b1, c1, d1, e1, w4, 8); - a1 := temp.0; - c1 := temp.1; - temp := r42(a2, b2, c2, d2, e2, w0, 14); - a2 := temp.0; - c2 := temp.1; - temp := r41(e1, a1, b1, c1, d1, w13, 9); - e1 := temp.0; - b1 := temp.1; - temp := r42(e2, a2, b2, c2, d2, w5, 6); - e2 := temp.0; - b2 := temp.1; - temp := r41(d1, e1, a1, b1, c1, w3, 14); - d1 := temp.0; - a1 := temp.1; - temp := r42(d2, e2, a2, b2, c2, w12, 9); - d2 := temp.0; - a2 := temp.1; - temp := r41(c1, d1, e1, a1, b1, w7, 5); - c1 := temp.0; - e1 := temp.1; - temp := r42(c2, d2, e2, a2, b2, w2, 12); - c2 := temp.0; - e2 := temp.1; - temp := r41(b1, c1, d1, e1, a1, w15, 6); - b1 := temp.0; - d1 := temp.1; - temp := r42(b2, c2, d2, e2, a2, w13, 9); - b2 := temp.0; - d2 := temp.1; - temp := r41(a1, b1, c1, d1, e1, w14, 8); - a1 := temp.0; - c1 := temp.1; - temp := r42(a2, b2, c2, d2, e2, w9, 12); - a2 := temp.0; - c2 := temp.1; - temp := r41(e1, a1, b1, c1, d1, w5, 6); - e1 := temp.0; - b1 := temp.1; - temp := r42(e2, a2, b2, c2, d2, w7, 5); - e2 := temp.0; - b2 := temp.1; - temp := r41(d1, e1, a1, b1, c1, w6, 5); - d1 := temp.0; - a1 := temp.1; - temp := r42(d2, e2, a2, b2, c2, w10, 15); - d2 := temp.0; - a2 := temp.1; - temp := r41(c1, d1, e1, a1, b1, w2, 12); - c1 := temp.0; - e1 := temp.1; - temp := r42(c2, d2, e2, a2, b2, w14, 8); - c2 := temp.0; - e2 := temp.1; - - temp := r51(b1, c1, d1, e1, a1, w4, 9); - b1 := temp.0; - d1 := temp.1; - temp := r52(b2, c2, d2, e2, a2, w12, 8); - b2 := temp.0; - d2 := temp.1; - temp := r51(a1, b1, c1, d1, e1, w0, 15); - a1 := temp.0; - c1 := temp.1; - temp := r52(a2, b2, c2, d2, e2, w15, 5); - a2 := temp.0; - c2 := temp.1; - temp := r51(e1, a1, b1, c1, d1, w5, 5); - e1 := temp.0; - b1 := temp.1; - temp := r52(e2, a2, b2, c2, d2, w10, 12); - e2 := temp.0; - b2 := temp.1; - temp := r51(d1, e1, a1, b1, c1, w9, 11); - d1 := temp.0; - a1 := temp.1; - temp := r52(d2, e2, a2, b2, c2, w4, 9); - d2 := temp.0; - a2 := temp.1; - temp := r51(c1, d1, e1, a1, b1, w7, 6); - c1 := temp.0; - e1 := temp.1; - temp := r52(c2, d2, e2, a2, b2, w1, 12); - c2 := temp.0; - e2 := temp.1; - temp := r51(b1, c1, d1, e1, a1, w12, 8); - b1 := temp.0; - d1 := temp.1; - temp := r52(b2, c2, d2, e2, a2, w5, 5); - b2 := temp.0; - d2 := temp.1; - temp := r51(a1, b1, c1, d1, e1, w2, 13); - a1 := temp.0; - c1 := temp.1; - temp := r52(a2, b2, c2, d2, e2, w8, 14); - a2 := temp.0; - c2 := temp.1; - temp := r51(e1, a1, b1, c1, d1, w10, 12); - e1 := temp.0; - b1 := temp.1; - temp := r52(e2, a2, b2, c2, d2, w7, 6); - e2 := temp.0; - b2 := temp.1; - temp := r51(d1, e1, a1, b1, c1, w14, 5); - d1 := temp.0; - a1 := temp.1; - temp := r52(d2, e2, a2, b2, c2, w6, 8); - d2 := temp.0; - a2 := temp.1; - temp := r51(c1, d1, e1, a1, b1, w1, 12); - c1 := temp.0; - e1 := temp.1; - temp := r52(c2, d2, e2, a2, b2, w2, 13); - c2 := temp.0; - e2 := temp.1; - temp := r51(b1, c1, d1, e1, a1, w3, 13); - b1 := temp.0; - d1 := temp.1; - temp := r52(b2, c2, d2, e2, a2, w13, 6); - b2 := temp.0; - d2 := temp.1; - temp := r51(a1, b1, c1, d1, e1, w8, 14); - a1 := temp.0; - c1 := temp.1; - temp := r52(a2, b2, c2, d2, e2, w14, 5); - a2 := temp.0; - c2 := temp.1; - temp := r51(e1, a1, b1, c1, d1, w11, 11); - e1 := temp.0; - b1 := temp.1; - temp := r52(e2, a2, b2, c2, d2, w0, 15); - e2 := temp.0; - b2 := temp.1; - temp := r51(d1, e1, a1, b1, c1, w6, 8); - d1 := temp.0; - a1 := temp.1; - temp := r52(d2, e2, a2, b2, c2, w3, 13); - d2 := temp.0; - a2 := temp.1; - temp := r51(c1, d1, e1, a1, b1, w15, 5); - c1 := temp.0; - e1 := temp.1; - temp := r52(c2, d2, e2, a2, b2, w9, 11); - c2 := temp.0; - e2 := temp.1; - temp := r51(b1, c1, d1, e1, a1, w13, 6); - b1 := temp.0; - d1 := temp.1; - temp := r52(b2, c2, d2, e2, a2, w11, 11); - b2 := temp.0; - d2 := temp.1; + // prettier-ignore + do { + // ---- Left line round 1: f1(b,c,d) = b^c^d, K = 0 + a1 := rol(a1 +% (b1 ^ c1 ^ d1) +% w0, 11) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% (a1 ^ b1 ^ c1) +% w1, 14) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% (e1 ^ a1 ^ b1) +% w2, 15) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% (d1 ^ e1 ^ a1) +% w3, 12) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% (c1 ^ d1 ^ e1) +% w4, 5) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% (b1 ^ c1 ^ d1) +% w5, 8) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% (a1 ^ b1 ^ c1) +% w6, 7) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% (e1 ^ a1 ^ b1) +% w7, 9) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% (d1 ^ e1 ^ a1) +% w8, 11) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% (c1 ^ d1 ^ e1) +% w9, 13) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% (b1 ^ c1 ^ d1) +% w10, 14) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% (a1 ^ b1 ^ c1) +% w11, 15) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% (e1 ^ a1 ^ b1) +% w12, 6) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% (d1 ^ e1 ^ a1) +% w13, 7) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% (c1 ^ d1 ^ e1) +% w14, 9) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% (b1 ^ c1 ^ d1) +% w15, 8) +% e1; c1 := rol(c1, 10); + + // ---- Left line round 2: f2(b,c,d) = (b & c) | (~b & d), K = 0x5A827999 + e1 := rol(e1 +% ((a1 & b1) | (^a1 & c1)) +% w7 +% 0x5A827999, 7) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 & a1) | (^e1 & b1)) +% w4 +% 0x5A827999, 6) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 & e1) | (^d1 & a1)) +% w13 +% 0x5A827999, 8) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 & d1) | (^c1 & e1)) +% w1 +% 0x5A827999, 13) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 & c1) | (^b1 & d1)) +% w10 +% 0x5A827999, 11) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 & b1) | (^a1 & c1)) +% w6 +% 0x5A827999, 9) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 & a1) | (^e1 & b1)) +% w15 +% 0x5A827999, 7) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 & e1) | (^d1 & a1)) +% w3 +% 0x5A827999, 15) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 & d1) | (^c1 & e1)) +% w12 +% 0x5A827999, 7) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 & c1) | (^b1 & d1)) +% w0 +% 0x5A827999, 12) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 & b1) | (^a1 & c1)) +% w9 +% 0x5A827999, 15) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 & a1) | (^e1 & b1)) +% w5 +% 0x5A827999, 9) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 & e1) | (^d1 & a1)) +% w2 +% 0x5A827999, 11) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 & d1) | (^c1 & e1)) +% w14 +% 0x5A827999, 7) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 & c1) | (^b1 & d1)) +% w11 +% 0x5A827999, 13) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 & b1) | (^a1 & c1)) +% w8 +% 0x5A827999, 12) +% d1; b1 := rol(b1, 10); + + // ---- Left line round 3: f3(b,c,d) = (b | ~c) ^ d, K = 0x6ED9EBA1 + d1 := rol(d1 +% ((e1 | ^a1) ^ b1) +% w3 +% 0x6ED9EBA1, 11) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 | ^e1) ^ a1) +% w10 +% 0x6ED9EBA1, 13) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 | ^d1) ^ e1) +% w14 +% 0x6ED9EBA1, 6) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 | ^c1) ^ d1) +% w4 +% 0x6ED9EBA1, 7) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 | ^b1) ^ c1) +% w9 +% 0x6ED9EBA1, 14) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 | ^a1) ^ b1) +% w15 +% 0x6ED9EBA1, 9) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 | ^e1) ^ a1) +% w8 +% 0x6ED9EBA1, 13) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 | ^d1) ^ e1) +% w1 +% 0x6ED9EBA1, 15) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 | ^c1) ^ d1) +% w2 +% 0x6ED9EBA1, 14) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 | ^b1) ^ c1) +% w7 +% 0x6ED9EBA1, 8) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 | ^a1) ^ b1) +% w0 +% 0x6ED9EBA1, 13) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 | ^e1) ^ a1) +% w6 +% 0x6ED9EBA1, 6) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 | ^d1) ^ e1) +% w13 +% 0x6ED9EBA1, 5) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 | ^c1) ^ d1) +% w11 +% 0x6ED9EBA1, 12) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 | ^b1) ^ c1) +% w5 +% 0x6ED9EBA1, 7) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 | ^a1) ^ b1) +% w12 +% 0x6ED9EBA1, 5) +% c1; a1 := rol(a1, 10); + + // ---- Left line round 4: f4(b,c,d) = (b & d) | (c & ~d), K = 0x8F1BBCDC + c1 := rol(c1 +% ((d1 & a1) | (e1 & ^a1)) +% w1 +% 0x8F1BBCDC, 11) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 & e1) | (d1 & ^e1)) +% w9 +% 0x8F1BBCDC, 12) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 & d1) | (c1 & ^d1)) +% w11 +% 0x8F1BBCDC, 14) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 & c1) | (b1 & ^c1)) +% w10 +% 0x8F1BBCDC, 15) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 & b1) | (a1 & ^b1)) +% w0 +% 0x8F1BBCDC, 14) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 & a1) | (e1 & ^a1)) +% w8 +% 0x8F1BBCDC, 15) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 & e1) | (d1 & ^e1)) +% w12 +% 0x8F1BBCDC, 9) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 & d1) | (c1 & ^d1)) +% w4 +% 0x8F1BBCDC, 8) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 & c1) | (b1 & ^c1)) +% w13 +% 0x8F1BBCDC, 9) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 & b1) | (a1 & ^b1)) +% w3 +% 0x8F1BBCDC, 14) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 & a1) | (e1 & ^a1)) +% w7 +% 0x8F1BBCDC, 5) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% ((c1 & e1) | (d1 & ^e1)) +% w15 +% 0x8F1BBCDC, 6) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% ((b1 & d1) | (c1 & ^d1)) +% w14 +% 0x8F1BBCDC, 8) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% ((a1 & c1) | (b1 & ^c1)) +% w5 +% 0x8F1BBCDC, 6) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% ((e1 & b1) | (a1 & ^b1)) +% w6 +% 0x8F1BBCDC, 5) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% ((d1 & a1) | (e1 & ^a1)) +% w2 +% 0x8F1BBCDC, 12) +% b1; e1 := rol(e1, 10); + + // ---- Left line round 5: f5(b,c,d) = b ^ (c | ~d), K = 0xA953FD4E + b1 := rol(b1 +% (c1 ^ (d1 | ^e1)) +% w4 +% 0xA953FD4E, 9) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% (b1 ^ (c1 | ^d1)) +% w0 +% 0xA953FD4E, 15) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% (a1 ^ (b1 | ^c1)) +% w5 +% 0xA953FD4E, 5) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% (e1 ^ (a1 | ^b1)) +% w9 +% 0xA953FD4E, 11) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% (d1 ^ (e1 | ^a1)) +% w7 +% 0xA953FD4E, 6) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% (c1 ^ (d1 | ^e1)) +% w12 +% 0xA953FD4E, 8) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% (b1 ^ (c1 | ^d1)) +% w2 +% 0xA953FD4E, 13) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% (a1 ^ (b1 | ^c1)) +% w10 +% 0xA953FD4E, 12) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% (e1 ^ (a1 | ^b1)) +% w14 +% 0xA953FD4E, 5) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% (d1 ^ (e1 | ^a1)) +% w1 +% 0xA953FD4E, 12) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% (c1 ^ (d1 | ^e1)) +% w3 +% 0xA953FD4E, 13) +% a1; d1 := rol(d1, 10); + a1 := rol(a1 +% (b1 ^ (c1 | ^d1)) +% w8 +% 0xA953FD4E, 14) +% e1; c1 := rol(c1, 10); + e1 := rol(e1 +% (a1 ^ (b1 | ^c1)) +% w11 +% 0xA953FD4E, 11) +% d1; b1 := rol(b1, 10); + d1 := rol(d1 +% (e1 ^ (a1 | ^b1)) +% w6 +% 0xA953FD4E, 8) +% c1; a1 := rol(a1, 10); + c1 := rol(c1 +% (d1 ^ (e1 | ^a1)) +% w15 +% 0xA953FD4E, 5) +% b1; e1 := rol(e1, 10); + b1 := rol(b1 +% (c1 ^ (d1 | ^e1)) +% w13 +% 0xA953FD4E, 6) +% a1; d1 := rol(d1, 10); + + // ---- Right line round 1: f5, K = 0x50A28BE6 + a2 := rol(a2 +% (b2 ^ (c2 | ^d2)) +% w5 +% 0x50A28BE6, 8) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% (a2 ^ (b2 | ^c2)) +% w14 +% 0x50A28BE6, 9) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% (e2 ^ (a2 | ^b2)) +% w7 +% 0x50A28BE6, 9) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% (d2 ^ (e2 | ^a2)) +% w0 +% 0x50A28BE6, 11) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% (c2 ^ (d2 | ^e2)) +% w9 +% 0x50A28BE6, 13) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% (b2 ^ (c2 | ^d2)) +% w2 +% 0x50A28BE6, 15) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% (a2 ^ (b2 | ^c2)) +% w11 +% 0x50A28BE6, 15) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% (e2 ^ (a2 | ^b2)) +% w4 +% 0x50A28BE6, 5) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% (d2 ^ (e2 | ^a2)) +% w13 +% 0x50A28BE6, 7) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% (c2 ^ (d2 | ^e2)) +% w6 +% 0x50A28BE6, 7) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% (b2 ^ (c2 | ^d2)) +% w15 +% 0x50A28BE6, 8) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% (a2 ^ (b2 | ^c2)) +% w8 +% 0x50A28BE6, 11) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% (e2 ^ (a2 | ^b2)) +% w1 +% 0x50A28BE6, 14) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% (d2 ^ (e2 | ^a2)) +% w10 +% 0x50A28BE6, 14) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% (c2 ^ (d2 | ^e2)) +% w3 +% 0x50A28BE6, 12) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% (b2 ^ (c2 | ^d2)) +% w12 +% 0x50A28BE6, 6) +% e2; c2 := rol(c2, 10); + + // ---- Right line round 2: f4, K = 0x5C4DD124 + e2 := rol(e2 +% ((a2 & c2) | (b2 & ^c2)) +% w6 +% 0x5C4DD124, 9) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 & b2) | (a2 & ^b2)) +% w11 +% 0x5C4DD124, 13) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 & a2) | (e2 & ^a2)) +% w3 +% 0x5C4DD124, 15) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 & e2) | (d2 & ^e2)) +% w7 +% 0x5C4DD124, 7) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 & d2) | (c2 & ^d2)) +% w0 +% 0x5C4DD124, 12) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 & c2) | (b2 & ^c2)) +% w13 +% 0x5C4DD124, 8) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 & b2) | (a2 & ^b2)) +% w5 +% 0x5C4DD124, 9) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 & a2) | (e2 & ^a2)) +% w10 +% 0x5C4DD124, 11) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 & e2) | (d2 & ^e2)) +% w14 +% 0x5C4DD124, 7) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 & d2) | (c2 & ^d2)) +% w15 +% 0x5C4DD124, 7) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 & c2) | (b2 & ^c2)) +% w8 +% 0x5C4DD124, 12) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 & b2) | (a2 & ^b2)) +% w12 +% 0x5C4DD124, 7) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 & a2) | (e2 & ^a2)) +% w4 +% 0x5C4DD124, 6) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 & e2) | (d2 & ^e2)) +% w9 +% 0x5C4DD124, 15) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 & d2) | (c2 & ^d2)) +% w1 +% 0x5C4DD124, 13) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 & c2) | (b2 & ^c2)) +% w2 +% 0x5C4DD124, 11) +% d2; b2 := rol(b2, 10); + + // ---- Right line round 3: f3, K = 0x6D703EF3 + d2 := rol(d2 +% ((e2 | ^a2) ^ b2) +% w15 +% 0x6D703EF3, 9) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 | ^e2) ^ a2) +% w5 +% 0x6D703EF3, 7) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 | ^d2) ^ e2) +% w1 +% 0x6D703EF3, 15) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 | ^c2) ^ d2) +% w3 +% 0x6D703EF3, 11) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 | ^b2) ^ c2) +% w7 +% 0x6D703EF3, 8) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 | ^a2) ^ b2) +% w14 +% 0x6D703EF3, 6) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 | ^e2) ^ a2) +% w6 +% 0x6D703EF3, 6) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 | ^d2) ^ e2) +% w9 +% 0x6D703EF3, 14) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 | ^c2) ^ d2) +% w11 +% 0x6D703EF3, 12) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 | ^b2) ^ c2) +% w8 +% 0x6D703EF3, 13) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 | ^a2) ^ b2) +% w12 +% 0x6D703EF3, 5) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 | ^e2) ^ a2) +% w2 +% 0x6D703EF3, 14) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 | ^d2) ^ e2) +% w10 +% 0x6D703EF3, 13) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 | ^c2) ^ d2) +% w0 +% 0x6D703EF3, 13) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 | ^b2) ^ c2) +% w4 +% 0x6D703EF3, 7) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 | ^a2) ^ b2) +% w13 +% 0x6D703EF3, 5) +% c2; a2 := rol(a2, 10); + + // ---- Right line round 4: f2, K = 0x7A6D76E9 + c2 := rol(c2 +% ((d2 & e2) | (^d2 & a2)) +% w8 +% 0x7A6D76E9, 15) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 & d2) | (^c2 & e2)) +% w6 +% 0x7A6D76E9, 5) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 & c2) | (^b2 & d2)) +% w4 +% 0x7A6D76E9, 8) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 & b2) | (^a2 & c2)) +% w1 +% 0x7A6D76E9, 11) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 & a2) | (^e2 & b2)) +% w3 +% 0x7A6D76E9, 14) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 & e2) | (^d2 & a2)) +% w11 +% 0x7A6D76E9, 14) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 & d2) | (^c2 & e2)) +% w15 +% 0x7A6D76E9, 6) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 & c2) | (^b2 & d2)) +% w0 +% 0x7A6D76E9, 14) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 & b2) | (^a2 & c2)) +% w5 +% 0x7A6D76E9, 6) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 & a2) | (^e2 & b2)) +% w12 +% 0x7A6D76E9, 9) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 & e2) | (^d2 & a2)) +% w2 +% 0x7A6D76E9, 12) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% ((c2 & d2) | (^c2 & e2)) +% w13 +% 0x7A6D76E9, 9) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% ((b2 & c2) | (^b2 & d2)) +% w9 +% 0x7A6D76E9, 12) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% ((a2 & b2) | (^a2 & c2)) +% w7 +% 0x7A6D76E9, 5) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% ((e2 & a2) | (^e2 & b2)) +% w10 +% 0x7A6D76E9, 15) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% ((d2 & e2) | (^d2 & a2)) +% w14 +% 0x7A6D76E9, 8) +% b2; e2 := rol(e2, 10); + + // ---- Right line round 5: f1, K = 0 + b2 := rol(b2 +% (c2 ^ d2 ^ e2) +% w12, 8) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% (b2 ^ c2 ^ d2) +% w15, 5) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% (a2 ^ b2 ^ c2) +% w10, 12) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% (e2 ^ a2 ^ b2) +% w4, 9) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% (d2 ^ e2 ^ a2) +% w1, 12) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% (c2 ^ d2 ^ e2) +% w5, 5) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% (b2 ^ c2 ^ d2) +% w8, 14) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% (a2 ^ b2 ^ c2) +% w7, 6) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% (e2 ^ a2 ^ b2) +% w6, 8) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% (d2 ^ e2 ^ a2) +% w2, 13) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% (c2 ^ d2 ^ e2) +% w13, 6) +% a2; d2 := rol(d2, 10); + a2 := rol(a2 +% (b2 ^ c2 ^ d2) +% w14, 5) +% e2; c2 := rol(c2, 10); + e2 := rol(e2 +% (a2 ^ b2 ^ c2) +% w0, 15) +% d2; b2 := rol(b2, 10); + d2 := rol(d2 +% (e2 ^ a2 ^ b2) +% w3, 13) +% c2; a2 := rol(a2, 10); + c2 := rol(c2 +% (d2 ^ e2 ^ a2) +% w9, 11) +% b2; e2 := rol(e2, 10); + b2 := rol(b2 +% (c2 ^ d2 ^ e2) +% w11, 11) +% a2; d2 := rol(d2, 10); + }; + + // Combine the two lines back into the chaining state. let t : Nat32 = s[0]; s[0] := s[1] +% c1 +% d2; s[1] := s[2] +% d1 +% e2; @@ -712,62 +281,123 @@ module { s[4] := t +% b1 +% c2; }; + // Append a single byte to the current block, processing the block when + // it becomes full. The byte is folded into msg[i_msg >> 2] at the + // little-endian position implied by (i_msg & 3). + private func writeByte(b : Nat8) { + let pos = i_msg; + let wi = Nat16.toNat(pos >> 2); + let lane = pos & 0x3; + let v : Nat32 = b.toNat16().toNat32() << Nat32.fromNat16(lane << 3); + if (lane == 0) { + // First byte of a fresh word: overwrite (the slot may carry stale + // data from an earlier block, since we never explicitly clear msg). + msg[wi] := v; + } else { + msg[wi] := msg[wi] | v; + }; + let next = pos +% 1; + if (next == 64) { + transform(); + n_blocks +%= 1; + i_msg := 0; + } else { + i_msg := next; + }; + }; + public func write(data : [Nat8]) { - var bufsize : Nat = (bytes % 64).toNat(); - var transformOffset : Nat = 0; - - // Check if the incoming data is enough to fill the buffer - // which might have data from a previous call. - if (bufsize > 0 and (bufsize + data.size() >= 64)) { - // Fill the buffer, and process it. - for (i in Nat.range(bufsize, 64)) { - buf[i] := data[i - bufsize]; - }; - // Add the count of processed bytes. - bytes += 64 - Nat64.fromNat(bufsize); - transform(buf.toArray(), 0); - // All data in the buffer has been processed, reset buffer data size - // point transformOffset to index of not-yet processed data. - transformOffset += 64 - bufsize; - bufsize := 0; + // Don't allow writes if Digest is closed, i.e. after sum() + assert (not closed); + + let n = data.size(); + if (n == 0) return; + var i = 0; + + // 1) If a partial block is in progress, fill it byte by byte. + while (i_msg != 0 and i < n) { + writeByte(data[i]); + i += 1; }; - // If size of not-yet processed incoming data fits in buffer, - // process it. - while (data.size() >= transformOffset + 64) { - // Process full chunks directly from the source. - transform(data, transformOffset); - // Add the count of processed bytes. - bytes += 64; - transformOffset += 64; + + // 2) Fast path: process any number of full 64-byte blocks directly, + // decoding 16 little-endian words inline into msg per block. + // prettier-ignore + while (i + 64 <= n) { + msg[0] := data[i].toNat16().toNat32() | (data[i+1].toNat16().toNat32() << 8) | (data[i+2].toNat16().toNat32() << 16) | (data[i+3].toNat16().toNat32() << 24); + msg[1] := data[i+4].toNat16().toNat32() | (data[i+5].toNat16().toNat32() << 8) | (data[i+6].toNat16().toNat32() << 16) | (data[i+7].toNat16().toNat32() << 24); + msg[2] := data[i+8].toNat16().toNat32() | (data[i+9].toNat16().toNat32() << 8) | (data[i+10].toNat16().toNat32() << 16) | (data[i+11].toNat16().toNat32() << 24); + msg[3] := data[i+12].toNat16().toNat32() | (data[i+13].toNat16().toNat32() << 8) | (data[i+14].toNat16().toNat32() << 16) | (data[i+15].toNat16().toNat32() << 24); + msg[4] := data[i+16].toNat16().toNat32() | (data[i+17].toNat16().toNat32() << 8) | (data[i+18].toNat16().toNat32() << 16) | (data[i+19].toNat16().toNat32() << 24); + msg[5] := data[i+20].toNat16().toNat32() | (data[i+21].toNat16().toNat32() << 8) | (data[i+22].toNat16().toNat32() << 16) | (data[i+23].toNat16().toNat32() << 24); + msg[6] := data[i+24].toNat16().toNat32() | (data[i+25].toNat16().toNat32() << 8) | (data[i+26].toNat16().toNat32() << 16) | (data[i+27].toNat16().toNat32() << 24); + msg[7] := data[i+28].toNat16().toNat32() | (data[i+29].toNat16().toNat32() << 8) | (data[i+30].toNat16().toNat32() << 16) | (data[i+31].toNat16().toNat32() << 24); + msg[8] := data[i+32].toNat16().toNat32() | (data[i+33].toNat16().toNat32() << 8) | (data[i+34].toNat16().toNat32() << 16) | (data[i+35].toNat16().toNat32() << 24); + msg[9] := data[i+36].toNat16().toNat32() | (data[i+37].toNat16().toNat32() << 8) | (data[i+38].toNat16().toNat32() << 16) | (data[i+39].toNat16().toNat32() << 24); + msg[10] := data[i+40].toNat16().toNat32() | (data[i+41].toNat16().toNat32() << 8) | (data[i+42].toNat16().toNat32() << 16) | (data[i+43].toNat16().toNat32() << 24); + msg[11] := data[i+44].toNat16().toNat32() | (data[i+45].toNat16().toNat32() << 8) | (data[i+46].toNat16().toNat32() << 16) | (data[i+47].toNat16().toNat32() << 24); + msg[12] := data[i+48].toNat16().toNat32() | (data[i+49].toNat16().toNat32() << 8) | (data[i+50].toNat16().toNat32() << 16) | (data[i+51].toNat16().toNat32() << 24); + msg[13] := data[i+52].toNat16().toNat32() | (data[i+53].toNat16().toNat32() << 8) | (data[i+54].toNat16().toNat32() << 16) | (data[i+55].toNat16().toNat32() << 24); + msg[14] := data[i+56].toNat16().toNat32() | (data[i+57].toNat16().toNat32() << 8) | (data[i+58].toNat16().toNat32() << 16) | (data[i+59].toNat16().toNat32() << 24); + msg[15] := data[i+60].toNat16().toNat32() | (data[i+61].toNat16().toNat32() << 8) | (data[i+62].toNat16().toNat32() << 16) | (data[i+63].toNat16().toNat32() << 24); + transform(); + n_blocks +%= 1; + i += 64; }; - // Push any not-yet processed data to buffer. - for (i in Nat.range(transformOffset, data.size())) { - buf[bufsize + i - transformOffset] := data[i]; - bytes += 1; + + // 3) Tail: copy remaining bytes one at a time into the partial block. + while (i < n) { + writeByte(data[i]); + i += 1; }; }; + // Finalize the digest and return the 20-byte RIPEMD-160 hash. + // + // NOTE: sum() is consuming and locks the Digest. + // Calling sum() a second time or calling one of the write..() functions after sum() will trap. + // You have to call reset() before you can re-use a Digest for another message. public func sum() : [Nat8] { - let pad : [var Nat8] = VarArray.repeat( - 0, - (1 + ((119 - (bytes % 64)) % 64)).toNat(), - ); - pad[0] := 0x80; - - let sizedesc : [var Nat8] = VarArray.repeat(0, 8); - Common.writeLE64(sizedesc, 0, bytes << 3); - - write(pad.toArray()); - write(sizedesc.toArray()); - - let hash : [var Nat8] = VarArray.repeat(0, 20); - Common.writeLE32(hash, 0, s[0]); - Common.writeLE32(hash, 4, s[1]); - Common.writeLE32(hash, 8, s[2]); - Common.writeLE32(hash, 12, s[3]); - Common.writeLE32(hash, 16, s[4]); - - hash.toArray(); + // Prevent sum() from being called twice. + assert (not closed); + + // Close Digest for futher writes. + closed := true; + + // Total message length in bits, captured before padding is appended. + let bitlen : Nat64 = ((n_blocks << 6) +% Nat64.fromNat(Nat16.toNat(i_msg))) << 3; + + // Standard MD-style padding: 0x80 byte, then zeros until 56 bytes + // remain in the current block, then 8 bytes of bit length (LE). + writeByte(0x80); + while (i_msg != 56) { + writeByte(0); + }; + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 8))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 16))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 24))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 32))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 40))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 48))); + writeByte(Nat8.fromIntWrap(Nat64.toNat(bitlen >> 56))); + // The 8th length byte fills the block, triggering transform(). + + // Serialize the 5 chaining words as 20 little-endian bytes. + let (b3, b2, b1, b0) = Prim.explodeNat32(s[0]); + let (b7, b6, b5, b4) = Prim.explodeNat32(s[1]); + let (b11, b10, b9, b8) = Prim.explodeNat32(s[2]); + let (b15, b14, b13, b12) = Prim.explodeNat32(s[3]); + let (b19, b18, b17, b16) = Prim.explodeNat32(s[4]); + + // prettier-ignore + [ + b0, b1, b2, b3, + b4, b5, b6, b7, + b8, b9, b10, b11, + b12, b13, b14, b15, + b16, b17, b18, b19, + ]; }; }; }; diff --git a/test/bitcoin/p2trKeyPathSigHash.test.mo b/test/bitcoin/p2trKeyPathSigHash.test.mo index 4229402..339d8dc 100644 --- a/test/bitcoin/p2trKeyPathSigHash.test.mo +++ b/test/bitcoin/p2trKeyPathSigHash.test.mo @@ -38,10 +38,14 @@ test( ); let hash0 = txLocktime0.createTaprootKeySpendSignatureHash( - testCase.amounts(), testCase.ownScript(), 0, + testCase.amounts(), + testCase.ownScript(), + 0, ); let hash42 = txLocktime42.createTaprootKeySpendSignatureHash( - testCase.amounts(), testCase.ownScript(), 0, + testCase.amounts(), + testCase.ownScript(), + 0, ); expect.blob(Blob.fromArray(hash0)).notEqual(Blob.fromArray(hash42)); @@ -62,10 +66,14 @@ test( ); let hash2 = txVersion2.createTaprootKeySpendSignatureHash( - testCase.amounts(), testCase.ownScript(), 0, + testCase.amounts(), + testCase.ownScript(), + 0, ); let hash1 = txVersion1.createTaprootKeySpendSignatureHash( - testCase.amounts(), testCase.ownScript(), 0, + testCase.amounts(), + testCase.ownScript(), + 0, ); expect.blob(Blob.fromArray(hash2)).notEqual(Blob.fromArray(hash1)); diff --git a/test/bitcoin/p2trScriptPathSigHash.test.mo b/test/bitcoin/p2trScriptPathSigHash.test.mo index c565413..0115d45 100644 --- a/test/bitcoin/p2trScriptPathSigHash.test.mo +++ b/test/bitcoin/p2trScriptPathSigHash.test.mo @@ -40,10 +40,16 @@ test( let leafHash = P2tr.leafHash(testCase.leafScript()); let hash0 = txLocktime0.createTaprootScriptSpendSignatureHash( - testCase.amounts(), testCase.ownScript(), 0, leafHash, + testCase.amounts(), + testCase.ownScript(), + 0, + leafHash, ); let hash42 = txLocktime42.createTaprootScriptSpendSignatureHash( - testCase.amounts(), testCase.ownScript(), 0, leafHash, + testCase.amounts(), + testCase.ownScript(), + 0, + leafHash, ); expect.blob(Blob.fromArray(hash0)).notEqual(Blob.fromArray(hash42)); diff --git a/test/ripemd160.test.mo b/test/ripemd160.test.mo index d8e58f7..ac02fea 100644 --- a/test/ripemd160.test.mo +++ b/test/ripemd160.test.mo @@ -1,9 +1,12 @@ import Nat "mo:core/Nat"; // @testmode wasi -import VarArray "mo:core/VarArray"; +import Array "mo:core/Array"; import Blob "mo:core/Blob"; +import Nat8 "mo:core/Nat8"; import Text "mo:core/Text"; +import VarArray "mo:core/VarArray"; + import Ripemd160 "../src/Ripemd160"; import { test } "mo:test"; @@ -115,28 +118,48 @@ test( digest.write(Blob.toArray(Text.encodeUtf8("vwxyz"))); assert ( - [ - 247, - 28, - 39, - 16, - 156, - 105, - 44, - 27, - 86, - 187, - 220, - 235, - 91, - 157, - 40, - 101, - 179, - 112, - 141, - 188, - ] == digest.sum() + // prettier-ignore + digest.sum() == [ + 247, 28, 39, 16, 156, 105, 44, 27, 86, 187, 220, 235, 91, 157, 40, 101, + 179, 112, 141, 188 + ] ); }, ); + +test( + "single write spanning full block + tail", + func() { + // 96 bytes = one full 64-byte block + 32 tail bytes, exercising both the + // full-block fast path and the tail path inside Ripemd160.write(). + let input : [Nat8] = Array.tabulate(96, func i { Nat8.fromIntWrap(i * 19 + 7) }); + let expected : [Nat8] = [ + 0xc3, + 0xfd, + 0x14, + 0xbf, + 0x82, + 0xc7, + 0x10, + 0x5e, + 0xd2, + 0x86, + 0x0c, + 0xa8, + 0xa3, + 0xa2, + 0xad, + 0x54, + 0x8b, + 0x74, + 0x71, + 0x5a, + ]; + + assert (Ripemd160.hash(input) == expected); + + let digest : Ripemd160.Digest = Ripemd160.Digest(); + digest.write(input); + assert (digest.sum() == expected); + }, +);