lib/stun/src/integrity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const HmacSha1 = std.crypto.auth.hmac.HmacSha1;
4 const HmacSha256 = std.crypto.auth.hmac.sha2.HmacSha256;
5 const Md5 = std.crypto.hash.Md5;
6 const Sha256 = std.crypto.hash.sha2.Sha256;
7
8 pub const CredentialError = error{
9 CredentialTooLong,
10 NonAsciiCredential,
11 };
12
13 pub const sha1_bytes: usize = HmacSha1.mac_length;
14 pub const sha256_bytes: usize = HmacSha256.mac_length;
15 pub const md5_key_bytes: usize = Md5.digest_length;
16 pub const sha256_key_bytes: usize = Sha256.digest_length;
17 pub const fingerprint_xor: u32 = 0x5354554e;
18 pub const max_credential_bytes: usize = 763;
19
20 pub fn shortTermKey(password: []const u8) CredentialError![]const u8 {
21 try validateAscii(password);
22 return password;
23 }
24
25 pub fn longTermKeyMd5(
26 username: []const u8,
27 realm: []const u8,
28 password: []const u8,
29 ) CredentialError![md5_key_bytes]u8 {
30 try validateCredentials(username, realm, password);
31 var hash = Md5.init(.{});
32 hash.update(username);
33 hash.update(":");
34 hash.update(realm);
35 hash.update(":");
36 hash.update(password);
37 var output: [md5_key_bytes]u8 = undefined;
38 hash.final(&output);
39 return output;
40 }
41
42 pub fn longTermKeySha256(
43 username: []const u8,
44 realm: []const u8,
45 password: []const u8,
46 ) CredentialError![sha256_key_bytes]u8 {
47 try validateCredentials(username, realm, password);
48 var hash = Sha256.init(.{});
49 hash.update(username);
50 hash.update(":");
51 hash.update(realm);
52 hash.update(":");
53 hash.update(password);
54 var output: [sha256_key_bytes]u8 = undefined;
55 hash.final(&output);
56 return output;
57 }
58
59 fn validateCredentials(
60 username: []const u8,
61 realm: []const u8,
62 password: []const u8,
63 ) CredentialError!void {
64 try validateAscii(username);
65 try validateAscii(realm);
66 try validateAscii(password);
67 }
68
69 fn validateAscii(bytes: []const u8) CredentialError!void {
70 if (bytes.len > max_credential_bytes) return error.CredentialTooLong;
71 for (0..max_credential_bytes) |index| {
72 if (index == bytes.len) break;
73 if (!std.ascii.isAscii(bytes[index])) return error.NonAsciiCredential;
74 }
75 }
76
77 pub fn hmacSha1Adjusted(
78 output: *[sha1_bytes]u8,
79 message: []const u8,
80 covered_end: usize,
81 adjusted_payload_len: u16,
82 key: []const u8,
83 ) void {
84 std.debug.assert(message.len >= 20);
85 std.debug.assert(covered_end >= 20);
86 std.debug.assert(covered_end <= message.len);
87 var adjusted_length: [2]u8 = undefined;
88 std.mem.writeInt(u16, &adjusted_length, adjusted_payload_len, .big);
89 var hmac = HmacSha1.init(key);
90 hmac.update(message[0..2]);
91 hmac.update(&adjusted_length);
92 hmac.update(message[4..covered_end]);
93 hmac.final(output);
94 }
95
96 pub fn hmacSha256Adjusted(
97 output: *[sha256_bytes]u8,
98 message: []const u8,
99 covered_end: usize,
100 adjusted_payload_len: u16,
101 key: []const u8,
102 ) void {
103 std.debug.assert(message.len >= 20);
104 std.debug.assert(covered_end >= 20);
105 std.debug.assert(covered_end <= message.len);
106 var adjusted_length: [2]u8 = undefined;
107 std.mem.writeInt(u16, &adjusted_length, adjusted_payload_len, .big);
108 var hmac = HmacSha256.init(key);
109 hmac.update(message[0..2]);
110 hmac.update(&adjusted_length);
111 hmac.update(message[4..covered_end]);
112 hmac.final(output);
113 }
114
115 pub fn fingerprint(message_prefix: []const u8) u32 {
116 return std.hash.Crc32.hash(message_prefix) ^ fingerprint_xor;
117 }
118
119 pub fn equalSha1(actual: [sha1_bytes]u8, expected: []const u8) bool {
120 if (expected.len != sha1_bytes) return false;
121 return std.crypto.timing_safe.eql([sha1_bytes]u8, actual, expected[0..sha1_bytes].*);
122 }
123
124 pub fn equalSha256(actual: [sha256_bytes]u8, expected: []const u8) bool {
125 if (expected.len < 16) return false;
126 if (expected.len > sha256_bytes) return false;
127 var difference: u8 = 0;
128 for (0..sha256_bytes) |index| {
129 if (index < expected.len) difference |= actual[index] ^ expected[index];
130 }
131 return difference == 0;
132 }