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 }