| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | const assert = std.debug.assert; |
| 4 | const math = std.math; |
| 5 | const mem = std.mem; |
| 6 | |
| 7 | const kangarootwelve = @import("kangarootwelve.zig"); |
| 8 | |
| 9 | const KeccakState = std.crypto.core.keccak.State; |
| 10 | |
| 11 | pub const Sha3_224 = Keccak(1600, 224, 0x06, 24); |
| 12 | pub const Sha3_256 = Keccak(1600, 256, 0x06, 24); |
| 13 | pub const Sha3_384 = Keccak(1600, 384, 0x06, 24); |
| 14 | pub const Sha3_512 = Keccak(1600, 512, 0x06, 24); |
| 15 | |
| 16 | pub const Keccak256 = Keccak(1600, 256, 0x01, 24); |
| 17 | pub const Keccak512 = Keccak(1600, 512, 0x01, 24); |
| 18 | |
| 19 | pub const Shake128 = Shake(128); |
| 20 | pub const Shake256 = Shake(256); |
| 21 | |
| 22 | pub const CShake128 = CShake(128, null); |
| 23 | pub const CShake256 = CShake(256, null); |
| 24 | |
| 25 | pub const KMac128 = KMac(128); |
| 26 | pub const KMac256 = KMac(256); |
| 27 | |
| 28 | pub const TupleHash128 = TupleHash(128); |
| 29 | pub const TupleHash256 = TupleHash(256); |
| 30 | |
| 31 | pub const KT128 = kangarootwelve.KT128; |
| 32 | pub const KT256 = kangarootwelve.KT256; |
| 33 | |
| 34 | /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. |
| 35 | /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. |
| 36 | /// The delimiter is 0x1f by default, but can be changed for context-separation. |
| 37 | /// For a protocol that uses both KangarooTwelve and TurboSHAKE128, it is recommended to avoid using 0x06, 0x07 or 0x0b for the delimiter. |
| 38 | pub fn TurboShake128(delim: ?u7) type { |
| 39 | return TurboShake(128, delim); |
| 40 | } |
| 41 | |
| 42 | /// TurboSHAKE256 is a XOF (a secure hash function with a variable output length), with a 256 bit security level. |
| 43 | /// It is based on the same permutation as SHA3 and SHAKE256, but which much higher performance. |
| 44 | /// The delimiter is 0x1f by default, but can be changed for context-separation. |
| 45 | pub fn TurboShake256(comptime delim: ?u7) type { |
| 46 | return TurboShake(256, delim); |
| 47 | } |
| 48 | |
| 49 | /// A generic Keccak hash function. |
| 50 | pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 51 | comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length |
| 52 | |
| 53 | const State = KeccakState(f, output_bits * 2, rounds); |
| 54 | |
| 55 | return struct { |
| 56 | const Self = @This(); |
| 57 | |
| 58 | st: State, |
| 59 | |
| 60 | /// The output length, in bytes. |
| 61 | pub const digest_length: comptime_int = @divCeil(output_bits, 8); |
| 62 | /// The block length, or rate, in bytes. |
| 63 | pub const block_length = State.rate; |
| 64 | /// The delimiter can be overwritten in the options. |
| 65 | pub const Options = struct { delim: u8 = default_delim }; |
| 66 | |
| 67 | /// Initialize a Keccak hash function. |
| 68 | pub fn init(options: Options) Self { |
| 69 | return Self{ .st = .{ .delim = options.delim } }; |
| 70 | } |
| 71 | |
| 72 | /// Hash a slice of bytes. |
| 73 | pub fn hash(bytes: []const u8, out: *[digest_length]u8, options: Options) void { |
| 74 | var st = Self.init(options); |
| 75 | st.update(bytes); |
| 76 | st.final(out); |
| 77 | } |
| 78 | |
| 79 | /// Absorb a slice of bytes into the state. |
| 80 | pub fn update(self: *Self, bytes: []const u8) void { |
| 81 | self.st.absorb(bytes); |
| 82 | } |
| 83 | |
| 84 | /// Return the hash of the absorbed bytes. |
| 85 | pub fn final(self: *Self, out: *[digest_length]u8) void { |
| 86 | self.st.pad(); |
| 87 | self.st.squeeze(out[0..]); |
| 88 | } |
| 89 | }; |
| 90 | } |
| 91 | |
| 92 | /// The SHAKE extendable output hash function. |
| 93 | pub fn Shake(comptime security_level: u11) type { |
| 94 | return ShakeLike(security_level, 0x1f, 24); |
| 95 | } |
| 96 | |
| 97 | /// The TurboSHAKE extendable output hash function. |
| 98 | /// It is based on the same permutation as SHA3 and SHAKE, but which much higher performance. |
| 99 | /// The delimiter is 0x1f by default, but can be changed for context-separation. |
| 100 | /// https://eprint.iacr.org/2023/342 |
| 101 | pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type { |
| 102 | comptime assert(security_level <= 256); |
| 103 | const d = delim orelse 0x1f; |
| 104 | comptime assert(d >= 0x01); // delimiter must be >= 1 |
| 105 | return ShakeLike(security_level, d, 12); |
| 106 | } |
| 107 | |
| 108 | fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 109 | const f = 1600; |
| 110 | const State = KeccakState(f, security_level * 2, rounds); |
| 111 | |
| 112 | return struct { |
| 113 | const Self = @This(); |
| 114 | |
| 115 | st: State, |
| 116 | buf: [State.rate]u8 = undefined, |
| 117 | offset: usize = 0, |
| 118 | padded: bool = false, |
| 119 | |
| 120 | /// The recommended output length, in bytes. |
| 121 | pub const digest_length = security_level / 8 * 2; |
| 122 | /// The block length, or rate, in bytes. |
| 123 | pub const block_length = State.rate; |
| 124 | /// The delimiter can be overwritten in the options. |
| 125 | pub const Options = struct { delim: u8 = default_delim }; |
| 126 | |
| 127 | /// Initialize a SHAKE extensible hash function. |
| 128 | pub fn init(options: Options) Self { |
| 129 | return Self{ .st = .{ .delim = options.delim } }; |
| 130 | } |
| 131 | |
| 132 | /// Hash a slice of bytes. |
| 133 | /// `out` can be any length. |
| 134 | pub fn hash(bytes: []const u8, out: []u8, options: Options) void { |
| 135 | var st = Self.init(options); |
| 136 | st.update(bytes); |
| 137 | st.squeeze(out); |
| 138 | } |
| 139 | |
| 140 | /// Absorb a slice of bytes into the state. |
| 141 | pub fn update(self: *Self, bytes: []const u8) void { |
| 142 | self.st.absorb(bytes); |
| 143 | } |
| 144 | |
| 145 | /// Squeeze a slice of bytes from the state. |
| 146 | /// `out` can be any length, and the function can be called multiple times. |
| 147 | pub fn squeeze(self: *Self, out_: []u8) void { |
| 148 | if (!self.padded) { |
| 149 | self.st.pad(); |
| 150 | self.padded = true; |
| 151 | } |
| 152 | var out = out_; |
| 153 | if (self.offset > 0) { |
| 154 | const left = self.buf.len - self.offset; |
| 155 | if (left > 0) { |
| 156 | const n = @min(left, out.len); |
| 157 | @memcpy(out[0..n], self.buf[self.offset..][0..n]); |
| 158 | out = out[n..]; |
| 159 | self.offset += n; |
| 160 | if (out.len == 0) { |
| 161 | return; |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | const full_blocks = out[0 .. out.len - out.len % State.rate]; |
| 166 | if (full_blocks.len > 0) { |
| 167 | self.st.squeeze(full_blocks); |
| 168 | out = out[full_blocks.len..]; |
| 169 | } |
| 170 | if (out.len > 0) { |
| 171 | self.st.squeeze(self.buf[0..]); |
| 172 | @memcpy(out[0..], self.buf[0..out.len]); |
| 173 | self.offset = out.len; |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | /// Return the hash of the absorbed bytes. |
| 178 | /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead). |
| 179 | pub fn final(self: *Self, out: []u8) void { |
| 180 | self.squeeze(out); |
| 181 | self.st.st.clear(0, State.rate); |
| 182 | } |
| 183 | |
| 184 | /// Align the input to a block boundary. |
| 185 | pub fn fillBlock(self: *Self) void { |
| 186 | self.st.fillBlock(); |
| 187 | } |
| 188 | }; |
| 189 | } |
| 190 | |
| 191 | /// The cSHAKE extendable output hash function. |
| 192 | /// cSHAKE is similar to SHAKE, but in addition to the input message, it also takes an optional context (aka customization string). |
| 193 | pub fn CShake(comptime security_level: u11, comptime fname: ?[]const u8) type { |
| 194 | return CShakeLike(security_level, 0x04, 24, fname); |
| 195 | } |
| 196 | |
| 197 | fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5, comptime fname: ?[]const u8) type { |
| 198 | return struct { |
| 199 | const Shaker = ShakeLike(security_level, default_delim, rounds); |
| 200 | shaker: Shaker, |
| 201 | |
| 202 | /// The recommended output length, in bytes. |
| 203 | pub const digest_length = Shaker.digest_length; |
| 204 | /// The block length, or rate, in bytes. |
| 205 | pub const block_length = Shaker.block_length; |
| 206 | |
| 207 | /// cSHAKE options can include a context string. |
| 208 | pub const Options = struct { context: ?[]const u8 = null }; |
| 209 | |
| 210 | const Self = @This(); |
| 211 | |
| 212 | /// Initialize a SHAKE extensible hash function. |
| 213 | pub fn init(options: Options) Self { |
| 214 | if (fname == null and options.context == null) { |
| 215 | return Self{ .shaker = Shaker.init(.{ .delim = 0x1f }) }; |
| 216 | } |
| 217 | var shaker = Shaker.init(.{}); |
| 218 | comptime assert(Shaker.block_length % 8 == 0); |
| 219 | const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); |
| 220 | shaker.update(encoded_rate_len.slice()); |
| 221 | const encoded_zero = comptime NistLengthEncoding.encode(.left, 0); |
| 222 | if (fname) |name| { |
| 223 | const encoded_fname_len = comptime NistLengthEncoding.encode(.left, name.len); |
| 224 | const encoded_fname = comptime encoded_fname_len.slice() ++ name; |
| 225 | shaker.update(encoded_fname); |
| 226 | } else { |
| 227 | shaker.update(encoded_zero.slice()); |
| 228 | } |
| 229 | if (options.context) |context| { |
| 230 | const encoded_context_len = NistLengthEncoding.encode(.left, context.len); |
| 231 | shaker.update(encoded_context_len.slice()); |
| 232 | shaker.update(context); |
| 233 | } else { |
| 234 | shaker.update(encoded_zero.slice()); |
| 235 | } |
| 236 | shaker.st.fillBlock(); |
| 237 | return Self{ .shaker = shaker }; |
| 238 | } |
| 239 | |
| 240 | /// Hash a slice of bytes. |
| 241 | /// `out` can be any length. |
| 242 | pub fn hash(bytes: []const u8, out: []u8, options: Options) void { |
| 243 | var st = Self.init(options); |
| 244 | st.update(bytes); |
| 245 | st.squeeze(out); |
| 246 | } |
| 247 | |
| 248 | /// Absorb a slice of bytes into the state. |
| 249 | pub fn update(self: *Self, bytes: []const u8) void { |
| 250 | self.shaker.update(bytes); |
| 251 | } |
| 252 | |
| 253 | /// Squeeze a slice of bytes from the state. |
| 254 | /// `out` can be any length, and the function can be called multiple times. |
| 255 | pub fn squeeze(self: *Self, out: []u8) void { |
| 256 | self.shaker.squeeze(out); |
| 257 | } |
| 258 | |
| 259 | /// Return the hash of the absorbed bytes. |
| 260 | /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead). |
| 261 | pub fn final(self: *Self, out: []u8) void { |
| 262 | self.shaker.final(out); |
| 263 | } |
| 264 | |
| 265 | /// Align the input to a block boundary. |
| 266 | pub fn fillBlock(self: *Self) void { |
| 267 | self.shaker.fillBlock(); |
| 268 | } |
| 269 | }; |
| 270 | } |
| 271 | |
| 272 | /// The KMAC extendable output authentication function. |
| 273 | /// KMAC is a keyed version of the cSHAKE function, with an optional context. |
| 274 | /// It can be used as an SHA-3 based alternative to HMAC, as well as a generic keyed XoF (extendable output function). |
| 275 | pub fn KMac(comptime security_level: u11) type { |
| 276 | return KMacLike(security_level, 0x04, 24); |
| 277 | } |
| 278 | |
| 279 | fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 280 | const CShaker = CShakeLike(security_level, default_delim, rounds, "KMAC"); |
| 281 | |
| 282 | return struct { |
| 283 | const Self = @This(); |
| 284 | |
| 285 | /// The recommended output length, in bytes. |
| 286 | pub const mac_length = CShaker.digest_length; |
| 287 | /// The minimum output length, in bytes. |
| 288 | pub const mac_length_min = 4; |
| 289 | /// The recommended key length, in bytes. |
| 290 | pub const key_length = security_level / 8; |
| 291 | /// The minimum key length, in bytes. |
| 292 | pub const key_length_min = 0; |
| 293 | /// The block length, or rate, in bytes. |
| 294 | pub const block_length = CShaker.block_length; |
| 295 | |
| 296 | cshaker: CShaker, |
| 297 | xof_mode: bool = false, |
| 298 | |
| 299 | /// KMAC options can include a context string. |
| 300 | pub const Options = struct { |
| 301 | context: ?[]const u8 = null, |
| 302 | }; |
| 303 | |
| 304 | /// Initialize a state for the KMAC function, with an optional context and an arbitrary-long key. |
| 305 | /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. |
| 306 | /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. |
| 307 | pub fn initWithOptions(key: []const u8, options: Options) Self { |
| 308 | var cshaker = CShaker.init(.{ .context = options.context }); |
| 309 | const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); |
| 310 | cshaker.update(encoded_rate_len.slice()); |
| 311 | const encoded_key_len = NistLengthEncoding.encode(.left, key.len); |
| 312 | cshaker.update(encoded_key_len.slice()); |
| 313 | cshaker.update(key); |
| 314 | cshaker.fillBlock(); |
| 315 | return Self{ |
| 316 | .cshaker = cshaker, |
| 317 | }; |
| 318 | } |
| 319 | |
| 320 | /// Initialize a state for the KMAC function. |
| 321 | /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. |
| 322 | /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. |
| 323 | pub fn init(key: []const u8) Self { |
| 324 | return initWithOptions(key, .{}); |
| 325 | } |
| 326 | |
| 327 | /// Add data to the state. |
| 328 | pub fn update(self: *Self, b: []const u8) void { |
| 329 | self.cshaker.update(b); |
| 330 | } |
| 331 | |
| 332 | /// Return an authentication tag for the current state. |
| 333 | pub fn final(self: *Self, out: []u8) void { |
| 334 | const encoded_out_len = NistLengthEncoding.encode(.right, out.len); |
| 335 | self.update(encoded_out_len.slice()); |
| 336 | self.cshaker.final(out); |
| 337 | } |
| 338 | |
| 339 | /// Squeeze a slice of bytes from the state. |
| 340 | /// `out` can be any length, and the function can be called multiple times. |
| 341 | pub fn squeeze(self: *Self, out: []u8) void { |
| 342 | if (!self.xof_mode) { |
| 343 | const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); |
| 344 | self.update(encoded_out_len.slice()); |
| 345 | self.xof_mode = true; |
| 346 | } |
| 347 | self.cshaker.squeeze(out); |
| 348 | } |
| 349 | |
| 350 | /// Return an authentication tag for a message and a key, with an optional context. |
| 351 | pub fn createWithOptions(out: []u8, msg: []const u8, key: []const u8, options: Options) void { |
| 352 | var ctx = Self.initWithOptions(key, options); |
| 353 | ctx.update(msg); |
| 354 | ctx.final(out); |
| 355 | } |
| 356 | |
| 357 | /// Return an authentication tag for a message and a key. |
| 358 | pub fn create(out: []u8, msg: []const u8, key: []const u8) void { |
| 359 | var ctx = Self.init(key); |
| 360 | ctx.update(msg); |
| 361 | ctx.final(out); |
| 362 | } |
| 363 | }; |
| 364 | } |
| 365 | |
| 366 | /// The TupleHash extendable output hash function, with domain-separated inputs. |
| 367 | /// TupleHash is a secure hash function with a variable output length, based on the cSHAKE function. |
| 368 | /// It is designed for unambiguously hashing tuples of data. |
| 369 | /// |
| 370 | /// With most hash functions, calling `update("A")` followed by `update("B")`is identical to `update("AB")`. |
| 371 | /// With TupleHash, this is not the case: `update("A"); update("B")` is different from `update("AB")`. |
| 372 | /// |
| 373 | /// Any number of inputs can be hashed, and the output depends on individual inputs and their order. |
| 374 | pub fn TupleHash(comptime security_level: u11) type { |
| 375 | return TupleHashLike(security_level, 0x04, 24); |
| 376 | } |
| 377 | |
| 378 | fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 379 | const CShaker = CShakeLike(security_level, default_delim, rounds, "TupleHash"); |
| 380 | |
| 381 | return struct { |
| 382 | const Self = @This(); |
| 383 | |
| 384 | /// The output length, in bytes. |
| 385 | pub const digest_length = CShaker.digest_length; |
| 386 | /// The block length, or rate, in bytes. |
| 387 | pub const block_length = CShaker.block_length; |
| 388 | |
| 389 | cshaker: CShaker, |
| 390 | xof_mode: bool = false, |
| 391 | |
| 392 | /// TupleHash options can include a context string. |
| 393 | pub const Options = struct { |
| 394 | context: ?[]const u8 = null, |
| 395 | }; |
| 396 | |
| 397 | /// Initialize a state for the TupleHash function, with an optional context. |
| 398 | /// If the context is going to be reused, the structure can be initialized once, and cloned for each message. |
| 399 | /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. |
| 400 | /// |
| 401 | /// A key can be optionally added to the context to create a keyed TupleHash function, similar to KMAC. |
| 402 | pub fn initWithOptions(options: Options) Self { |
| 403 | const cshaker = CShaker.init(.{ .context = options.context }); |
| 404 | return Self{ |
| 405 | .cshaker = cshaker, |
| 406 | }; |
| 407 | } |
| 408 | |
| 409 | /// Initialize a state for the MAC function. |
| 410 | pub fn init() Self { |
| 411 | return initWithOptions(.{}); |
| 412 | } |
| 413 | |
| 414 | /// Add data to the state, separated from previous updates. |
| 415 | pub fn update(self: *Self, b: []const u8) void { |
| 416 | const encoded_b_len = NistLengthEncoding.encode(.left, b.len); |
| 417 | self.cshaker.update(encoded_b_len.slice()); |
| 418 | self.cshaker.update(b); |
| 419 | } |
| 420 | |
| 421 | /// Return an authentication tag for the current state. |
| 422 | pub fn final(self: *Self, out: []u8) void { |
| 423 | const encoded_out_len = NistLengthEncoding.encode(.right, out.len); |
| 424 | self.cshaker.update(encoded_out_len.slice()); |
| 425 | self.cshaker.final(out); |
| 426 | } |
| 427 | |
| 428 | /// Align the input to a block boundary. |
| 429 | pub fn fillBlock(self: *Self) void { |
| 430 | self.cshaker.fillBlock(); |
| 431 | } |
| 432 | |
| 433 | /// Squeeze a slice of bytes from the state. |
| 434 | /// `out` can be any length, and the function can be called multiple times. |
| 435 | pub fn squeeze(self: *Self, out: []u8) void { |
| 436 | if (!self.xof_mode) { |
| 437 | const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); |
| 438 | self.update(encoded_out_len.slice()); |
| 439 | self.xof_mode = true; |
| 440 | } |
| 441 | self.cshaker.squeeze(out); |
| 442 | } |
| 443 | }; |
| 444 | } |
| 445 | |
| 446 | /// The NIST SP 800-185 encoded length format. |
| 447 | pub const NistLengthEncoding = enum { |
| 448 | left, |
| 449 | right, |
| 450 | |
| 451 | /// A length encoded according to NIST SP 800-185. |
| 452 | pub const Length = struct { |
| 453 | /// The size of the encoded value, in bytes. |
| 454 | len: usize = 0, |
| 455 | /// A buffer to store the encoded length. |
| 456 | buf: [@sizeOf(usize) + 1]u8 = undefined, |
| 457 | |
| 458 | /// Return the encoded length as a slice. |
| 459 | pub fn slice(self: *const Length) []const u8 { |
| 460 | return self.buf[0..self.len]; |
| 461 | } |
| 462 | }; |
| 463 | |
| 464 | /// Encode a length according to NIST SP 800-185. |
| 465 | pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length { |
| 466 | const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3; |
| 467 | const len_bytes = @divCeil(len_bits, 8); |
| 468 | |
| 469 | var res = Length{ .len = len_bytes + 1 }; |
| 470 | if (encoding == .right) { |
| 471 | res.buf[len_bytes] = @intCast(len_bytes); |
| 472 | } |
| 473 | const end = if (encoding == .right) len_bytes - 1 else len_bytes; |
| 474 | res.buf[end] = @truncate(len << 3); |
| 475 | var len_ = len >> 5; |
| 476 | for (1..len_bytes) |i| { |
| 477 | res.buf[end - i] = @truncate(len_); |
| 478 | len_ >>= 8; |
| 479 | } |
| 480 | if (encoding == .left) { |
| 481 | res.buf[0] = @intCast(len_bytes); |
| 482 | } |
| 483 | return res; |
| 484 | } |
| 485 | }; |
| 486 | |
| 487 | const htest = @import("test.zig"); |
| 488 | |
| 489 | test { |
| 490 | _ = kangarootwelve; |
| 491 | } |
| 492 | |
| 493 | test "sha3-224 single" { |
| 494 | try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""); |
| 495 | try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"); |
| 496 | try htest.assertEqualHash(Sha3_224, "543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 497 | } |
| 498 | |
| 499 | test "sha3-224 streaming" { |
| 500 | var h = Sha3_224.init(.{}); |
| 501 | var out: [28]u8 = undefined; |
| 502 | |
| 503 | h.final(out[0..]); |
| 504 | try htest.assertEqual("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", out[0..]); |
| 505 | |
| 506 | h = Sha3_224.init(.{}); |
| 507 | h.update("abc"); |
| 508 | h.final(out[0..]); |
| 509 | try htest.assertEqual("e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", out[0..]); |
| 510 | |
| 511 | h = Sha3_224.init(.{}); |
| 512 | h.update("a"); |
| 513 | h.update("b"); |
| 514 | h.update("c"); |
| 515 | h.final(out[0..]); |
| 516 | try htest.assertEqual("e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", out[0..]); |
| 517 | } |
| 518 | |
| 519 | test "sha3-256 single" { |
| 520 | try htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""); |
| 521 | try htest.assertEqualHash(Sha3_256, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"); |
| 522 | try htest.assertEqualHash(Sha3_256, "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 523 | } |
| 524 | |
| 525 | test "sha3-256 streaming" { |
| 526 | var h = Sha3_256.init(.{}); |
| 527 | var out: [32]u8 = undefined; |
| 528 | |
| 529 | h.final(out[0..]); |
| 530 | try htest.assertEqual("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", out[0..]); |
| 531 | |
| 532 | h = Sha3_256.init(.{}); |
| 533 | h.update("abc"); |
| 534 | h.final(out[0..]); |
| 535 | try htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]); |
| 536 | |
| 537 | h = Sha3_256.init(.{}); |
| 538 | h.update("a"); |
| 539 | h.update("b"); |
| 540 | h.update("c"); |
| 541 | h.final(out[0..]); |
| 542 | try htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]); |
| 543 | } |
| 544 | |
| 545 | test "sha3-256 aligned final" { |
| 546 | var block: [Sha3_256.block_length]u8 = @splat(0); |
| 547 | var out: [Sha3_256.digest_length]u8 = undefined; |
| 548 | |
| 549 | var h = Sha3_256.init(.{}); |
| 550 | h.update(&block); |
| 551 | h.final(out[0..]); |
| 552 | } |
| 553 | |
| 554 | test "sha3-384 single" { |
| 555 | const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004"; |
| 556 | try htest.assertEqualHash(Sha3_384, h1, ""); |
| 557 | const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"; |
| 558 | try htest.assertEqualHash(Sha3_384, h2, "abc"); |
| 559 | const h3 = "79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7"; |
| 560 | try htest.assertEqualHash(Sha3_384, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 561 | } |
| 562 | |
| 563 | test "sha3-384 streaming" { |
| 564 | var h = Sha3_384.init(.{}); |
| 565 | var out: [48]u8 = undefined; |
| 566 | |
| 567 | const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004"; |
| 568 | h.final(out[0..]); |
| 569 | try htest.assertEqual(h1, out[0..]); |
| 570 | |
| 571 | const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25"; |
| 572 | h = Sha3_384.init(.{}); |
| 573 | h.update("abc"); |
| 574 | h.final(out[0..]); |
| 575 | try htest.assertEqual(h2, out[0..]); |
| 576 | |
| 577 | h = Sha3_384.init(.{}); |
| 578 | h.update("a"); |
| 579 | h.update("b"); |
| 580 | h.update("c"); |
| 581 | h.final(out[0..]); |
| 582 | try htest.assertEqual(h2, out[0..]); |
| 583 | } |
| 584 | |
| 585 | test "sha3-512 single" { |
| 586 | const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"; |
| 587 | try htest.assertEqualHash(Sha3_512, h1, ""); |
| 588 | const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"; |
| 589 | try htest.assertEqualHash(Sha3_512, h2, "abc"); |
| 590 | const h3 = "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185"; |
| 591 | try htest.assertEqualHash(Sha3_512, h3, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 592 | } |
| 593 | |
| 594 | test "sha3-512 streaming" { |
| 595 | var h = Sha3_512.init(.{}); |
| 596 | var out: [64]u8 = undefined; |
| 597 | |
| 598 | const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"; |
| 599 | h.final(out[0..]); |
| 600 | try htest.assertEqual(h1, out[0..]); |
| 601 | |
| 602 | const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"; |
| 603 | h = Sha3_512.init(.{}); |
| 604 | h.update("abc"); |
| 605 | h.final(out[0..]); |
| 606 | try htest.assertEqual(h2, out[0..]); |
| 607 | |
| 608 | h = Sha3_512.init(.{}); |
| 609 | h.update("a"); |
| 610 | h.update("b"); |
| 611 | h.update("c"); |
| 612 | h.final(out[0..]); |
| 613 | try htest.assertEqual(h2, out[0..]); |
| 614 | } |
| 615 | |
| 616 | test "sha3-512 aligned final" { |
| 617 | var block: [Sha3_512.block_length]u8 = @splat(0); |
| 618 | var out: [Sha3_512.digest_length]u8 = undefined; |
| 619 | |
| 620 | var h = Sha3_512.init(.{}); |
| 621 | h.update(&block); |
| 622 | h.final(out[0..]); |
| 623 | } |
| 624 | |
| 625 | test "keccak-256 single" { |
| 626 | try htest.assertEqualHash(Keccak256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ""); |
| 627 | try htest.assertEqualHash(Keccak256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc"); |
| 628 | try htest.assertEqualHash(Keccak256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 629 | } |
| 630 | |
| 631 | test "keccak-512 single" { |
| 632 | try htest.assertEqualHash(Keccak512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", ""); |
| 633 | try htest.assertEqualHash(Keccak512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc"); |
| 634 | try htest.assertEqualHash(Keccak512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"); |
| 635 | } |
| 636 | |
| 637 | test "SHAKE-128 single" { |
| 638 | var out: [10]u8 = undefined; |
| 639 | Shake128.hash("hello123", &out, .{}); |
| 640 | try htest.assertEqual("1b85861510bc4d8e467d", &out); |
| 641 | } |
| 642 | |
| 643 | test "SHAKE-128 multisqueeze" { |
| 644 | var out: [10]u8 = undefined; |
| 645 | var h = Shake128.init(.{}); |
| 646 | h.update("hello123"); |
| 647 | h.squeeze(out[0..4]); |
| 648 | h.squeeze(out[4..]); |
| 649 | try htest.assertEqual("1b85861510bc4d8e467d", &out); |
| 650 | } |
| 651 | |
| 652 | test "SHAKE-128 multisqueeze with multiple blocks" { |
| 653 | var out: [100]u8 = undefined; |
| 654 | var out2: [100]u8 = undefined; |
| 655 | |
| 656 | var h = Shake128.init(.{}); |
| 657 | h.update("hello123"); |
| 658 | h.squeeze(out[0..50]); |
| 659 | h.squeeze(out[50..]); |
| 660 | |
| 661 | var h2 = Shake128.init(.{}); |
| 662 | h2.update("hello123"); |
| 663 | h2.squeeze(&out2); |
| 664 | try std.testing.expectEqualSlices(u8, &out, &out2); |
| 665 | } |
| 666 | |
| 667 | test "SHAKE-256 single" { |
| 668 | var out: [10]u8 = undefined; |
| 669 | Shake256.hash("hello123", &out, .{}); |
| 670 | try htest.assertEqual("ade612ba265f92de4a37", &out); |
| 671 | } |
| 672 | |
| 673 | test "TurboSHAKE-128" { |
| 674 | var out: [32]u8 = undefined; |
| 675 | TurboShake(128, 0x06).hash("\xff", &out, .{}); |
| 676 | try htest.assertEqual("8ec9c66465ed0d4a6c35d13506718d687a25cb05c74cca1e42501abd83874a67", &out); |
| 677 | } |
| 678 | |
| 679 | test "SHA-3 with streaming" { |
| 680 | var msg: [613]u8 = [613]u8{ 0x97, 0xd1, 0x2d, 0x1a, 0x16, 0x2d, 0x36, 0x4d, 0x20, 0x62, 0x19, 0x0b, 0x14, 0x93, 0xbb, 0xf8, 0x5b, 0xea, 0x04, 0xc2, 0x61, 0x8e, 0xd6, 0x08, 0x81, 0xa1, 0x1d, 0x73, 0x27, 0x48, 0xbf, 0xa4, 0xba, 0xb1, 0x9a, 0x48, 0x9c, 0xf9, 0x9b, 0xff, 0x34, 0x48, 0xa9, 0x75, 0xea, 0xc8, 0xa3, 0x48, 0x24, 0x9d, 0x75, 0x27, 0x48, 0xec, 0x03, 0xb0, 0xbb, 0xdf, 0x33, 0x90, 0xe3, 0x93, 0xed, 0x68, 0x24, 0x39, 0x12, 0xdf, 0xea, 0xee, 0x8c, 0x9f, 0x96, 0xde, 0x42, 0x46, 0x8c, 0x2b, 0x17, 0x83, 0x36, 0xfb, 0xf4, 0xf7, 0xff, 0x79, 0xb9, 0x45, 0x41, 0xc9, 0x56, 0x1a, 0x6b, 0x0c, 0xa4, 0x1a, 0xdd, 0x6b, 0x95, 0xe8, 0x03, 0x0f, 0x09, 0x29, 0x40, 0x1b, 0xea, 0x87, 0xfa, 0xb9, 0x18, 0xa9, 0x95, 0x07, 0x7c, 0x2f, 0x7c, 0x33, 0xfb, 0xc5, 0x11, 0x5e, 0x81, 0x0e, 0xbc, 0xae, 0xec, 0xb3, 0xe1, 0x4a, 0x26, 0x56, 0xe8, 0x5b, 0x11, 0x9d, 0x37, 0x06, 0x9b, 0x34, 0x31, 0x6e, 0xa3, 0xba, 0x41, 0xbc, 0x11, 0xd8, 0xc5, 0x15, 0xc9, 0x30, 0x2c, 0x9b, 0xb6, 0x71, 0xd8, 0x7c, 0xbc, 0x38, 0x2f, 0xd5, 0xbd, 0x30, 0x96, 0xd4, 0xa3, 0x00, 0x77, 0x9d, 0x55, 0x4a, 0x33, 0x53, 0xb6, 0xb3, 0x35, 0x1b, 0xae, 0xe5, 0xdc, 0x22, 0x23, 0x85, 0x95, 0x88, 0xf9, 0x3b, 0xbf, 0x74, 0x13, 0xaa, 0xcb, 0x0a, 0x60, 0x79, 0x13, 0x79, 0xc0, 0x4a, 0x02, 0xdb, 0x1c, 0xc9, 0xff, 0x60, 0x57, 0x9a, 0x70, 0x28, 0x58, 0x60, 0xbc, 0x57, 0x07, 0xc7, 0x47, 0x1a, 0x45, 0x71, 0x76, 0x94, 0xfb, 0x05, 0xad, 0xec, 0x12, 0x29, 0x5a, 0x44, 0x6a, 0x81, 0xd9, 0xc6, 0xf0, 0xb6, 0x9b, 0x97, 0x83, 0x69, 0xfb, 0xdc, 0x0d, 0x4a, 0x67, 0xbc, 0x72, 0xf5, 0x43, 0x5e, 0x9b, 0x13, 0xf2, 0xe4, 0x6d, 0x49, 0xdb, 0x76, 0xcb, 0x42, 0x6a, 0x3c, 0x9f, 0xa1, 0xfe, 0x5e, 0xca, 0x0a, 0xfc, 0xfa, 0x39, 0x27, 0xd1, 0x3c, 0xcb, 0x9a, 0xde, 0x4c, 0x6b, 0x09, 0x8b, 0x49, 0xfd, 0x1e, 0x3d, 0x5e, 0x67, 0x7c, 0x57, 0xad, 0x90, 0xcc, 0x46, 0x5f, 0x5c, 0xae, 0x6a, 0x9c, 0xb2, 0xcd, 0x2c, 0x89, 0x78, 0xcf, 0xf1, 0x49, 0x96, 0x55, 0x1e, 0x04, 0xef, 0x0e, 0x1c, 0xde, 0x6c, 0x96, 0x51, 0x00, 0xee, 0x9a, 0x1f, 0x8d, 0x61, 0xbc, 0xeb, 0xb1, 0xa6, 0xa5, 0x21, 0x8b, 0xa7, 0xf8, 0x25, 0x41, 0x48, 0x62, 0x5b, 0x01, 0x6c, 0x7c, 0x2a, 0xe8, 0xff, 0xf9, 0xf9, 0x1f, 0xe2, 0x79, 0x2e, 0xd1, 0xff, 0xa3, 0x2e, 0x1c, 0x3a, 0x1a, 0x5d, 0x2b, 0x7b, 0x87, 0x25, 0x22, 0xa4, 0x90, 0xea, 0x26, 0x9d, 0xdd, 0x13, 0x60, 0x4c, 0x10, 0x03, 0xf6, 0x99, 0xd3, 0x21, 0x0c, 0x69, 0xc6, 0xd8, 0xc8, 0x9e, 0x94, 0x89, 0x51, 0x21, 0xe3, 0x9a, 0xcd, 0xda, 0x54, 0x72, 0x64, 0xae, 0x94, 0x79, 0x36, 0x81, 0x44, 0x14, 0x6d, 0x3a, 0x0e, 0xa6, 0x30, 0xbf, 0x95, 0x99, 0xa6, 0xf5, 0x7f, 0x4f, 0xef, 0xc6, 0x71, 0x2f, 0x36, 0x13, 0x14, 0xa2, 0x9d, 0xc2, 0x0c, 0x0d, 0x4e, 0xc0, 0x02, 0xd3, 0x6f, 0xee, 0x98, 0x5e, 0x24, 0x31, 0x74, 0x11, 0x96, 0x6e, 0x43, 0x57, 0xe8, 0x8e, 0xa0, 0x8d, 0x3d, 0x79, 0x38, 0x20, 0xc2, 0x0f, 0xb4, 0x75, 0x99, 0x3b, 0xb1, 0xf0, 0xe8, 0xe1, 0xda, 0xf9, 0xd4, 0xe6, 0xd6, 0xf4, 0x8a, 0x32, 0x4a, 0x4a, 0x25, 0xa8, 0xd9, 0x60, 0xd6, 0x33, 0x31, 0x97, 0xb9, 0xb6, 0xed, 0x5f, 0xfc, 0x15, 0xbd, 0x13, 0xc0, 0x3a, 0x3f, 0x1f, 0x2d, 0x09, 0x1d, 0xeb, 0x69, 0x6a, 0xfe, 0xd7, 0x95, 0x3e, 0x8a, 0x4e, 0xe1, 0x6e, 0x61, 0xb2, 0x6c, 0xe3, 0x2b, 0x70, 0x60, 0x7e, 0x8c, 0xe4, 0xdd, 0x27, 0x30, 0x7e, 0x0d, 0xc7, 0xb7, 0x9a, 0x1a, 0x3c, 0xcc, 0xa7, 0x22, 0x77, 0x14, 0x05, 0x50, 0x57, 0x31, 0x1b, 0xc8, 0xbf, 0xce, 0x52, 0xaf, 0x9c, 0x8e, 0x10, 0x2e, 0xd2, 0x16, 0xb6, 0x6e, 0x43, 0x10, 0xaf, 0x8b, 0xde, 0x1d, 0x60, 0xb2, 0x7d, 0xe6, 0x2f, 0x08, 0x10, 0x12, 0x7e, 0xb4, 0x76, 0x45, 0xb6, 0xd8, 0x9b, 0x26, 0x40, 0xa1, 0x63, 0x5c, 0x7a, 0x2a, 0xb1, 0x8c, 0xd6, 0xa4, 0x6f, 0x5a, 0xae, 0x33, 0x7e, 0x6d, 0x71, 0xf5, 0xc8, 0x6d, 0x80, 0x1c, 0x35, 0xfc, 0x3f, 0xc1, 0xa6, 0xc6, 0x1a, 0x15, 0x04, 0x6d, 0x76, 0x38, 0x32, 0x95, 0xb2, 0x51, 0x1a, 0xe9, 0x3e, 0x89, 0x9f, 0x0c, 0x79 }; |
| 681 | var out: [Sha3_256.digest_length]u8 = undefined; |
| 682 | |
| 683 | Sha3_256.hash(&msg, &out, .{}); |
| 684 | try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); |
| 685 | |
| 686 | var h = Sha3_256.init(.{}); |
| 687 | h.update(msg[0..64]); |
| 688 | h.update(msg[64..613]); |
| 689 | h.final(&out); |
| 690 | try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); |
| 691 | } |
| 692 | |
| 693 | test "cSHAKE-128 with no context nor function name" { |
| 694 | var out: [32]u8 = undefined; |
| 695 | CShake128.hash("hello123", &out, .{}); |
| 696 | try htest.assertEqual("1b85861510bc4d8e467d6f8a92270533cbaa7ba5e06c2d2a502854bac468b8b9", &out); |
| 697 | } |
| 698 | |
| 699 | test "cSHAKE-128 with context" { |
| 700 | var out: [32]u8 = undefined; |
| 701 | CShake128.hash("hello123", &out, .{ .context = "custom" }); |
| 702 | try htest.assertEqual("7509fa13a6bd3e38ad5c6fac042142c233996e40ebffc86c276f108b3b19cc6a", &out); |
| 703 | } |
| 704 | |
| 705 | test "cSHAKE-128 with context and function" { |
| 706 | var out: [32]u8 = undefined; |
| 707 | CShake(128, "function").hash("hello123", &out, .{ .context = "custom" }); |
| 708 | try htest.assertEqual("ad7f4d7db2d96587fcd5047c65d37c368f5366e3afac60bb9b66b0bb95dfb675", &out); |
| 709 | } |
| 710 | |
| 711 | test "cSHAKE-256" { |
| 712 | var out: [32]u8 = undefined; |
| 713 | CShake256.hash("hello123", &out, .{ .context = "custom" }); |
| 714 | try htest.assertEqual("dabe027eb1a6cbe3a0542d0560eb4e6b39146dd72ae1bf89c970a61bd93b1813", &out); |
| 715 | } |
| 716 | |
| 717 | test "KMAC-128 with empty key and message" { |
| 718 | var out: [KMac128.mac_length]u8 = undefined; |
| 719 | const key = ""; |
| 720 | KMac128.create(&out, "", key); |
| 721 | try htest.assertEqual("5c135c615152fb4d9784dd1155f9b6034e013fd77165c327dfa4d36701983ef7", &out); |
| 722 | } |
| 723 | |
| 724 | test "KMAC-128" { |
| 725 | var out: [KMac128.mac_length]u8 = undefined; |
| 726 | const key = "A KMAC secret key"; |
| 727 | KMac128.create(&out, "hello123", key); |
| 728 | try htest.assertEqual("1fa1c0d761129a83f9a4299ca137674de8373a3cc437799ae4c129e651627f8e", &out); |
| 729 | } |
| 730 | |
| 731 | test "KMAC-128 with a customization string" { |
| 732 | var out: [KMac128.mac_length]u8 = undefined; |
| 733 | const key = "A KMAC secret key"; |
| 734 | KMac128.createWithOptions(&out, "hello123", key, .{ .context = "custom" }); |
| 735 | try htest.assertEqual("c58c6d42dc00a27dfa8e7e08f8c9307cecb5d662ddb11b6c36057fc2e0e068ba", &out); |
| 736 | } |
| 737 | |
| 738 | test "KMACXOF-128" { |
| 739 | const key = "A KMAC secret key"; |
| 740 | var xof = KMac128.init(key); |
| 741 | xof.update("hello123"); |
| 742 | var out: [50]u8 = undefined; |
| 743 | xof.squeeze(&out); |
| 744 | try htest.assertEqual("628c2fb870d294b3673ac82d9f0d651aae6a5bb8084ea8cd8343cb888d075b9053173200a71f301141069c3c0322527981f7", &out); |
| 745 | xof.squeeze(&out); |
| 746 | try htest.assertEqual("7b638e178cfdac5727a4ea7694efaa967a65a1d0034501855acff506b4158d187d5a18d668e67b43f2abf61144b20ed4c09f", &out); |
| 747 | } |
| 748 | |
| 749 | test "KMACXOF-256" { |
| 750 | const key = "A KMAC secret key"; |
| 751 | var xof = KMac256.init(key); |
| 752 | xof.update("hello123"); |
| 753 | var out: [50]u8 = undefined; |
| 754 | xof.squeeze(&out); |
| 755 | try htest.assertEqual("23fc644bc2655ba6fde7b7c11f2804f22e8d8c6bd7db856268bf3370ce2362703f6c7e91916a1b8c116e60edfbcb25613054", &out); |
| 756 | xof.squeeze(&out); |
| 757 | try htest.assertEqual("ff97251020ff255ee65a1c1f5f78ebe904f61211c39f973f82fbce2b196b9f51c2cb12afe51549a0f1eaf7954e657ba11af3", &out); |
| 758 | } |
| 759 | |
| 760 | test "TupleHash-128" { |
| 761 | var st = TupleHash128.init(); |
| 762 | st.update("hello"); |
| 763 | st.update("123"); |
| 764 | var out: [32]u8 = undefined; |
| 765 | st.final(&out); |
| 766 | try htest.assertEqual("3938d49ade8ec0f0c305ac63497b2d2e8b2f650714f9667cc41816b1c11ffd20", &out); |
| 767 | } |
| 768 | |
| 769 | test "TupleHash-256" { |
| 770 | var st = TupleHash256.init(); |
| 771 | st.update("hello"); |
| 772 | st.update("123"); |
| 773 | var out: [64]u8 = undefined; |
| 774 | st.final(&out); |
| 775 | try htest.assertEqual("2dca563c2882f2ba4f46a441a4c5e13fb97150d1436fe99c7e4e43a2d20d0f1cd3d38483bde4a966930606dfa6c61c4ca6400aeedfb474d1bf0d7f6a70968289", &out); |
| 776 | } |