Generalize SHA, implement SHA-2.
This commit is contained in:
@ -23,7 +23,7 @@ Most (**theoretical!**) users should directly use one of the cryptographic *prot
|
|||||||
- Advanced Encryption Standard (FIPS 197): AES-128, AES-192, AES-256
|
- Advanced Encryption Standard (FIPS 197): AES-128, AES-192, AES-256
|
||||||
- ChaCha20 (RFC 7539): ChaCha20 with 64-bit nonce and 64-bit counter, ChaCha20 with 96-bit nonce and 32-bit counter
|
- ChaCha20 (RFC 7539): ChaCha20 with 64-bit nonce and 64-bit counter, ChaCha20 with 96-bit nonce and 32-bit counter
|
||||||
- Salsa20: Salsa20/20 with 256-key, Salsa20/20 with 128-bit key
|
- Salsa20: Salsa20/20 with 256-key, Salsa20/20 with 128-bit key
|
||||||
- Secure Hashing Algorithm: SHA-1
|
- Secure Hashing Algorithm: SHA-1, SHA-224, SHA-256
|
||||||
|
|
||||||
### Protocols
|
### Protocols
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ Most (**theoretical!**) users should directly use one of the cryptographic *prot
|
|||||||
- DES, 3DES
|
- DES, 3DES
|
||||||
- Block cipher modes: CBC-PKCS7, CFB, OFB, CTR, GCM
|
- Block cipher modes: CBC-PKCS7, CFB, OFB, CTR, GCM
|
||||||
- Poly1305
|
- Poly1305
|
||||||
- SHA-2, SHA-3
|
- SHA-3, HMAC
|
||||||
- BigIntegers & modular arithmetic
|
- BigIntegers & modular arithmetic
|
||||||
- Cryptographically secure random BigInteger generation & primality testing
|
- Cryptographically secure random BigInteger generation & primality testing
|
||||||
- Elliptic Curve groups (over Fp fields)
|
- Elliptic Curve groups (over Fp fields)
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const dbg = builtin.mode == builtin.Mode.Debug;
|
||||||
|
|
||||||
// ----------------------------------- ERROR DEFINITIONS ----------------------------------- //
|
// ----------------------------------- ERROR DEFINITIONS ----------------------------------- //
|
||||||
|
|
||||||
pub const MessageLengthLimitExceeded = error.MessageLengthLimitExceeded;
|
pub const MessageLengthLimitExceeded = error.MessageLengthLimitExceeded;
|
||||||
|
|
||||||
// ----------------------------------- SHA CONSTANTS ----------------------------------- //
|
// ----------------------------------- SHA CONSTANTS ----------------------------------- //
|
||||||
|
|
||||||
//pub const ShaAlgorithm = enum { Sha1, Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256 };
|
|
||||||
|
|
||||||
const SHA_1_DIGEST_LENGTH = 160 / 8;
|
const SHA_1_DIGEST_LENGTH = 160 / 8;
|
||||||
const SHA_224_DIGEST_LENGTH = 224 / 8;
|
const SHA_224_DIGEST_LENGTH = 224 / 8;
|
||||||
const SHA_256_DIGEST_LENGTH = 256 / 8;
|
const SHA_256_DIGEST_LENGTH = 256 / 8;
|
||||||
@ -43,23 +44,22 @@ const SHA_512_256_IV = [_]u64{
|
|||||||
0x96283EE2A88EFFE3, 0xBE5E1E2553863992, 0x2B0199FC2C85B8AA, 0x0EB72DDC81C52CA2,
|
0x96283EE2A88EFFE3, 0xBE5E1E2553863992, 0x2B0199FC2C85B8AA, 0x0EB72DDC81C52CA2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------- SHA ALGORITHMS ----------------------------------- //
|
// ----------------------------------- SHA ALGORITHMS ----------------------------------- //
|
||||||
|
|
||||||
const Sha1Ctx = struct {
|
const Sha1Ctx = struct {
|
||||||
const BLOCK_SIZE = 512 / 8;
|
const BLOCK_SIZE = 512 / 8;
|
||||||
const MESSAGE_SCHEDULE_WORDS = 80;
|
const MESSAGE_SCHEDULE_WORDS = 80;
|
||||||
|
|
||||||
message_schedule: [MESSAGE_SCHEDULE_WORDS]u32,
|
hash: [SHA_1_DIGEST_LENGTH / @sizeOf(u32)]u32,
|
||||||
hash: [SHA_1_DIGEST_LENGTH / 4]u32,
|
|
||||||
message_buffer: [BLOCK_SIZE]u8,
|
message_buffer: [BLOCK_SIZE]u8,
|
||||||
message_length: u64,
|
message_length: u64,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Sha2Ctx = struct {
|
const Sha2Ctx = struct {
|
||||||
const BLOCK_SIZE = 512 / 8;
|
const BLOCK_SIZE = 512 / 8;
|
||||||
|
const MESSAGE_SCHEDULE_WORDS = 64;
|
||||||
|
|
||||||
message_schedule: [16]u32,
|
hash: [SHA_256_DIGEST_LENGTH / @sizeOf(u32)]u32,
|
||||||
hash: [8]u32,
|
|
||||||
message_buffer: [BLOCK_SIZE]u8,
|
message_buffer: [BLOCK_SIZE]u8,
|
||||||
message_length: u64,
|
message_length: u64,
|
||||||
|
|
||||||
@ -69,8 +69,7 @@ const Sha2Ctx = struct {
|
|||||||
const Sha3Ctx = struct {
|
const Sha3Ctx = struct {
|
||||||
const BLOCK_SIZE = 1024 / 8;
|
const BLOCK_SIZE = 1024 / 8;
|
||||||
|
|
||||||
message_schedule: [16]u64,
|
hash: [SHA_512_DIGEST_LENGTH / @sizeOf(u64)]u64,
|
||||||
hash: [8]u64,
|
|
||||||
message_buffer: [BLOCK_SIZE]u8,
|
message_buffer: [BLOCK_SIZE]u8,
|
||||||
message_length: u128,
|
message_length: u128,
|
||||||
|
|
||||||
@ -80,27 +79,21 @@ const Sha3Ctx = struct {
|
|||||||
t_is_384: bool,
|
t_is_384: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn sha1_new() Sha1Ctx {
|
pub fn sha_generic_update(
|
||||||
var ctx = Sha1Ctx{
|
ShaCtx: type,
|
||||||
.message_schedule = undefined,
|
limit_length_bits: comptime_int,
|
||||||
.hash = undefined,
|
compression_function: *const fn (*ShaCtx) void,
|
||||||
.message_buffer = undefined,
|
ctx: *ShaCtx,
|
||||||
.message_length = 0,
|
message: []const u8,
|
||||||
};
|
) !void {
|
||||||
@memcpy(&ctx.hash, &SHA_1_IV);
|
if (ctx.message_length + message.len >= limit_length_bits / 8)
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void {
|
|
||||||
// SHA-1 can digest a message of a maximum length of (2^64 - 1) bits due to the nature of its padding.
|
|
||||||
if (ctx.message_length + message.len >= ((1 << 64) / 8))
|
|
||||||
return MessageLengthLimitExceeded;
|
return MessageLengthLimitExceeded;
|
||||||
|
|
||||||
const cnt_buffered_bytes = ctx.message_length % Sha1Ctx.BLOCK_SIZE;
|
const cnt_buffered_bytes = ctx.message_length % ShaCtx.BLOCK_SIZE;
|
||||||
|
|
||||||
// Simplest case - the message did not fully fill the block size
|
// Simplest case - the message does not fully fill the block yet,
|
||||||
// so it's just copied to the context and no hashing is done yet.
|
// so it's just copied to the context buffer and no hashing is done.
|
||||||
if (cnt_buffered_bytes + message.len < Sha1Ctx.BLOCK_SIZE) {
|
if (cnt_buffered_bytes + message.len < Sha2Ctx.BLOCK_SIZE) {
|
||||||
@memcpy(
|
@memcpy(
|
||||||
ctx.message_buffer[cnt_buffered_bytes .. cnt_buffered_bytes + message.len],
|
ctx.message_buffer[cnt_buffered_bytes .. cnt_buffered_bytes + message.len],
|
||||||
message[0..],
|
message[0..],
|
||||||
@ -112,18 +105,21 @@ pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void {
|
|||||||
// Otherwise: first, copy & hash the first block.
|
// Otherwise: first, copy & hash the first block.
|
||||||
@memcpy(
|
@memcpy(
|
||||||
ctx.message_buffer[cnt_buffered_bytes..],
|
ctx.message_buffer[cnt_buffered_bytes..],
|
||||||
message[0 .. Sha1Ctx.BLOCK_SIZE - cnt_buffered_bytes],
|
message[0 .. ShaCtx.BLOCK_SIZE - cnt_buffered_bytes],
|
||||||
);
|
);
|
||||||
sha1_hash_one_block(ctx);
|
compression_function(ctx);
|
||||||
var cnt_message_bytes_processed = Sha1Ctx.BLOCK_SIZE - cnt_buffered_bytes;
|
var cnt_message_bytes_processed = ShaCtx.BLOCK_SIZE - cnt_buffered_bytes;
|
||||||
ctx.message_length += cnt_message_bytes_processed;
|
ctx.message_length += cnt_message_bytes_processed;
|
||||||
|
|
||||||
// Then, as long as there is at least another block available, copy and hash it.
|
// Then, as long as there is at least another block available, copy and hash it.
|
||||||
while (message.len - cnt_message_bytes_processed >= Sha1Ctx.BLOCK_SIZE) {
|
while (message.len - cnt_message_bytes_processed >= ShaCtx.BLOCK_SIZE) {
|
||||||
@memcpy(ctx.message_buffer[0..], message[cnt_message_bytes_processed .. cnt_message_bytes_processed + Sha1Ctx.BLOCK_SIZE]);
|
@memcpy(
|
||||||
sha1_hash_one_block(ctx);
|
ctx.message_buffer[0..],
|
||||||
ctx.message_length += Sha1Ctx.BLOCK_SIZE;
|
message[cnt_message_bytes_processed .. cnt_message_bytes_processed + ShaCtx.BLOCK_SIZE],
|
||||||
cnt_message_bytes_processed += Sha1Ctx.BLOCK_SIZE;
|
);
|
||||||
|
compression_function(ctx);
|
||||||
|
ctx.message_length += ShaCtx.BLOCK_SIZE;
|
||||||
|
cnt_message_bytes_processed += ShaCtx.BLOCK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, copy any leftover bytes to the context buffer without hashing.
|
// Finally, copy any leftover bytes to the context buffer without hashing.
|
||||||
@ -135,27 +131,33 @@ pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void {
|
|||||||
ctx.message_length += cnt_leftover_bytes;
|
ctx.message_length += cnt_leftover_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha1_final(ctx: *Sha1Ctx, out: *[SHA_1_DIGEST_LENGTH]u8) void {
|
pub fn sha_generic_final(
|
||||||
// The message length is stored in the padding as a 64-bit int.
|
ShaCtx: type,
|
||||||
const message_length_bytes = 64 / 8;
|
WordType: type,
|
||||||
|
MessageLengthIntType: type,
|
||||||
|
digest_length: comptime_int,
|
||||||
|
compression_function: *const fn (*ShaCtx) void,
|
||||||
|
ctx: *ShaCtx,
|
||||||
|
out: *[digest_length]u8,
|
||||||
|
) void {
|
||||||
|
const message_length_bytes = @typeInfo(MessageLengthIntType).Int.bits / 8;
|
||||||
|
const cnt_leftover_bytes = ctx.message_length % ShaCtx.BLOCK_SIZE;
|
||||||
|
|
||||||
const cnt_leftover_bytes = ctx.message_length % Sha1Ctx.BLOCK_SIZE;
|
// Simple case: The leftover message is short enough* for the padding to only span one block.
|
||||||
|
// *: "Short enough" depends on the particular SHA variant.
|
||||||
// Simpler case: The leftover message is shorter than 446 bits
|
if (cnt_leftover_bytes < ShaCtx.BLOCK_SIZE - message_length_bytes) {
|
||||||
// (or 55 bytes) and the padding only spans one block.
|
const cnt_padding_bytes = ShaCtx.BLOCK_SIZE - message_length_bytes - cnt_leftover_bytes;
|
||||||
if (cnt_leftover_bytes < Sha1Ctx.BLOCK_SIZE - (message_length_bytes)) {
|
|
||||||
const cnt_padding_bytes = Sha1Ctx.BLOCK_SIZE - message_length_bytes - cnt_leftover_bytes;
|
|
||||||
|
|
||||||
// The padding (without the message length) is a single 1 bit followed by 0 bits.
|
// The padding (without the message length) is a single 1 bit followed by 0 bits.
|
||||||
ctx.message_buffer[cnt_leftover_bytes] = 0x80;
|
ctx.message_buffer[cnt_leftover_bytes] = 0x80;
|
||||||
@memset(ctx.message_buffer[cnt_leftover_bytes + 1 .. cnt_leftover_bytes + cnt_padding_bytes], 0x00);
|
@memset(ctx.message_buffer[cnt_leftover_bytes + 1 .. cnt_leftover_bytes + cnt_padding_bytes], 0x00);
|
||||||
|
|
||||||
// The length is appended.
|
// The length is appended.
|
||||||
const length = serialize_int_big_endian(u64, ctx.message_length * 8);
|
const length = serialize_int_big_endian(MessageLengthIntType, ctx.message_length * 8);
|
||||||
@memcpy(ctx.message_buffer[cnt_leftover_bytes + cnt_padding_bytes ..], length[0..]);
|
@memcpy(ctx.message_buffer[cnt_leftover_bytes + cnt_padding_bytes ..], length[0..]);
|
||||||
|
|
||||||
// The padded block is finally hashed.
|
// The padded block is finally hashed.
|
||||||
sha1_hash_one_block(ctx);
|
compression_function(ctx);
|
||||||
}
|
}
|
||||||
// Otherwise, the padding spans 2 blocks in total
|
// Otherwise, the padding spans 2 blocks in total
|
||||||
// and two more hash iterations are performed.
|
// and two more hash iterations are performed.
|
||||||
@ -163,30 +165,67 @@ pub fn sha1_final(ctx: *Sha1Ctx, out: *[SHA_1_DIGEST_LENGTH]u8) void {
|
|||||||
// Pad and hash the first block.
|
// Pad and hash the first block.
|
||||||
ctx.message_buffer[cnt_leftover_bytes] = 0x80;
|
ctx.message_buffer[cnt_leftover_bytes] = 0x80;
|
||||||
@memset(ctx.message_buffer[cnt_leftover_bytes + 1 ..], 0x00);
|
@memset(ctx.message_buffer[cnt_leftover_bytes + 1 ..], 0x00);
|
||||||
sha1_hash_one_block(ctx);
|
compression_function(ctx);
|
||||||
|
|
||||||
// Hash the second block.
|
// Hash the second block.
|
||||||
@memset(ctx.message_buffer[0..(Sha1Ctx.BLOCK_SIZE - message_length_bytes)], 0x00);
|
@memset(ctx.message_buffer[0..(ShaCtx.BLOCK_SIZE - message_length_bytes)], 0x00);
|
||||||
const length = serialize_int_big_endian(u64, ctx.message_length * 8);
|
const length = serialize_int_big_endian(MessageLengthIntType, ctx.message_length * 8);
|
||||||
@memcpy(ctx.message_buffer[(Sha1Ctx.BLOCK_SIZE - message_length_bytes)..], length[0..]);
|
@memcpy(ctx.message_buffer[(ShaCtx.BLOCK_SIZE - message_length_bytes)..], length[0..]);
|
||||||
sha1_hash_one_block(ctx);
|
compression_function(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize the result.
|
// Serialize the result.
|
||||||
for (0..SHA_1_DIGEST_LENGTH / 4) |w| {
|
const word_size = @typeInfo(WordType).Int.bits / 8;
|
||||||
const serialized_word = serialize_int_big_endian(u32, ctx.hash[w]);
|
for (0..digest_length / word_size) |w| {
|
||||||
@memcpy(out[(w * 4)..(w * 4 + 4)], serialized_word[0..]);
|
const serialized_word = serialize_int_big_endian(WordType, ctx.hash[w]);
|
||||||
|
@memcpy(out[(w * word_size)..((w + 1) * word_size)], serialized_word[0..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha1_hash_one_block(ctx: *Sha1Ctx) void {
|
// ----------------------------------- SHA-1 ----------------------------------- //
|
||||||
|
|
||||||
|
pub fn sha1_new() Sha1Ctx {
|
||||||
|
var ctx = Sha1Ctx{
|
||||||
|
.hash = undefined,
|
||||||
|
.message_buffer = undefined,
|
||||||
|
.message_length = 0,
|
||||||
|
};
|
||||||
|
@memcpy(&ctx.hash, &SHA_1_IV);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sha1_update(ctx: *Sha1Ctx, message: []const u8) !void {
|
||||||
|
return sha_generic_update(
|
||||||
|
Sha1Ctx,
|
||||||
|
1 << 64,
|
||||||
|
&sha1_compress_block,
|
||||||
|
ctx,
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sha1_final(ctx: *Sha1Ctx, out: *[SHA_1_DIGEST_LENGTH]u8) void {
|
||||||
|
return sha_generic_final(
|
||||||
|
Sha1Ctx,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
SHA_1_DIGEST_LENGTH,
|
||||||
|
sha1_compress_block,
|
||||||
|
ctx,
|
||||||
|
out,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sha1_compress_block(ctx: *Sha1Ctx) void {
|
||||||
// Prepare the message schedule.
|
// Prepare the message schedule.
|
||||||
|
var message_schedule: [Sha1Ctx.MESSAGE_SCHEDULE_WORDS]u32 = undefined;
|
||||||
|
|
||||||
for (0..Sha1Ctx.BLOCK_SIZE / 4) |t|
|
for (0..Sha1Ctx.BLOCK_SIZE / 4) |t|
|
||||||
ctx.message_schedule[t] = deserialize_int_big_endian(u32, @ptrCast(ctx.message_buffer[(t * 4)..(t * 4 + 4)]));
|
message_schedule[t] = deserialize_int_big_endian(u32, @ptrCast(ctx.message_buffer[(t * 4)..(t * 4 + 4)]));
|
||||||
for (Sha1Ctx.BLOCK_SIZE / 4..Sha1Ctx.MESSAGE_SCHEDULE_WORDS) |t| {
|
for (Sha1Ctx.BLOCK_SIZE / 4..Sha1Ctx.MESSAGE_SCHEDULE_WORDS) |t| {
|
||||||
ctx.message_schedule[t] = rotl(
|
message_schedule[t] = rotl(
|
||||||
u32,
|
u32,
|
||||||
ctx.message_schedule[t - 3] ^ ctx.message_schedule[t - 8] ^ ctx.message_schedule[t - 14] ^ ctx.message_schedule[t - 16],
|
message_schedule[t - 3] ^ message_schedule[t - 8] ^ message_schedule[t - 14] ^ message_schedule[t - 16],
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -200,7 +239,7 @@ pub fn sha1_hash_one_block(ctx: *Sha1Ctx) void {
|
|||||||
|
|
||||||
// Perform the actual hashing.
|
// Perform the actual hashing.
|
||||||
inline for (0..Sha1Ctx.MESSAGE_SCHEDULE_WORDS) |t| {
|
inline for (0..Sha1Ctx.MESSAGE_SCHEDULE_WORDS) |t| {
|
||||||
const tmp = rotl(u32, a, 5) +% sha1_f(t, b, c, d) +% e +% sha1_k(t) +% ctx.message_schedule[t];
|
const tmp = rotl(u32, a, 5) +% sha1_f(t, b, c, d) +% e +% sha1_k(t) +% message_schedule[t];
|
||||||
e = d;
|
e = d;
|
||||||
d = c;
|
d = c;
|
||||||
c = rotl(u32, b, 30);
|
c = rotl(u32, b, 30);
|
||||||
@ -236,21 +275,14 @@ inline fn sha1_k(t: comptime_int) u32 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- SHA-2 ----------------------------------- //
|
||||||
|
|
||||||
pub fn sha2_new(t: comptime_int) Sha2Ctx {
|
pub fn sha2_new(t: comptime_int) Sha2Ctx {
|
||||||
if (comptime t != 224 and t != 256)
|
if (comptime t != 224 and t != 256)
|
||||||
@compileError("SHA-2 context can only be initialized in 224-bit and 256-bit mode.");
|
@compileError("SHA-2 context can only be initialized in 224-bit and 256-bit mode.");
|
||||||
|
|
||||||
var ctx = Sha2Ctx{
|
var ctx = Sha2Ctx{
|
||||||
.message_schedule = undefined,
|
|
||||||
.hash = undefined,
|
.hash = undefined,
|
||||||
.a = if (t == 224) SHA_224_IV[0] else SHA_256_IV[0],
|
|
||||||
.b = if (t == 224) SHA_224_IV[1] else SHA_256_IV[1],
|
|
||||||
.c = if (t == 224) SHA_224_IV[2] else SHA_256_IV[2],
|
|
||||||
.d = if (t == 224) SHA_224_IV[3] else SHA_256_IV[3],
|
|
||||||
.e = if (t == 224) SHA_224_IV[4] else SHA_256_IV[4],
|
|
||||||
.f = if (t == 224) SHA_224_IV[5] else SHA_256_IV[5],
|
|
||||||
.g = if (t == 224) SHA_224_IV[6] else SHA_256_IV[6],
|
|
||||||
.h = if (t == 224) SHA_224_IV[7] else SHA_256_IV[7],
|
|
||||||
.message_buffer = undefined,
|
.message_buffer = undefined,
|
||||||
.message_length = 0,
|
.message_length = 0,
|
||||||
.t_is_224 = (t == 224),
|
.t_is_224 = (t == 224),
|
||||||
@ -260,18 +292,112 @@ pub fn sha2_new(t: comptime_int) Sha2Ctx {
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sha2_update(ctx: *Sha2Ctx, message: []const u8) !void {
|
||||||
|
// All variants of SHA-2 can digest a message of a maximum length of (2^64 - 1) bits
|
||||||
|
// due to the nature of its padding (which is identical to SHA-1).
|
||||||
|
return sha_generic_update(
|
||||||
|
Sha2Ctx,
|
||||||
|
1 << 64,
|
||||||
|
&sha2_compress_block,
|
||||||
|
ctx,
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha2_compress_block(ctx: *Sha2Ctx) void {
|
||||||
|
// Prepare the message schedule.
|
||||||
|
var message_schedule: [Sha2Ctx.MESSAGE_SCHEDULE_WORDS]u32 = undefined;
|
||||||
|
|
||||||
|
for (0..Sha2Ctx.BLOCK_SIZE / 4) |t|
|
||||||
|
message_schedule[t] = deserialize_int_big_endian(u32, @ptrCast(ctx.message_buffer[(t * 4)..(t * 4 + 4)]));
|
||||||
|
for (Sha2Ctx.BLOCK_SIZE / 4..Sha2Ctx.MESSAGE_SCHEDULE_WORDS) |t| {
|
||||||
|
message_schedule[t] = sha2_sigma_256_1(message_schedule[t - 2]) +% message_schedule[t - 7] +% sha2_sigma_256_0(message_schedule[t - 15]) +% message_schedule[t - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize working variables.
|
||||||
|
var a = ctx.hash[0];
|
||||||
|
var b = ctx.hash[1];
|
||||||
|
var c = ctx.hash[2];
|
||||||
|
var d = ctx.hash[3];
|
||||||
|
var e = ctx.hash[4];
|
||||||
|
var f = ctx.hash[5];
|
||||||
|
var g = ctx.hash[6];
|
||||||
|
var h = ctx.hash[7];
|
||||||
|
|
||||||
|
// Perform the actual hashing.
|
||||||
|
inline for (0..Sha2Ctx.MESSAGE_SCHEDULE_WORDS) |t| {
|
||||||
|
const tmp1 = h +% sha2_Sigma_256_1(e) +% ch(u32, e, f, g) +% sha2_k_256[t] +% message_schedule[t];
|
||||||
|
const tmp2 = sha2_Sigma_256_0(a) +% maj(u32, a, b, c);
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = d +% tmp1;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = tmp1 +% tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the result to the previous hash state.
|
||||||
|
ctx.hash[0] +%= a;
|
||||||
|
ctx.hash[1] +%= b;
|
||||||
|
ctx.hash[2] +%= c;
|
||||||
|
ctx.hash[3] +%= d;
|
||||||
|
ctx.hash[4] +%= e;
|
||||||
|
ctx.hash[5] +%= f;
|
||||||
|
ctx.hash[6] +%= g;
|
||||||
|
ctx.hash[7] +%= h;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sha2_k_256 = [Sha2Ctx.MESSAGE_SCHEDULE_WORDS]u32{
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn sha2_Sigma_256_0(x: u32) u32 {
|
||||||
|
return rotr(u32, x, 2) ^ rotr(u32, x, 13) ^ rotr(u32, x, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha2_Sigma_256_1(x: u32) u32 {
|
||||||
|
return rotr(u32, x, 6) ^ rotr(u32, x, 11) ^ rotr(u32, x, 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha2_sigma_256_0(x: u32) u32 {
|
||||||
|
return rotr(u32, x, 7) ^ rotr(u32, x, 18) ^ shr(u32, x, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sha2_sigma_256_1(x: u32) u32 {
|
||||||
|
return rotr(u32, x, 17) ^ rotr(u32, x, 19) ^ shr(u32, x, 10);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sha224_new() Sha2Ctx {
|
pub fn sha224_new() Sha2Ctx {
|
||||||
return sha2_new(224);
|
return sha2_new(224);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha224_update(ctx: *Sha2Ctx, message: []const u8) !void {
|
pub fn sha224_update(ctx: *Sha2Ctx, message: []const u8) !void {
|
||||||
// TODO
|
if (dbg and !ctx.t_is_224)
|
||||||
_ = .{ ctx, message };
|
@panic("Debug: Attempt to call sha224_update on a SHA-256 context.");
|
||||||
|
return sha2_update(ctx, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha224_final(ctx: *Sha1Ctx, out: [SHA_224_DIGEST_LENGTH]u8) void {
|
pub fn sha224_final(ctx: *Sha2Ctx, out: *[SHA_224_DIGEST_LENGTH]u8) void {
|
||||||
// TODO
|
if (dbg and !ctx.t_is_224)
|
||||||
_ = .{ ctx, out };
|
@panic("Debug: Attempt to call sha224_final on a SHA-256 context.");
|
||||||
|
return sha_generic_final(
|
||||||
|
Sha2Ctx,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
SHA_224_DIGEST_LENGTH,
|
||||||
|
&sha2_compress_block,
|
||||||
|
ctx,
|
||||||
|
out,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha256_new() Sha2Ctx {
|
pub fn sha256_new() Sha2Ctx {
|
||||||
@ -279,18 +405,32 @@ pub fn sha256_new() Sha2Ctx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha256_update(ctx: *Sha2Ctx, message: []const u8) !void {
|
pub fn sha256_update(ctx: *Sha2Ctx, message: []const u8) !void {
|
||||||
// TODO
|
if (dbg and ctx.t_is_224)
|
||||||
_ = .{ ctx, message };
|
@panic("Debug: Attempt to call sha256_update on a SHA-224 context.");
|
||||||
|
return sha2_update(ctx, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha256_final(ctx: *Sha2Ctx, out: [SHA_256_DIGEST_LENGTH]u8) void {
|
pub fn sha256_final(ctx: *Sha2Ctx, out: *[SHA_256_DIGEST_LENGTH]u8) void {
|
||||||
// TODO
|
if (dbg and ctx.t_is_224)
|
||||||
_ = .{ ctx, out };
|
@panic("Debug: Attempt to call sha256_final on a SHA-224 context.");
|
||||||
|
return sha_generic_final(
|
||||||
|
Sha2Ctx,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
SHA_256_DIGEST_LENGTH,
|
||||||
|
&sha2_compress_block,
|
||||||
|
ctx,
|
||||||
|
out,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- SHA-3 ----------------------------------- //
|
||||||
|
|
||||||
pub fn sha3_new(t: comptime_int) Sha3Ctx {
|
pub fn sha3_new(t: comptime_int) Sha3Ctx {
|
||||||
if (comptime t != 224 and t != 256 and t != 384 and t != 512)
|
if (comptime t != 224 and t != 256 and t != 384 and t != 512)
|
||||||
@compileError("SHA-3 context can only be initialized in 224, 256, 384 and 512-bit mode.");
|
@compileError(
|
||||||
|
"SHA-3 context can currently only be initialized in 224, 256, 384 and 512-bit mode.",
|
||||||
|
);
|
||||||
|
|
||||||
const iv: *const [Sha3Ctx.BLOCK_SIZE]u64 = switch (t) {
|
const iv: *const [Sha3Ctx.BLOCK_SIZE]u64 = switch (t) {
|
||||||
224 => &SHA_512_224_IV,
|
224 => &SHA_512_224_IV,
|
||||||
@ -301,16 +441,7 @@ pub fn sha3_new(t: comptime_int) Sha3Ctx {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var ctx = Sha3Ctx{
|
var ctx = Sha3Ctx{
|
||||||
.message_schedule = undefined,
|
|
||||||
.hash = undefined,
|
.hash = undefined,
|
||||||
.a = iv[0],
|
|
||||||
.b = iv[1],
|
|
||||||
.c = iv[2],
|
|
||||||
.d = iv[3],
|
|
||||||
.e = iv[4],
|
|
||||||
.f = iv[5],
|
|
||||||
.g = iv[6],
|
|
||||||
.h = iv[7],
|
|
||||||
.message_buffer = undefined,
|
.message_buffer = undefined,
|
||||||
.message_length = 0,
|
.message_length = 0,
|
||||||
.t_is_224 = (t == 224),
|
.t_is_224 = (t == 224),
|
||||||
@ -331,7 +462,7 @@ pub fn sha384_update(ctx: *Sha3Ctx, message: []const u8) !void {
|
|||||||
_ = .{ ctx, message };
|
_ = .{ ctx, message };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha384_final(ctx: *Sha3Ctx, out: [SHA_384_DIGEST_LENGTH]u8) void {
|
pub fn sha384_final(ctx: *Sha3Ctx, out: *[SHA_384_DIGEST_LENGTH]u8) void {
|
||||||
// TODO
|
// TODO
|
||||||
_ = .{ ctx, out };
|
_ = .{ ctx, out };
|
||||||
}
|
}
|
||||||
@ -345,7 +476,7 @@ pub fn sha512_update(ctx: *Sha3Ctx, message: []const u8) !void {
|
|||||||
_ = .{ ctx, message };
|
_ = .{ ctx, message };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha512_final(ctx: *Sha3Ctx, out: [SHA_512_DIGEST_LENGTH]u8) void {
|
pub fn sha512_final(ctx: *Sha3Ctx, out: *[SHA_512_DIGEST_LENGTH]u8) void {
|
||||||
// TODO
|
// TODO
|
||||||
_ = .{ ctx, out };
|
_ = .{ ctx, out };
|
||||||
}
|
}
|
||||||
@ -359,7 +490,7 @@ pub fn sha512_224_update(ctx: *Sha3Ctx, message: []const u8) !void {
|
|||||||
_ = .{ ctx, message };
|
_ = .{ ctx, message };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha512_224_final(ctx: *Sha3Ctx, out: [SHA_512_224_DIGEST_LENGTH]u8) void {
|
pub fn sha512_224_final(ctx: *Sha3Ctx, out: *[SHA_512_224_DIGEST_LENGTH]u8) void {
|
||||||
// TODO
|
// TODO
|
||||||
_ = .{ ctx, out };
|
_ = .{ ctx, out };
|
||||||
}
|
}
|
||||||
@ -373,12 +504,12 @@ pub fn sha512_256_update(ctx: *Sha3Ctx, message: []const u8) !void {
|
|||||||
_ = .{ ctx, message };
|
_ = .{ ctx, message };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sha512_256_final(ctx: *Sha3Ctx, out: [SHA_512_256_DIGEST_LENGTH]u8) void {
|
pub fn sha512_256_final(ctx: *Sha3Ctx, out: *[SHA_512_256_DIGEST_LENGTH]u8) void {
|
||||||
// TODO
|
// TODO
|
||||||
_ = .{ ctx, out };
|
_ = .{ ctx, out };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------- Non-linear functions ----------------------------------- //
|
// ----------------------------------- LOGICAL FUNCTIONS ----------------------------------- //
|
||||||
|
|
||||||
fn ch(T: type, x: T, y: T, z: T) T {
|
fn ch(T: type, x: T, y: T, z: T) T {
|
||||||
return (x & y) ^ (~x & z);
|
return (x & y) ^ (~x & z);
|
||||||
@ -400,6 +531,16 @@ fn rotl(T: type, word: T, bits: comptime_int) T {
|
|||||||
return (word << bits) | (word >> (@bitSizeOf(T) - bits));
|
return (word << bits) | (word >> (@bitSizeOf(T) - bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rotr(T: type, word: T, bits: comptime_int) T {
|
||||||
|
if (comptime bits >= @bitSizeOf(T))
|
||||||
|
@compileError("Will not rotate word right by more bits than it has!");
|
||||||
|
return rotl(T, word, @bitSizeOf(T) - bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shr(T: type, word: T, bits: comptime_int) T {
|
||||||
|
return word >> bits;
|
||||||
|
}
|
||||||
|
|
||||||
fn serialize_int_big_endian(T: type, int: T) [@sizeOf(T)]u8 {
|
fn serialize_int_big_endian(T: type, int: T) [@sizeOf(T)]u8 {
|
||||||
var res: [@sizeOf(T)]u8 = undefined;
|
var res: [@sizeOf(T)]u8 = undefined;
|
||||||
for (0..@sizeOf(T)) |i|
|
for (0..@sizeOf(T)) |i|
|
||||||
@ -492,3 +633,115 @@ test "SHA-1 maximum length violation (simulated)" {
|
|||||||
ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits
|
ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits
|
||||||
try testing.expectError(MessageLengthLimitExceeded, sha1_update(&ctx, "a"));
|
try testing.expectError(MessageLengthLimitExceeded, sha1_update(&ctx, "a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.di-mgt.com.au/sha_testvectors.html
|
||||||
|
test "SHA-224 basic test" {
|
||||||
|
const tests = [_]struct {
|
||||||
|
message: []const u8,
|
||||||
|
hash: *const [2 * SHA_224_DIGEST_LENGTH]u8,
|
||||||
|
}{
|
||||||
|
.{ .message = "", .hash = "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f" },
|
||||||
|
.{ .message = "abc", .hash = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7" },
|
||||||
|
.{
|
||||||
|
.message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||||
|
.hash = "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
|
||||||
|
.hash = "c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var digest_buffer: [SHA_224_DIGEST_LENGTH]u8 = undefined;
|
||||||
|
|
||||||
|
for (tests) |t| {
|
||||||
|
var ctx = sha224_new();
|
||||||
|
try sha224_update(&ctx, t.message);
|
||||||
|
sha224_final(&ctx, &digest_buffer);
|
||||||
|
|
||||||
|
const reference = hex_to_bytes(SHA_224_DIGEST_LENGTH, t.hash);
|
||||||
|
try testing.expectEqualSlices(u8, reference[0..], digest_buffer[0..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "SHA-224 padding test" {
|
||||||
|
// Here we test every possible length of the message in the last block
|
||||||
|
// to make sure that the padding is correct in every single case.
|
||||||
|
|
||||||
|
// The following are the hashes of the ASCII strings '', 'a', 'aa', etc.
|
||||||
|
// up until 63 (= [SHA-224 block size in bits] / 8 - 1) concatenated 'a's.
|
||||||
|
const reference = [64]*const [2 * SHA_224_DIGEST_LENGTH]u8{ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5", "2ef29e646f6de95de993a59eb1a94cbf52986892949a0abc015a01f7", "ed782653bfec275cf37d027511a68cece08d1e53df1360c762ce043a", "fb56e0a07d1d4ba4a73d42a07d671f65f96513dc12f9d25f2b5ee86e", "04c15392c64c3db52f9a7fcfb7c0c370900e0308007617899430d088", "2886915dd9dedb0ce015f500ffb570901a69a8357728f893eef45344", "b1de26e908098a9b462c11fdd577613d356a7ff06ddb42fda3ab002b", "57dde80cb5dd10cee89424d7d9d8c4fd7949c882da78110e660dec06", "be63cd48d0b068927261c401a33a386136911c3420a5c335e00599fd", "194c4c5045aee1019b77cb1be88ac68034eb2ddd5a8368054141a11f", "5fa4fb5daff0a9b069061839e5605caff0446465f82268775a226333", "85e4d4fcca88691c8008ce19bd9a3de6b9814dd055c8d21bfd4037bb", "da79142fa39f7f11b990aa9dc4994b6e9ce49454058d5423869acf4c", "bd3278d31dec829db380cfe7d7100fecb4b853f054684687f4c27717", "e541fff1289f562eff589da3c8358d91e4dd589bf5dcc37555eb84fe", "0e0a4176c0c6966926acc6fccd2febd418a407df26f76645139d1320", "d5f1096a707710cf5038ed370f7839543eaea0ed054c7484319e2f9c", "e3a0b5557f7894cbb18d6ed06268242212f098d61e179e80a5e96dbc", "fbb6819175a23811c01073f6142af14e80f2d0c598bed6649f307e1f", "43586eff52cbaf9f22482f34a9437ff45bd2e7312ad586b3dd82802f", "2b9d728b01e2c27a40c5fc27d51d5c93f6a160f0f25da8d924e2b570", "eb7ff45dd3d5871ffd6aee24400367cc835aa0d2311d46b2a48b0424", "ac4f39fb481df8f86b6ddf6e527e61893d259b49810416584b468d22", "c4da7f3f63b9da485b734a270f1af6d132b15f177e06445faec5586b", "91a67814117203c32cfc333869bc74f8c721c10672c3516042d88a0b", "7824f1cc7591643231cefe91bbf6fa88b233bf1e8229a59a2cd01c15", "735d169738ebd05edc44cc49a6a99352815cafec12c10d5bd27e8359", "919523d5847d1ee4f9d72f6bd86e3d1bfb785831237a9d580b87448c", "4c166aebdf561231f2b679d8e8457667c0f374043de76d8e7306cb49", "31723190cc4bc6ae686caeea44c54b7b50e30941b8dca3404fe180d8", "f9b67111fdab7e860d1dbe801efa9b75b563e0ee606a9cbabc1288b9", "37e7740b3af132a8edc5e1636817ce1625b831e0ffd47e5c93d4e3b8", "21093fb6a83a36a2b55afa76da42d66de4ebc3b99fb340739d068500", "62d7fcd624a2c1d674ead77584bd66e06d2a6ed160702a594dd6a26e", "507fba56abf0fe3856d0d61527d2a96b18e234bad594bdc969fbfdf4", "6d3a8a79bcc6222986f3b3f17b716774de66d321d51ec34e49995b18", "b622cd8b687970603662b70401a36e464f6860ad337be21c86f17dce", "9c63438f755166862211cc708592f918d31c24e7e70080d6d08e3685", "0126a8fdfaea25bb65fa0b8d6586cd64eee956369b5d2c814607e9f9", "47e263d6419979086bd9a8c4f93591cba373912b68e2ae86085bd2df", "7d8c659cacc50ffc474948b79d65ed98bdab16f6b8a511d184593c86", "da1619b92df1a450be79a14a21e270cb2257230f7dfdc61128e0b49e", "babea0fecfea0aa82d1580abca47dad191f5b9d5a51788d64f678393", "61dd4e5d414a5ae61e76a9b7524223f3cdaf7c9c02b73c175b3e03dd", "fc50179169329ce825404d027fdab44058efc9f28ccddd694a31960e", "b5b7bef9dba5e6a57cecdc03d9a539667f3ca131343de3b6763d7463", "58756e846cce4e08b2ae1103ec3dd2c5755c15f94c1127782dde82c5", "73e0009122e9f4d311459277b81009e9cecc4b3dccf785d4ad476a14", "c0af565a56aeccfd2d40455f20d2c9431a7ab88c61e94973c97cff91", "df427221dc453d5c1466081d9d6e9da3155d5d0dff2a90eb0425036c", "7820fc9fc80c5ed788738da53fbfa6cf1fa981d656a3bb1e68cdf281", "163a72bc0462179bf0486f8a139da514913670d12bbe1d84efc44556", "3fae7c2d692c1610c4a20a17a790d256c3b0071bcdf6fb7fb9538681", "282e1dec88fa36a1070631cca69e3c08a5e18e29fb0b6f6927fbcc0d", "fb0bd626a70c28541dfa781bb5cc4d7d7f56622a58f01a0b1ddd646f", "d40854fc9caf172067136f2e29e1380b14626bf6f0dd06779f820dcd", "b5d09534784ab6578128bce7f28a96a56e3b45c4f734f74739076249", "00df3f1eaa489fd28a9de6e6d7b55402c4e3a56928c5043d77240237", "a82137820aaae9e66f277c3a9254f4a6078c47b410bc9d9a761c2e0b", "efda4316fe2d457d622cf1fc42993d41566f77449b7494b38e250c41", "54e3b540f6792b6a4570f5225717686fbf670fd0dfd3802e4ace9d77", "0daa67402af98b9988c65471b2589dbcdd8bb39569ed77c592aca4a4", "1d4e051f4d6fed2a63fd2421e65834cec00d64456553de3496ae8b1d" };
|
||||||
|
|
||||||
|
var digest_buffer: [SHA_224_DIGEST_LENGTH]u8 = undefined;
|
||||||
|
for (0..64) |i| {
|
||||||
|
var ctx = sha224_new();
|
||||||
|
for (0..i) |_|
|
||||||
|
try sha224_update(&ctx, "a");
|
||||||
|
sha224_final(&ctx, &digest_buffer);
|
||||||
|
|
||||||
|
const ref = hex_to_bytes(SHA_224_DIGEST_LENGTH, reference[i]);
|
||||||
|
try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "SHA-224 maximum length violation (simulated)" {
|
||||||
|
var ctx = sha224_new();
|
||||||
|
ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits
|
||||||
|
try testing.expectError(MessageLengthLimitExceeded, sha224_update(&ctx, "a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.di-mgt.com.au/sha_testvectors.html
|
||||||
|
test "SHA-256 basic test" {
|
||||||
|
const tests = [_]struct {
|
||||||
|
message: []const u8,
|
||||||
|
hash: *const [2 * SHA_256_DIGEST_LENGTH]u8,
|
||||||
|
}{
|
||||||
|
.{ .message = "", .hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" },
|
||||||
|
.{ .message = "abc", .hash = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" },
|
||||||
|
.{
|
||||||
|
.message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||||
|
.hash = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
|
||||||
|
.hash = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var digest_buffer: [SHA_256_DIGEST_LENGTH]u8 = undefined;
|
||||||
|
|
||||||
|
for (tests) |t| {
|
||||||
|
var ctx = sha256_new();
|
||||||
|
try sha256_update(&ctx, t.message);
|
||||||
|
sha256_final(&ctx, &digest_buffer);
|
||||||
|
|
||||||
|
const reference = hex_to_bytes(SHA_256_DIGEST_LENGTH, t.hash);
|
||||||
|
try testing.expectEqualSlices(u8, reference[0..], digest_buffer[0..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "SHA-256 padding test" {
|
||||||
|
// Here we test every possible length of the message in the last block
|
||||||
|
// to make sure that the padding is correct in every single case.
|
||||||
|
|
||||||
|
// The following are the hashes of the ASCII strings '', 'a', 'aa', etc.
|
||||||
|
// up until 63 (= [SHA-256 block size in bits] / 8 - 1) concatenated 'a's.
|
||||||
|
const reference = [64]*const [2 * SHA_256_DIGEST_LENGTH]u8{ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", "961b6dd3ede3cb8ecbaacbd68de040cd78eb2ed5889130cceb4c49268ea4d506", "9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0", "61be55a8e2f6b4e172338bddf184d6dbee29c98853e0a0485ecee7f27b9af0b4", "ed968e840d10d2d313a870bc131a4e2c311d7ad09bdf32b3418147221f51a6e2", "ed02457b5c41d964dbd2f2a609d63fe1bb7528dbe55e1abf5b52c249cd735797", "e46240714b5db3a23eee60479a623efba4d633d27fe4f03c904b9e219a7fbe60", "1f3ce40415a2081fa3eee75fc39fff8e56c22270d1a978a7249b592dcebd20b4", "f2aca93b80cae681221f0445fa4e2cae8a1f9f8fa1e1741d9639caad222f537d", "bf2cb58a68f684d95a3b78ef8f661c9a4e5b09e82cc8f9cc88cce90528caeb27", "28cb017dfc99073aa1b47c1b30f413e3ce774c4991eb4158de50f9dbb36d8043", "f24abc34b13fade76e805799f71187da6cd90b9cac373ae65ed57f143bd664e5", "a689d786e81340e45511dec6c7ab2d978434e5db123362450fe10cfac70d19d0", "82cab7df0abfb9d95dca4e5937ce2968c798c726fea48c016bf9763221efda13", "ef2df0b539c6c23de0f4cbe42648c301ae0e22e887340a4599fb4ef4e2678e48", "0c0beacef8877bbf2416eb00f2b5dc96354e26dd1df5517320459b1236860f8c", "b860666ee2966dd8f903be44ee605c6e1366f926d9f17a8f49937d11624eb99d", "c926defaaa3d13eda2fc63a553bb7fb7326bece6e7cb67ca5296e4727d89bab4", "a0b4aaab8a966e2193ba172d68162c4656860197f256b5f45f0203397ff3f99c", "42492da06234ad0ac76f5d5debdb6d1ae027cffbe746a1c13b89bb8bc0139137", "7df8e299c834de198e264c3e374bc58ecd9382252a705c183beb02f275571e3b", "ec7c494df6d2a7ea36668d656e6b8979e33641bfea378c15038af3964db057a3", "897d3e95b65f26676081f8b9f3a98b6ee4424566303e8d4e7c7522ebae219eab", "09f61f8d9cd65e6a0c258087c485b6293541364e42bd97b2d7936580c8aa3c54", "2f521e2a7d0bd812cbc035f4ed6806eb8d851793b04ba147e8f66b72f5d1f20f", "9976d549a25115dab4e36d0c1fb8f31cb07da87dd83275977360eb7dc09e88de", "cc0616e61cbd6e8e5e34e9fb2d320f37de915820206f5696c31f1fbd24aa16de", "9c547cb8115a44883b9f70ba68f75117cd55359c92611875e386f8af98c172ab", "6913c9c7fd42fe23df8b6bcd4dbaf1c17748948d97f2980b432319c39eddcf6c", "3a54fc0cbc0b0ef48b6507b7788096235d10292dd3ae24e22f5aa062d4f9864a", "61c60b487d1a921e0bcc9bf853dda0fb159b30bf57b2e2d2c753b00be15b5a09", "3ba3f5f43b92602683c19aee62a20342b084dd5971ddd33808d81a328879a547", "852785c805c77e71a22340a54e9d95933ed49121e7d2bf3c2d358854bc1359ea", "a27c896c4859204843166af66f0e902b9c3b3ed6d2fd13d435abc020065c526f", "629362afc62c74497caed2272e30f8125ecd0965f8d8d7cfc4e260f7f8dd319d", "22c1d24bcd03e9aee9832efccd6da613fc702793178e5f12c945c7b67ddda933", "21ec055b38ce759cd4d0f477e9bdec2c5b8199945db4439bae334a964df6246c", "365a9c3e2c2af0a56e47a9dac51c2c5381bf8f41273bad3175e0e619126ad087", "b4d5e56e929ba4cda349e9274e3603d0be246b82016bca20f363963c5f2d6845", "e33cdf9c7f7120b98e8c78408953e07f2ecd183006b5606df349b4c212acf43e", "c0f8bd4dbc2b0c03107c1c37913f2a7501f521467f45dd0fef6958e9a4692719", "7a538607fdaab9296995929f451565bbb8142e1844117322aafd2b3d76b01aff", "66d34fba71f8f450f7e45598853e53bfc23bbd129027cbb131a2f4ffd7878cd0", "16849877c6c21ef0bfa68e4f6747300ddb171b170b9f00e189edc4c2fc4db93e", "52789e3423b72beeb898456a4f49662e46b0cbb960784c5ef4b1399d327e7c27", "6643110c5628fff59edf76d82d5bf573bf800f16a4d65dfb1e5d6f1a46296d0b", "11eaed932c6c6fddfc2efc394e609facf4abe814fc6180d03b14fce13a07d0e5", "97daac0ee9998dfcad6c9c0970da5ca411c86233a944c25b47566f6a7bc1ddd5", "8f9bec6a62dd28ebd36d1227745592de6658b36974a3bb98a4c582f683ea6c42", "160b4e433e384e05e537dc59b467f7cb2403f0214db15c5db58862a3f1156d2e", "bfc5fe0e360152ca98c50fab4ed7e3078c17debc2917740d5000913b686ca129", "6c1b3dc7a706b9dc81352a6716b9c666c608d8626272c64b914ab05572fc6e84", "abe346a7259fc90b4c27185419628e5e6af6466b1ae9b5446cac4bfc26cf05c4", "a3f01b6939256127582ac8ae9fb47a382a244680806a3f613a118851c1ca1d47", "9f4390f8d30c2dd92ec9f095b65e2b9ae9b0a925a5258e241c9f1e910f734318", "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a", "f13b2d724659eb3bf47f2dd6af1accc87b81f09f59f2b75e5c0bed6589dfe8c6", "d5c039b748aa64665782974ec3dc3025c042edf54dcdc2b5de31385b094cb678", "111bb261277afd65f0744b247cd3e47d386d71563d0ed995517807d5ebd4fba3", "11ee391211c6256460b6ed375957fadd8061cafbb31daf967db875aebd5aaad4", "35d5fc17cfbbadd00f5e710ada39f194c5ad7c766ad67072245f1fad45f0f530", "f506898cc7c2e092f9eb9fadae7ba50383f5b46a2a4fe5597dbb553a78981268", "7d3e74a05d7db15bce4ad9ec0658ea98e3f06eeecf16b4c6fff2da457ddc2f34" };
|
||||||
|
|
||||||
|
var digest_buffer: [SHA_256_DIGEST_LENGTH]u8 = undefined;
|
||||||
|
for (0..64) |i| {
|
||||||
|
var ctx = sha256_new();
|
||||||
|
for (0..i) |_|
|
||||||
|
try sha256_update(&ctx, "a");
|
||||||
|
sha256_final(&ctx, &digest_buffer);
|
||||||
|
|
||||||
|
const ref = hex_to_bytes(SHA_256_DIGEST_LENGTH, reference[i]);
|
||||||
|
try testing.expectEqualSlices(u8, ref[0..], digest_buffer[0..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "SHA-256 maximum length violation (simulated)" {
|
||||||
|
var ctx = sha256_new();
|
||||||
|
ctx.message_length = (1 << 61) - 1; // 2^64 - 8 bits
|
||||||
|
try testing.expectError(MessageLengthLimitExceeded, sha256_update(&ctx, "a"));
|
||||||
|
}
|
||||||
|
|||||||
@ -6,7 +6,10 @@ const CryptoError = error{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// rn just for build, later will be used by high-level API
|
// rn just for build, later will be used by high-level API
|
||||||
const aes = @import("primitive/blockcipher/aes.zig");
|
pub const aes = @import("primitive/blockcipher/aes.zig");
|
||||||
|
pub const chacha20 = @import("primitive/streamcipher/chacha20.zig");
|
||||||
|
pub const salsa20 = @import("primitive/streamcipher/salsa20.zig");
|
||||||
|
pub const sha = @import("primitive/digest/sha.zig");
|
||||||
|
|
||||||
// Leave this for later, maybe make a separate ffi module
|
// Leave this for later, maybe make a separate ffi module
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user