lib/reticulum/src/hash.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const Sha256 = std.crypto.hash.sha2.Sha256;
4
5 /// 32 bytes, the width of a complete SHA-256 digest, following Reticulum@1.5.0
6 /// RNS/Identity.py:352.
7 pub const full_bytes: usize = 32;
8 /// 16 bytes, the width a destination hash and an identity hash take on the
9 /// wire, following Reticulum@1.5.0 RNS/Identity.py:362 and Reticulum@1.5.0
10 /// RNS/Reticulum.py:148.
11 pub const truncated_bytes: usize = 16;
12 /// 10 bytes, the width a destination's announced name hash and a rotating key's
13 /// identifier take, following Reticulum@1.5.0 RNS/Identity.py:83.
14 pub const name_bytes: usize = 10;
15
16 /// Returns the complete 32-byte SHA-256 digest of some bytes, following
17 /// Reticulum@1.5.0 RNS/Identity.py:352.
18 pub fn full(bytes: []const u8) [full_bytes]u8 {
19 var hasher = Hasher.init();
20 hasher.update(bytes);
21 return hasher.finalFull();
22 }
23
24 /// Returns the first 16 bytes of the SHA-256 digest of some bytes, following
25 /// Reticulum@1.5.0 RNS/Identity.py:362 and Reticulum@1.5.0
26 /// RNS/Reticulum.py:148.
27 pub fn truncated(bytes: []const u8) [truncated_bytes]u8 {
28 var hasher = Hasher.init();
29 hasher.update(bytes);
30 return hasher.finalTruncated();
31 }
32
33 /// Returns the first 10 bytes of the SHA-256 digest of some bytes, following
34 /// Reticulum@1.5.0 RNS/Identity.py:83 and Reticulum@1.5.0
35 /// RNS/Destination.py:120.
36 pub fn name(bytes: []const u8) [name_bytes]u8 {
37 var hasher = Hasher.init();
38 hasher.update(bytes);
39 return hasher.finalName();
40 }
41
42 /// A SHA-256 state that takes its input in pieces and then gives any of the
43 /// three widths. Reticulum hashes joined transcripts, so a caller feeds the
44 /// pieces in turn and needs no buffer holding them all at once. Taking a digest
45 /// works on a copy of the state, so one hasher gives more than one width.
46 pub const Hasher = struct {
47 state: Sha256,
48
49 pub fn init() Hasher {
50 return .{ .state = Sha256.init(.{}) };
51 }
52
53 pub fn update(self: *Hasher, bytes: []const u8) void {
54 self.state.update(bytes);
55 }
56
57 pub fn finalFull(self: Hasher) [full_bytes]u8 {
58 var state = self.state;
59 var result: [full_bytes]u8 = undefined;
60 state.final(&result);
61 return result;
62 }
63
64 pub fn finalTruncated(self: Hasher) [truncated_bytes]u8 {
65 const result = self.finalFull();
66 return result[0..truncated_bytes].*;
67 }
68
69 pub fn finalName(self: Hasher) [name_bytes]u8 {
70 const result = self.finalFull();
71 return result[0..name_bytes].*;
72 }
73 };