lib/reticulum/src/wire/hash.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const flags = @import("flags.zig");
 3 const header = @import("header.zig");
 4 
 5 const Sha256 = std.crypto.hash.sha2.Sha256;
 6 const assert = std.debug.assert;
 7 
 8 pub const HashError = error{PacketTooShort};
 9 
10 fn suffixOffset(bytes: []const u8) HashError!usize {
11     if (bytes.len < 1) return error.PacketTooShort;
12     const header_type = flags.Flags.decode(bytes[0]).header;
13     if (bytes.len < header.headerLength(header_type)) return error.PacketTooShort;
14     return switch (header_type) {
15         .one => 2,
16         .two => 18,
17     };
18 }
19 
20 /// Returns how many bytes a packet hash covers: one for the masked flags byte
21 /// plus everything from the destination hash onward, so a caller can size a
22 /// buffer or check a bound before it hashes a packet, following Reticulum@1.5.0
23 /// RNS/Packet.py:353-357. The call returns `error.PacketTooShort` for an empty
24 /// input, and for bytes shorter than the header their flags byte claims.
25 pub fn hashableLength(bytes: []const u8) HashError!usize {
26     const offset = try suffixOffset(bytes);
27     assert(offset <= bytes.len);
28     return 1 + bytes.len - offset;
29 }
30 
31 /// Returns the 32-byte SHA-256 digest that names one Reticulum datagram
32 /// (*packet*), so a caller can match the proof sent back for a packet to the
33 /// packet it answers or recognize one it has already handled. The digest covers
34 /// the low four bits of the packet's first byte followed by the destination
35 /// hash onward, which leaves out the hop count and the 16 bytes a carrying node
36 /// inserts, so the name holds still at every node the packet crosses. The two
37 /// pieces stream into the hash state where they lie, so the call copies the
38 /// hashable part nowhere. The call returns `error.PacketTooShort` for an empty
39 /// input, and for bytes shorter than the header their flags byte claims.
40 pub fn full(bytes: []const u8) HashError![32]u8 {
41     const offset = try suffixOffset(bytes);
42     assert(offset <= bytes.len);
43     const first = [1]u8{bytes[0] & 0x0f};
44     var state = Sha256.init(.{});
45     state.update(&first);
46     state.update(bytes[offset..]);
47     var result: [32]u8 = undefined;
48     state.final(&result);
49     return result;
50 }
51 
52 /// Returns the first 16 bytes of the full hash of one Reticulum datagram
53 /// (*packet*), the width carried as the destination so a caller can address the
54 /// proof sent back for a packet, following Reticulum@1.5.0
55 /// RNS/Packet.py:349-351.
56 pub fn truncated(bytes: []const u8) HashError![16]u8 {
57     const result = try full(bytes);
58     return result[0..16].*;
59 }