lib/reticulum/src/crypto/hmac.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 
 3 const HmacSha256 = std.crypto.auth.hmac.sha2.HmacSha256;
 4 
 5 pub const tag_length: u8 = HmacSha256.mac_length;
 6 
 7 /// An HMAC-SHA256 state that takes its input in pieces and then gives the
 8 /// 32-byte tag. The key derivation feeds three pieces into one of these for
 9 /// every output block, so it needs no buffer holding them together.
10 pub const Signer = struct {
11     state: HmacSha256,
12 
13     pub fn init(key: []const u8) Signer {
14         return .{ .state = HmacSha256.init(key) };
15     }
16 
17     pub fn update(self: *Signer, data: []const u8) void {
18         self.state.update(data);
19     }
20 
21     pub fn final(self: *Signer) [tag_length]u8 {
22         var tag: [tag_length]u8 = undefined;
23         self.state.final(&tag);
24         return tag;
25     }
26 };
27 
28 /// Returns the 32-byte HMAC-SHA256 tag of some data under a key, following
29 /// Reticulum@1.5.0 RNS/Cryptography/HMAC.py:27-45.
30 pub fn sign(key: []const u8, data: []const u8) [tag_length]u8 {
31     var signer = Signer.init(key);
32     signer.update(data);
33     return signer.final();
34 }
35 
36 /// Returns whether a tag matches the one the key and data give, following
37 /// Reticulum@1.5.0 RNS/Cryptography/HMAC.py:185-188. The comparison takes the
38 /// same time whatever the bytes are, so a failure says nothing about how far
39 /// the tag matched.
40 pub fn verify(key: []const u8, data: []const u8, tag: [tag_length]u8) bool {
41     const expected = sign(key, data);
42     return std.crypto.timing_safe.eql([tag_length]u8, expected, tag);
43 }