io_v2_digest.zig (1988B)
1 //! Scoped, incremental SHA-256 state. No chunk/history retention and no writer or 2 //! publication policy. The host registry supplies ownership and exclusive leases. 3 const std = @import("std"); 4 pub const Digest = struct { 5 state: std.crypto.hash.sha2.Sha256 = std.crypto.hash.sha2.Sha256.init(.{}), 6 finished: bool = false, 7 pub fn update(self: *Digest, bytes: []const u8) error{ InvalidBound, InvalidState }!void { 8 if (bytes.len == 0 or bytes.len > 4096) return error.InvalidBound; 9 if (self.finished) return error.InvalidState; 10 self.state.update(bytes); 11 } 12 pub fn finish(self: *Digest) error{InvalidState}![64]u8 { 13 if (self.finished) return error.InvalidState; 14 var bytes: [32]u8 = undefined; 15 self.state.final(&bytes); 16 self.finished = true; 17 var hex: [64]u8 = undefined; 18 for (bytes, 0..) |byte, i| { 19 hex[2 * i] = "0123456789abcdef"[byte >> 4]; 20 hex[2 * i + 1] = "0123456789abcdef"[byte & 15]; 21 } 22 return hex; 23 } 24 }; 25 test "independent SHA-256 vectors, partitioning and terminal state" { 26 const t = std.testing; 27 var empty: Digest = .{}; 28 try t.expectEqualStrings("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", &(try empty.finish())); 29 try t.expectError(error.InvalidState, empty.finish()); 30 try t.expectError(error.InvalidState, empty.update("x")); 31 var abc: Digest = .{}; 32 try t.expectError(error.InvalidBound, abc.update("")); 33 try t.expectError(error.InvalidBound, abc.update(&([_]u8{'x'} ** 4097))); 34 try abc.update("a"); 35 try abc.update("bc"); 36 try t.expectEqualStrings("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", &(try abc.finish())); 37 var million: Digest = .{}; 38 const block = [_]u8{'a'} ** 1000; 39 for (0..1000) |_| try million.update(&block); 40 try t.expectEqualStrings("cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", &(try million.finish())); 41 }