| ... | @@ -18,11 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); | ... | @@ -18,11 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); |
| 18 | pub const Shake128 = Shake(128); | 18 | pub const Shake128 = Shake(128); |
| 19 | pub const Shake256 = Shake(256); | 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 | /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. | 30 | /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. |
| 22 | /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. | 31 | /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. |
| 23 | /// The delimiter is 0x1f by default, but can be changed for context-separation. | 32 | /// The delimiter is 0x1f by default, but can be changed for context-separation. |
| 24 | /// For a protocol that uses both KangarooTwelve and TurboSHAKE128, it is recommended to avoid using 0x06, 0x07 or 0x0b for the delimiter. | 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 | return TurboShake(128, delim); | 35 | return TurboShake(128, delim); |
| 27 | } | 36 | } |
| 28 | | 37 | |
| ... | @@ -34,27 +43,26 @@ pub fn TurboShake256(comptime delim: ?u7) type { | ... | @@ -34,27 +43,26 @@ pub fn TurboShake256(comptime delim: ?u7) type { |
| 34 | } | 43 | } |
| 35 | | 44 | |
| 36 | /// A generic Keccak hash function. | 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 | comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length | 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 | return struct { | 51 | return struct { |
| 43 | const Self = @This(); | 52 | const Self = @This(); |
| 44 | | 53 | |
| 45 | st: State = .{}, | 54 | st: State, |
| 46 | | 55 | |
| 47 | /// The output length, in bytes. | 56 | /// The output length, in bytes. |
| 48 | pub const digest_length = output_bits / 8; | 57 | pub const digest_length = output_bits / 8; |
| 49 | /// The block length, or rate, in bytes. | 58 | /// The block length, or rate, in bytes. |
| 50 | pub const block_length = State.rate; | 59 | pub const block_length = State.rate; |
| 51 | /// Keccak does not have any options. | 60 | /// The delimiter can be overwritten in the options. |
| 52 | pub const Options = struct {}; | 61 | pub const Options = struct { delim: u8 = default_delim }; |
| 53 | | 62 | |
| 54 | /// Initialize a Keccak hash function. | 63 | /// Initialize a Keccak hash function. |
| 55 | pub fn init(options: Options) Self { | 64 | pub fn init(options: Options) Self { |
| 56 | _ = options; | 65 | return Self{ .st = .{ .delim = options.delim } }; |
| 57 | return Self{}; | | |
| 58 | } | 66 | } |
| 59 | | 67 | |
| 60 | /// Hash a slice of bytes. | 68 | /// Hash a slice of bytes. |
| ... | @@ -105,29 +113,28 @@ pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type { | ... | @@ -105,29 +113,28 @@ pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type { |
| 105 | return ShakeLike(security_level, d, 12); | 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 | const f = 1600; | 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 | return struct { | 120 | return struct { |
| 113 | const Self = @This(); | 121 | const Self = @This(); |
| 114 | | 122 | |
| 115 | st: State = .{}, | 123 | st: State, |
| 116 | buf: [State.rate]u8 = undefined, | 124 | buf: [State.rate]u8 = undefined, |
| 117 | offset: usize = 0, | 125 | offset: usize = 0, |
| 118 | padded: bool = false, | 126 | padded: bool = false, |
| 119 | | 127 | |
| 120 | /// The recommended output length, in bytes. | 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 | /// The block length, or rate, in bytes. | 130 | /// The block length, or rate, in bytes. |
| 123 | pub const block_length = State.rate; | 131 | pub const block_length = State.rate; |
| 124 | /// Keccak does not have any options. | 132 | /// The delimiter can be overwritten in the options. |
| 125 | pub const Options = struct {}; | 133 | pub const Options = struct { delim: u8 = default_delim }; |
| 126 | | 134 | |
| 127 | /// Initialize a SHAKE extensible hash function. | 135 | /// Initialize a SHAKE extensible hash function. |
| 128 | pub fn init(options: Options) Self { | 136 | pub fn init(options: Options) Self { |
| 129 | _ = options; | 137 | return Self{ .st = .{ .delim = options.delim } }; |
| 130 | return Self{}; | | |
| 131 | } | 138 | } |
| 132 | | 139 | |
| 133 | /// Hash a slice of bytes. | 140 | /// Hash a slice of bytes. |
| ... | @@ -182,6 +189,11 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: | ... | @@ -182,6 +189,11 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: |
| 182 | self.st.st.clear(0, State.rate); | 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 | pub const Error = error{}; | 197 | pub const Error = error{}; |
| 186 | pub const Writer = std.io.Writer(*Self, Error, write); | 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,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 | const htest = @import("test.zig"); | 543 | const htest = @import("test.zig"); |
| 200 | | 544 | |
| 201 | test "sha3-224 single" { | 545 | test "sha3-224 single" { |
| ... | @@ -397,3 +741,88 @@ test "SHA-3 with streaming" { | ... | @@ -397,3 +741,88 @@ test "SHA-3 with streaming" { |
| 397 | h.final(&out); | 741 | h.final(&out); |
| 398 | try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); | 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 | } |