| ... | ... | @@ -18,11 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); |
| 18 | 18 | pub const Shake128 = Shake(128); |
| 19 | 19 | pub const Shake256 = Shake(256); |
| 20 | 20 | |
| 21 | pub const CShake128 = CShake(128, null); |
| 22 | pub const CShake256 = CShake(256, null); |
| 23 | |
| 24 | pub const KMac128 = KMac(128); |
| 25 | pub const KMac256 = KMac(256); |
| 26 | |
| 27 | pub const TupleHash128 = TupleHash(128); |
| 28 | pub const TupleHash256 = TupleHash(256); |
| 29 | |
| 21 | 30 | /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. |
| 22 | 31 | /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. |
| 23 | 32 | /// The delimiter is 0x1f by default, but can be changed for context-separation. |
| 24 | 33 | /// For a protocol that uses both KangarooTwelve and TurboSHAKE128, it is recommended to avoid using 0x06, 0x07 or 0x0b for the delimiter. |
| 25 | | pub fn TurboShake128(comptime delim: ?u7) type { |
| 34 | pub fn TurboShake128(delim: ?u7) type { |
| 26 | 35 | return TurboShake(128, delim); |
| 27 | 36 | } |
| 28 | 37 | |
| ... | ... | @@ -34,27 +43,26 @@ pub fn TurboShake256(comptime delim: ?u7) type { |
| 34 | 43 | } |
| 35 | 44 | |
| 36 | 45 | /// A generic Keccak hash function. |
| 37 | | pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type { |
| 46 | pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 38 | 47 | comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length |
| 39 | 48 | |
| 40 | | const State = KeccakState(f, output_bits * 2, delim, rounds); |
| 49 | const State = KeccakState(f, output_bits * 2, rounds); |
| 41 | 50 | |
| 42 | 51 | return struct { |
| 43 | 52 | const Self = @This(); |
| 44 | 53 | |
| 45 | | st: State = .{}, |
| 54 | st: State, |
| 46 | 55 | |
| 47 | 56 | /// The output length, in bytes. |
| 48 | 57 | pub const digest_length = output_bits / 8; |
| 49 | 58 | /// The block length, or rate, in bytes. |
| 50 | 59 | pub const block_length = State.rate; |
| 51 | | /// Keccak does not have any options. |
| 52 | | pub const Options = struct {}; |
| 60 | /// The delimiter can be overwritten in the options. |
| 61 | pub const Options = struct { delim: u8 = default_delim }; |
| 53 | 62 | |
| 54 | 63 | /// Initialize a Keccak hash function. |
| 55 | 64 | pub fn init(options: Options) Self { |
| 56 | | _ = options; |
| 57 | | return Self{}; |
| 65 | return Self{ .st = .{ .delim = options.delim } }; |
| 58 | 66 | } |
| 59 | 67 | |
| 60 | 68 | /// Hash a slice of bytes. |
| ... | ... | @@ -105,29 +113,28 @@ pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type { |
| 105 | 113 | return ShakeLike(security_level, d, 12); |
| 106 | 114 | } |
| 107 | 115 | |
| 108 | | fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { |
| 116 | fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 109 | 117 | const f = 1600; |
| 110 | | const State = KeccakState(f, security_level * 2, delim, rounds); |
| 118 | const State = KeccakState(f, security_level * 2, rounds); |
| 111 | 119 | |
| 112 | 120 | return struct { |
| 113 | 121 | const Self = @This(); |
| 114 | 122 | |
| 115 | | st: State = .{}, |
| 123 | st: State, |
| 116 | 124 | buf: [State.rate]u8 = undefined, |
| 117 | 125 | offset: usize = 0, |
| 118 | 126 | padded: bool = false, |
| 119 | 127 | |
| 120 | 128 | /// The recommended output length, in bytes. |
| 121 | | pub const digest_length = security_level / 2; |
| 129 | pub const digest_length = security_level / 8 * 2; |
| 122 | 130 | /// The block length, or rate, in bytes. |
| 123 | 131 | pub const block_length = State.rate; |
| 124 | | /// Keccak does not have any options. |
| 125 | | pub const Options = struct {}; |
| 132 | /// The delimiter can be overwritten in the options. |
| 133 | pub const Options = struct { delim: u8 = default_delim }; |
| 126 | 134 | |
| 127 | 135 | /// Initialize a SHAKE extensible hash function. |
| 128 | 136 | pub fn init(options: Options) Self { |
| 129 | | _ = options; |
| 130 | | return Self{}; |
| 137 | return Self{ .st = .{ .delim = options.delim } }; |
| 131 | 138 | } |
| 132 | 139 | |
| 133 | 140 | /// Hash a slice of bytes. |
| ... | ... | @@ -182,6 +189,11 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: |
| 182 | 189 | self.st.st.clear(0, State.rate); |
| 183 | 190 | } |
| 184 | 191 | |
| 192 | /// Align the input to a block boundary. |
| 193 | pub fn fillBlock(self: *Self) void { |
| 194 | self.st.fillBlock(); |
| 195 | } |
| 196 | |
| 185 | 197 | pub const Error = error{}; |
| 186 | 198 | pub const Writer = std.io.Writer(*Self, Error, write); |
| 187 | 199 | |
| ... | ... | @@ -196,6 +208,338 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: |
| 196 | 208 | }; |
| 197 | 209 | } |
| 198 | 210 | |
| 211 | /// The cSHAKE extendable output hash function. |
| 212 | /// cSHAKE is similar to SHAKE, but in addition to the input message, it also takes an optional context (aka customization string). |
| 213 | pub fn CShake(comptime security_level: u11, comptime fname: ?[]const u8) type { |
| 214 | return CShakeLike(security_level, 0x04, 24, fname); |
| 215 | } |
| 216 | |
| 217 | fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5, comptime fname: ?[]const u8) type { |
| 218 | return struct { |
| 219 | const Shaker = ShakeLike(security_level, default_delim, rounds); |
| 220 | shaker: Shaker, |
| 221 | |
| 222 | /// The recommended output length, in bytes. |
| 223 | pub const digest_length = Shaker.digest_length; |
| 224 | /// The block length, or rate, in bytes. |
| 225 | pub const block_length = Shaker.block_length; |
| 226 | |
| 227 | /// cSHAKE options can include a context string. |
| 228 | pub const Options = struct { context: ?[]const u8 = null }; |
| 229 | |
| 230 | const Self = @This(); |
| 231 | |
| 232 | /// Initialize a SHAKE extensible hash function. |
| 233 | pub fn init(options: Options) Self { |
| 234 | if (fname == null and options.context == null) { |
| 235 | return Self{ .shaker = Shaker.init(.{ .delim = 0x1f }) }; |
| 236 | } |
| 237 | var shaker = Shaker.init(.{}); |
| 238 | comptime assert(Shaker.block_length % 8 == 0); |
| 239 | const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); |
| 240 | shaker.update(encoded_rate_len.slice()); |
| 241 | const encoded_zero = comptime NistLengthEncoding.encode(.left, 0); |
| 242 | if (fname) |name| { |
| 243 | const encoded_fname_len = comptime NistLengthEncoding.encode(.left, name.len); |
| 244 | const encoded_fname = comptime encoded_fname_len.slice() ++ name; |
| 245 | shaker.update(encoded_fname); |
| 246 | } else { |
| 247 | shaker.update(encoded_zero.slice()); |
| 248 | } |
| 249 | if (options.context) |context| { |
| 250 | const encoded_context_len = NistLengthEncoding.encode(.left, context.len); |
| 251 | shaker.update(encoded_context_len.slice()); |
| 252 | shaker.update(context); |
| 253 | } else { |
| 254 | shaker.update(encoded_zero.slice()); |
| 255 | } |
| 256 | shaker.st.fillBlock(); |
| 257 | return Self{ .shaker = shaker }; |
| 258 | } |
| 259 | |
| 260 | /// Hash a slice of bytes. |
| 261 | /// `out` can be any length. |
| 262 | pub fn hash(bytes: []const u8, out: []u8, options: Options) void { |
| 263 | var st = Self.init(options); |
| 264 | st.update(bytes); |
| 265 | st.squeeze(out); |
| 266 | } |
| 267 | |
| 268 | /// Absorb a slice of bytes into the state. |
| 269 | pub fn update(self: *Self, bytes: []const u8) void { |
| 270 | self.shaker.update(bytes); |
| 271 | } |
| 272 | |
| 273 | /// Squeeze a slice of bytes from the state. |
| 274 | /// `out` can be any length, and the function can be called multiple times. |
| 275 | pub fn squeeze(self: *Self, out: []u8) void { |
| 276 | self.shaker.squeeze(out); |
| 277 | } |
| 278 | |
| 279 | /// Return the hash of the absorbed bytes. |
| 280 | /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead). |
| 281 | pub fn final(self: *Self, out: []u8) void { |
| 282 | self.shaker.final(out); |
| 283 | } |
| 284 | |
| 285 | /// Align the input to a block boundary. |
| 286 | pub fn fillBlock(self: *Self) void { |
| 287 | self.shaker.fillBlock(); |
| 288 | } |
| 289 | |
| 290 | pub const Error = error{}; |
| 291 | pub const Writer = std.io.Writer(*Self, Error, write); |
| 292 | |
| 293 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 294 | self.update(bytes); |
| 295 | return bytes.len; |
| 296 | } |
| 297 | |
| 298 | pub fn writer(self: *Self) Writer { |
| 299 | return .{ .context = self }; |
| 300 | } |
| 301 | }; |
| 302 | } |
| 303 | |
| 304 | /// The KMAC extendable output authentication function. |
| 305 | /// KMAC is a keyed version of the cSHAKE function, with an optional context. |
| 306 | /// It can be used as an SHA-3 based alternative to HMAC, as well as a generic keyed XoF (extendable output function). |
| 307 | pub fn KMac(comptime security_level: u11) type { |
| 308 | return KMacLike(security_level, 0x04, 24); |
| 309 | } |
| 310 | |
| 311 | fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 312 | const CShaker = CShakeLike(security_level, default_delim, rounds, "KMAC"); |
| 313 | |
| 314 | return struct { |
| 315 | const Self = @This(); |
| 316 | |
| 317 | /// The recommended output length, in bytes. |
| 318 | pub const mac_length = CShaker.digest_length; |
| 319 | /// The minimum output length, in bytes. |
| 320 | pub const mac_length_min = 4; |
| 321 | /// The recommended key length, in bytes. |
| 322 | pub const key_length = security_level / 8; |
| 323 | /// The minimum key length, in bytes. |
| 324 | pub const key_length_min = 0; |
| 325 | /// The block length, or rate, in bytes. |
| 326 | pub const block_length = CShaker.block_length; |
| 327 | |
| 328 | cshaker: CShaker, |
| 329 | xof_mode: bool = false, |
| 330 | |
| 331 | /// KMAC options can include a context string. |
| 332 | pub const Options = struct { |
| 333 | context: ?[]const u8 = null, |
| 334 | }; |
| 335 | |
| 336 | /// Initialize a state for the KMAC function, with an optional context and an arbitrary-long key. |
| 337 | /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. |
| 338 | /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. |
| 339 | pub fn initWithOptions(key: []const u8, options: Options) Self { |
| 340 | var cshaker = CShaker.init(.{ .context = options.context }); |
| 341 | const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); |
| 342 | cshaker.update(encoded_rate_len.slice()); |
| 343 | const encoded_key_len = NistLengthEncoding.encode(.left, key.len); |
| 344 | cshaker.update(encoded_key_len.slice()); |
| 345 | cshaker.update(key); |
| 346 | cshaker.fillBlock(); |
| 347 | return Self{ |
| 348 | .cshaker = cshaker, |
| 349 | }; |
| 350 | } |
| 351 | |
| 352 | /// Initialize a state for the KMAC function. |
| 353 | /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. |
| 354 | /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. |
| 355 | pub fn init(key: []const u8) Self { |
| 356 | return initWithOptions(key, .{}); |
| 357 | } |
| 358 | |
| 359 | /// Add data to the state. |
| 360 | pub fn update(self: *Self, b: []const u8) void { |
| 361 | self.cshaker.update(b); |
| 362 | } |
| 363 | |
| 364 | /// Return an authentication tag for the current state. |
| 365 | pub fn final(self: *Self, out: []u8) void { |
| 366 | const encoded_out_len = NistLengthEncoding.encode(.right, out.len); |
| 367 | self.update(encoded_out_len.slice()); |
| 368 | self.cshaker.final(out); |
| 369 | } |
| 370 | |
| 371 | /// Squeeze a slice of bytes from the state. |
| 372 | /// `out` can be any length, and the function can be called multiple times. |
| 373 | pub fn squeeze(self: *Self, out: []u8) void { |
| 374 | if (!self.xof_mode) { |
| 375 | const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); |
| 376 | self.update(encoded_out_len.slice()); |
| 377 | self.xof_mode = true; |
| 378 | } |
| 379 | self.cshaker.squeeze(out); |
| 380 | } |
| 381 | |
| 382 | /// Return an authentication tag for a message and a key, with an optional context. |
| 383 | pub fn createWithOptions(out: []u8, msg: []const u8, key: []const u8, options: Options) void { |
| 384 | var ctx = Self.initWithOptions(key, options); |
| 385 | ctx.update(msg); |
| 386 | ctx.final(out); |
| 387 | } |
| 388 | |
| 389 | /// Return an authentication tag for a message and a key. |
| 390 | pub fn create(out: []u8, msg: []const u8, key: []const u8) void { |
| 391 | var ctx = Self.init(key); |
| 392 | ctx.update(msg); |
| 393 | ctx.final(out); |
| 394 | } |
| 395 | |
| 396 | pub const Error = error{}; |
| 397 | pub const Writer = std.io.Writer(*Self, Error, write); |
| 398 | |
| 399 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 400 | self.update(bytes); |
| 401 | return bytes.len; |
| 402 | } |
| 403 | |
| 404 | pub fn writer(self: *Self) Writer { |
| 405 | return .{ .context = self }; |
| 406 | } |
| 407 | }; |
| 408 | } |
| 409 | |
| 410 | /// The TupleHash extendable output hash function, with domain-separated inputs. |
| 411 | /// TupleHash is a secure hash function with a variable output length, based on the cSHAKE function. |
| 412 | /// It is designed for unambiguously hashing tuples of data. |
| 413 | /// |
| 414 | /// With most hash functions, calling `update("A")` followed by `update("B")`is identical to `update("AB")`. |
| 415 | /// With TupleHash, this is not the case: `update("A"); update("B")` is different from `update("AB")`. |
| 416 | /// |
| 417 | /// Any number of inputs can be hashed, and the output depends on individual inputs and their order. |
| 418 | pub fn TupleHash(comptime security_level: u11) type { |
| 419 | return TupleHashLike(security_level, 0x04, 24); |
| 420 | } |
| 421 | |
| 422 | fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { |
| 423 | const CShaker = CShakeLike(security_level, default_delim, rounds, "TupleHash"); |
| 424 | |
| 425 | return struct { |
| 426 | const Self = @This(); |
| 427 | |
| 428 | /// The output length, in bytes. |
| 429 | pub const digest_length = CShaker.digest_length; |
| 430 | /// The block length, or rate, in bytes. |
| 431 | pub const block_length = CShaker.block_length; |
| 432 | |
| 433 | cshaker: CShaker, |
| 434 | xof_mode: bool = false, |
| 435 | |
| 436 | /// TupleHash options can include a context string. |
| 437 | pub const Options = struct { |
| 438 | context: ?[]const u8 = null, |
| 439 | }; |
| 440 | |
| 441 | /// Initialize a state for the TupleHash function, with an optional context. |
| 442 | /// If the context is going to be reused, the structure can be initialized once, and cloned for each message. |
| 443 | /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. |
| 444 | /// |
| 445 | /// A key can be optionally added to the context to create a keyed TupleHash function, similar to KMAC. |
| 446 | pub fn initWithOptions(options: Options) Self { |
| 447 | const cshaker = CShaker.init(.{ .context = options.context }); |
| 448 | return Self{ |
| 449 | .cshaker = cshaker, |
| 450 | }; |
| 451 | } |
| 452 | |
| 453 | /// Initialize a state for the MAC function. |
| 454 | pub fn init() Self { |
| 455 | return initWithOptions(.{}); |
| 456 | } |
| 457 | |
| 458 | /// Add data to the state, separated from previous updates. |
| 459 | pub fn update(self: *Self, b: []const u8) void { |
| 460 | const encoded_b_len = NistLengthEncoding.encode(.left, b.len); |
| 461 | self.cshaker.update(encoded_b_len.slice()); |
| 462 | self.cshaker.update(b); |
| 463 | } |
| 464 | |
| 465 | /// Return an authentication tag for the current state. |
| 466 | pub fn final(self: *Self, out: []u8) void { |
| 467 | const encoded_out_len = NistLengthEncoding.encode(.right, out.len); |
| 468 | self.cshaker.update(encoded_out_len.slice()); |
| 469 | self.cshaker.final(out); |
| 470 | } |
| 471 | |
| 472 | /// Align the input to a block boundary. |
| 473 | pub fn fillBlock(self: *Self) void { |
| 474 | self.cshaker.fillBlock(); |
| 475 | } |
| 476 | |
| 477 | /// Squeeze a slice of bytes from the state. |
| 478 | /// `out` can be any length, and the function can be called multiple times. |
| 479 | pub fn squeeze(self: *Self, out: []u8) void { |
| 480 | if (!self.xof_mode) { |
| 481 | const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); |
| 482 | self.update(encoded_out_len.slice()); |
| 483 | self.xof_mode = true; |
| 484 | } |
| 485 | self.cshaker.squeeze(out); |
| 486 | } |
| 487 | |
| 488 | pub const Error = error{}; |
| 489 | pub const Writer = std.io.Writer(*Self, Error, write); |
| 490 | |
| 491 | fn write(self: *Self, bytes: []const u8) Error!usize { |
| 492 | self.update(bytes); |
| 493 | return bytes.len; |
| 494 | } |
| 495 | |
| 496 | pub fn writer(self: *Self) Writer { |
| 497 | return .{ .context = self }; |
| 498 | } |
| 499 | }; |
| 500 | } |
| 501 | |
| 502 | /// The NIST SP 800-185 encoded length format. |
| 503 | pub const NistLengthEncoding = enum { |
| 504 | left, |
| 505 | right, |
| 506 | |
| 507 | /// A length encoded according to NIST SP 800-185. |
| 508 | pub const Length = struct { |
| 509 | /// The size of the encoded value, in bytes. |
| 510 | len: usize = 0, |
| 511 | /// A buffer to store the encoded length. |
| 512 | buf: [@sizeOf(usize) + 1]u8 = undefined, |
| 513 | |
| 514 | /// Return the encoded length as a slice. |
| 515 | pub fn slice(self: *const Length) []const u8 { |
| 516 | return self.buf[0..self.len]; |
| 517 | } |
| 518 | }; |
| 519 | |
| 520 | /// Encode a length according to NIST SP 800-185. |
| 521 | pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length { |
| 522 | const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3; |
| 523 | const len_bytes = std.math.divCeil(usize, len_bits, 8) catch unreachable; |
| 524 | |
| 525 | var res = Length{ .len = len_bytes + 1 }; |
| 526 | if (encoding == .right) { |
| 527 | res.buf[len_bytes] = @intCast(len_bytes); |
| 528 | } |
| 529 | const end = if (encoding == .right) len_bytes - 1 else len_bytes; |
| 530 | res.buf[end] = @truncate(len << 3); |
| 531 | var len_ = len >> 5; |
| 532 | for (1..len_bytes) |i| { |
| 533 | res.buf[end - i] = @truncate(len_); |
| 534 | len_ >>= 8; |
| 535 | } |
| 536 | if (encoding == .left) { |
| 537 | res.buf[0] = @intCast(len_bytes); |
| 538 | } |
| 539 | return res; |
| 540 | } |
| 541 | }; |
| 542 | |
| 199 | 543 | const htest = @import("test.zig"); |
| 200 | 544 | |
| 201 | 545 | test "sha3-224 single" { |
| ... | ... | @@ -397,3 +741,88 @@ test "SHA-3 with streaming" { |
| 397 | 741 | h.final(&out); |
| 398 | 742 | try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); |
| 399 | 743 | } |
| 744 | |
| 745 | test "cSHAKE-128 with no context nor function name" { |
| 746 | var out: [32]u8 = undefined; |
| 747 | CShake128.hash("hello123", &out, .{}); |
| 748 | try htest.assertEqual("1b85861510bc4d8e467d6f8a92270533cbaa7ba5e06c2d2a502854bac468b8b9", &out); |
| 749 | } |
| 750 | |
| 751 | test "cSHAKE-128 with context" { |
| 752 | var out: [32]u8 = undefined; |
| 753 | CShake128.hash("hello123", &out, .{ .context = "custom" }); |
| 754 | try htest.assertEqual("7509fa13a6bd3e38ad5c6fac042142c233996e40ebffc86c276f108b3b19cc6a", &out); |
| 755 | } |
| 756 | |
| 757 | test "cSHAKE-128 with context and function" { |
| 758 | var out: [32]u8 = undefined; |
| 759 | CShake(128, "function").hash("hello123", &out, .{ .context = "custom" }); |
| 760 | try htest.assertEqual("ad7f4d7db2d96587fcd5047c65d37c368f5366e3afac60bb9b66b0bb95dfb675", &out); |
| 761 | } |
| 762 | |
| 763 | test "cSHAKE-256" { |
| 764 | var out: [32]u8 = undefined; |
| 765 | CShake256.hash("hello123", &out, .{ .context = "custom" }); |
| 766 | try htest.assertEqual("dabe027eb1a6cbe3a0542d0560eb4e6b39146dd72ae1bf89c970a61bd93b1813", &out); |
| 767 | } |
| 768 | |
| 769 | test "KMAC-128 with empty key and message" { |
| 770 | var out: [KMac128.mac_length]u8 = undefined; |
| 771 | const key = ""; |
| 772 | KMac128.create(&out, "", key); |
| 773 | try htest.assertEqual("5c135c615152fb4d9784dd1155f9b6034e013fd77165c327dfa4d36701983ef7", &out); |
| 774 | } |
| 775 | |
| 776 | test "KMAC-128" { |
| 777 | var out: [KMac128.mac_length]u8 = undefined; |
| 778 | const key = "A KMAC secret key"; |
| 779 | KMac128.create(&out, "hello123", key); |
| 780 | try htest.assertEqual("1fa1c0d761129a83f9a4299ca137674de8373a3cc437799ae4c129e651627f8e", &out); |
| 781 | } |
| 782 | |
| 783 | test "KMAC-128 with a customization string" { |
| 784 | var out: [KMac128.mac_length]u8 = undefined; |
| 785 | const key = "A KMAC secret key"; |
| 786 | KMac128.createWithOptions(&out, "hello123", key, .{ .context = "custom" }); |
| 787 | try htest.assertEqual("c58c6d42dc00a27dfa8e7e08f8c9307cecb5d662ddb11b6c36057fc2e0e068ba", &out); |
| 788 | } |
| 789 | |
| 790 | test "KMACXOF-128" { |
| 791 | const key = "A KMAC secret key"; |
| 792 | var xof = KMac128.init(key); |
| 793 | xof.update("hello123"); |
| 794 | var out: [50]u8 = undefined; |
| 795 | xof.squeeze(&out); |
| 796 | try htest.assertEqual("628c2fb870d294b3673ac82d9f0d651aae6a5bb8084ea8cd8343cb888d075b9053173200a71f301141069c3c0322527981f7", &out); |
| 797 | xof.squeeze(&out); |
| 798 | try htest.assertEqual("7b638e178cfdac5727a4ea7694efaa967a65a1d0034501855acff506b4158d187d5a18d668e67b43f2abf61144b20ed4c09f", &out); |
| 799 | } |
| 800 | |
| 801 | test "KMACXOF-256" { |
| 802 | const key = "A KMAC secret key"; |
| 803 | var xof = KMac256.init(key); |
| 804 | xof.update("hello123"); |
| 805 | var out: [50]u8 = undefined; |
| 806 | xof.squeeze(&out); |
| 807 | try htest.assertEqual("23fc644bc2655ba6fde7b7c11f2804f22e8d8c6bd7db856268bf3370ce2362703f6c7e91916a1b8c116e60edfbcb25613054", &out); |
| 808 | xof.squeeze(&out); |
| 809 | try htest.assertEqual("ff97251020ff255ee65a1c1f5f78ebe904f61211c39f973f82fbce2b196b9f51c2cb12afe51549a0f1eaf7954e657ba11af3", &out); |
| 810 | } |
| 811 | |
| 812 | test "TupleHash-128" { |
| 813 | var st = TupleHash128.init(); |
| 814 | st.update("hello"); |
| 815 | st.update("123"); |
| 816 | var out: [32]u8 = undefined; |
| 817 | st.final(&out); |
| 818 | try htest.assertEqual("3938d49ade8ec0f0c305ac63497b2d2e8b2f650714f9667cc41816b1c11ffd20", &out); |
| 819 | } |
| 820 | |
| 821 | test "TupleHash-256" { |
| 822 | var st = TupleHash256.init(); |
| 823 | st.update("hello"); |
| 824 | st.update("123"); |
| 825 | var out: [64]u8 = undefined; |
| 826 | st.final(&out); |
| 827 | try htest.assertEqual("2dca563c2882f2ba4f46a441a4c5e13fb97150d1436fe99c7e4e43a2d20d0f1cd3d38483bde4a966930606dfa6c61c4ca6400aeedfb474d1bf0d7f6a70968289", &out); |
| 828 | } |