From 6f9ea9eaef79863ebdc9bf44b2af67ec4caad031 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 19 Aug 2020 16:21:05 +0200 Subject: [PATCH] Breaking: sort std/crypto functions into categories Instead of having all primitives and constructions share the same namespace, they are now organized by category and function family. Types within the same category are expected to share the exact same API. --- lib/std/bloom_filter.zig | 2 +- lib/std/build/write_file.zig | 2 +- lib/std/cache_hash.zig | 2 +- lib/std/crypto.zig | 125 +++++++++++++++------------- lib/std/crypto/25519/ed25519.zig | 2 +- lib/std/crypto/benchmark.zig | 33 ++++---- lib/std/crypto/chacha20.zig | 134 ++++++++++++++++--------------- lib/std/crypto/gimli.zig | 10 ++- lib/std/crypto/hmac.zig | 23 ++++-- lib/std/crypto/md5.zig | 26 +++--- lib/std/crypto/sha1.zig | 28 ++++--- lib/std/crypto/sha2.zig | 31 +++---- lib/std/crypto/sha3.zig | 10 +-- lib/std/rand.zig | 4 +- lib/std/zig.zig | 2 +- tools/process_headers.zig | 2 +- 16 files changed, 242 insertions(+), 194 deletions(-) diff --git a/lib/std/bloom_filter.zig b/lib/std/bloom_filter.zig index 34fa42fe00..5ccef8d140 100644 --- a/lib/std/bloom_filter.zig +++ b/lib/std/bloom_filter.zig @@ -158,7 +158,7 @@ pub fn BloomFilter( } fn hashFunc(out: []u8, Ki: usize, in: []const u8) void { - var st = std.crypto.gimli.Hash.init(); + var st = std.crypto.hash.Gimli.init(); st.update(std.mem.asBytes(&Ki)); st.update(in); st.final(out); diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig index fe15e6e0e3..222de7c79a 100644 --- a/lib/std/build/write_file.zig +++ b/lib/std/build/write_file.zig @@ -58,7 +58,7 @@ pub const WriteFileStep = struct { // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b // directly and construct the path, and no "cache hit" detection happens; the files // are always written. - var hash = std.crypto.Blake2b384.init(); + var hash = std.crypto.hash.blake2.Blake2b384.init(); // Random bytes to make WriteFileStep unique. Refresh this with // new random bytes when WriteFileStep implementation is modified diff --git a/lib/std/cache_hash.zig b/lib/std/cache_hash.zig index 58c9f444f3..8025929347 100644 --- a/lib/std/cache_hash.zig +++ b/lib/std/cache_hash.zig @@ -4,7 +4,7 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std.zig"); -const Blake3 = std.crypto.Blake3; +const Blake3 = std.crypto.hash.Blake3; const fs = std.fs; const base64 = std.base64; const ArrayList = std.ArrayList; diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index edca2bc2ff..2a18bd88c7 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -3,60 +3,68 @@ // This file is part of [zig](https://ziglang.org/), which is MIT licensed. // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. -pub const Md5 = @import("crypto/md5.zig").Md5; -pub const Sha1 = @import("crypto/sha1.zig").Sha1; -const sha2 = @import("crypto/sha2.zig"); -pub const Sha224 = sha2.Sha224; -pub const Sha256 = sha2.Sha256; -pub const Sha384 = sha2.Sha384; -pub const Sha512 = sha2.Sha512; +/// Hash functions. +pub const hash = struct { + pub const Md5 = @import("crypto/md5.zig").Md5; + pub const Sha1 = @import("crypto/sha1.zig").Sha1; + pub const sha2 = @import("crypto/sha2.zig"); + pub const sha3 = @import("crypto/sha3.zig"); + pub const blake2 = @import("crypto/blake2.zig"); + pub const Blake3 = @import("crypto/blake3.zig").Blake3; + pub const Gimli = @import("crypto/gimli.zig").Hash; +}; -const sha3 = @import("crypto/sha3.zig"); -pub const Sha3_224 = sha3.Sha3_224; -pub const Sha3_256 = sha3.Sha3_256; -pub const Sha3_384 = sha3.Sha3_384; -pub const Sha3_512 = sha3.Sha3_512; - -pub const gimli = @import("crypto/gimli.zig"); - -const blake2 = @import("crypto/blake2.zig"); -pub const Blake2s224 = blake2.Blake2s224; -pub const Blake2s256 = blake2.Blake2s256; -pub const Blake2b384 = blake2.Blake2b384; -pub const Blake2b512 = blake2.Blake2b512; - -pub const Blake3 = @import("crypto/blake3.zig").Blake3; - -const hmac = @import("crypto/hmac.zig"); -pub const HmacMd5 = hmac.HmacMd5; -pub const HmacSha1 = hmac.HmacSha1; -pub const HmacSha256 = hmac.HmacSha256; -pub const HmacBlake2s256 = hmac.HmacBlake2s256; - -pub const chacha20 = @import("crypto/chacha20.zig"); -pub const chaCha20IETF = chacha20.chaCha20IETF; -pub const chaCha20With64BitNonce = chacha20.chaCha20With64BitNonce; -pub const xChaCha20IETF = chacha20.xChaCha20IETF; - -pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; - -const import_aes = @import("crypto/aes.zig"); -pub const AES128 = import_aes.AES128; -pub const AES256 = import_aes.AES256; - -pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; -pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519; -pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; -pub const X25519 = @import("crypto/25519/x25519.zig").X25519; -pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; +/// Authentication (MAC) functions. +pub const auth = struct { + pub const hmac = @import("crypto/hmac.zig"); +}; +/// Authenticated Encryption with Associated Data pub const aead = struct { - pub const Gimli = gimli.Aead; + const chacha20 = @import("crypto/chacha20.zig"); + + pub const Gimli = @import("crypto/gimli.zig").Aead; pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305; pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305; }; +/// MAC functions requiring single-use secret keys. +pub const onetimeauth = struct { + pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; +}; + +/// Core functions, that should rarely be used directly by applications. +pub const core = struct { + pub const aes = @import("crypto/aes.zig"); + pub const Gimli = @import("crypto/gimli.zig").State; +}; + +/// Elliptic-curve arithmetic. +pub const ecc = struct { + pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; + pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; + pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; +}; + +/// Diffie-Hellman key exchange functions. +pub const dh = struct { + pub const X25519 = @import("crypto/25519/x25519.zig").X25519; +}; + +/// Digital signature functions. +pub const sign = struct { + pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519; +}; + +/// Stream ciphers. These do not provide any kind of authentication. +/// Most applications should be using AEAD constructions instead of stream ciphers directly. +pub const stream = struct { + pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF; + pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF; + pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce; +}; + const std = @import("std.zig"); pub const randomBytes = std.os.getrandom; @@ -83,16 +91,21 @@ test "crypto" { test "issue #4532: no index out of bounds" { const types = [_]type{ - Md5, - Sha1, - Sha224, - Sha256, - Sha384, - Sha512, - Blake2s224, - Blake2s256, - Blake2b384, - Blake2b512, + hash.Md5, + hash.Sha1, + hash.sha2.Sha224, + hash.sha2.Sha256, + hash.sha2.Sha384, + hash.sha2.Sha512, + hash.sha3.Sha3_224, + hash.sha3.Sha3_256, + hash.sha3.Sha3_384, + hash.sha3.Sha3_512, + hash.blake2.Blake2s224, + hash.blake2.Blake2s256, + hash.blake2.Blake2b384, + hash.blake2.Blake2b512, + hash.Gimli, }; inline for (types) |Hasher| { diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 935abaecb3..d02fa0a2fd 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -6,7 +6,7 @@ const std = @import("std"); const fmt = std.fmt; const mem = std.mem; -const Sha512 = std.crypto.Sha512; +const Sha512 = std.crypto.hash.sha2.Sha512; /// Ed25519 (EdDSA) signatures. pub const Ed25519 = struct { diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 5c27f2f4f5..748d575751 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -22,16 +22,16 @@ const Crypto = struct { }; const hashes = [_]Crypto{ - Crypto{ .ty = crypto.Md5, .name = "md5" }, - Crypto{ .ty = crypto.Sha1, .name = "sha1" }, - Crypto{ .ty = crypto.Sha256, .name = "sha256" }, - Crypto{ .ty = crypto.Sha512, .name = "sha512" }, - Crypto{ .ty = crypto.Sha3_256, .name = "sha3-256" }, - Crypto{ .ty = crypto.Sha3_512, .name = "sha3-512" }, - Crypto{ .ty = crypto.gimli.Hash, .name = "gimli-hash" }, - Crypto{ .ty = crypto.Blake2s256, .name = "blake2s" }, - Crypto{ .ty = crypto.Blake2b512, .name = "blake2b" }, - Crypto{ .ty = crypto.Blake3, .name = "blake3" }, + Crypto{ .ty = crypto.hash.Md5, .name = "md5" }, + Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" }, + Crypto{ .ty = crypto.hash.sha2.Sha256, .name = "sha256" }, + Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" }, + Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" }, + Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" }, + Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" }, + Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" }, + Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" }, + Crypto{ .ty = crypto.hash.Blake3, .name = "blake3" }, }; pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 { @@ -55,10 +55,11 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 } const macs = [_]Crypto{ - Crypto{ .ty = crypto.Poly1305, .name = "poly1305" }, - Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" }, - Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" }, - Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" }, + Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" }, + Crypto{ .ty = crypto.auth.HmacMd5, .name = "hmac-md5" }, + Crypto{ .ty = crypto.auth.HmacSha1, .name = "hmac-sha1" }, + Crypto{ .ty = crypto.auth.sha2.HmacSha256, .name = "hmac-sha256" }, + Crypto{ .ty = crypto.auth.sha2.HmacSha512, .name = "hmac-sha512" }, }; pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { @@ -84,7 +85,7 @@ pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { return throughput; } -const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }}; +const exchanges = [_]Crypto{Crypto{ .ty = crypto.dh.X25519, .name = "x25519" }}; pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_count: comptime_int) !u64 { std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length); @@ -111,7 +112,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c return throughput; } -const signatures = [_]Crypto{Crypto{ .ty = crypto.Ed25519, .name = "ed25519" }}; +const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }}; pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { var seed: [Signature.seed_length]u8 = undefined; diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index d402eb6750..1a359ecfc7 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -12,7 +12,7 @@ const assert = std.debug.assert; const testing = std.testing; const builtin = @import("builtin"); const maxInt = std.math.maxInt; -const Poly1305 = std.crypto.Poly1305; +const Poly1305 = std.crypto.onetimeauth.Poly1305; const QuarterRound = struct { a: usize, @@ -137,56 +137,60 @@ fn keyToWords(key: [32]u8) [8]u32 { /// /// ChaCha20 is self-reversing. To decrypt just run the cipher with the same /// counter, nonce, and key. -pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void { - assert(in.len >= out.len); - assert((in.len >> 6) + counter <= maxInt(u32)); +pub const ChaCha20IETF = struct { + pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void { + assert(in.len >= out.len); + assert((in.len >> 6) + counter <= maxInt(u32)); - var c: [4]u32 = undefined; - c[0] = counter; - c[1] = mem.readIntLittle(u32, nonce[0..4]); - c[2] = mem.readIntLittle(u32, nonce[4..8]); - c[3] = mem.readIntLittle(u32, nonce[8..12]); - chaCha20_internal(out, in, keyToWords(key), c); -} + var c: [4]u32 = undefined; + c[0] = counter; + c[1] = mem.readIntLittle(u32, nonce[0..4]); + c[2] = mem.readIntLittle(u32, nonce[4..8]); + c[3] = mem.readIntLittle(u32, nonce[8..12]); + chaCha20_internal(out, in, keyToWords(key), c); + } +}; /// This is the original ChaCha20 before RFC 7539, which recommends using the /// orgininal version on applications such as disk or file encryption that might /// exceed the 256 GiB limit of the 96-bit nonce version. -pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void { - assert(in.len >= out.len); - assert(counter +% (in.len >> 6) >= counter); +pub const ChaCha20With64BitNonce = struct { + pub fn xor(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void { + assert(in.len >= out.len); + assert(counter +% (in.len >> 6) >= counter); - var cursor: usize = 0; - const k = keyToWords(key); - var c: [4]u32 = undefined; - c[0] = @truncate(u32, counter); - c[1] = @truncate(u32, counter >> 32); - c[2] = mem.readIntLittle(u32, nonce[0..4]); - c[3] = mem.readIntLittle(u32, nonce[4..8]); + var cursor: usize = 0; + const k = keyToWords(key); + var c: [4]u32 = undefined; + c[0] = @truncate(u32, counter); + c[1] = @truncate(u32, counter >> 32); + c[2] = mem.readIntLittle(u32, nonce[0..4]); + c[3] = mem.readIntLittle(u32, nonce[4..8]); - const block_size = (1 << 6); - // The full block size is greater than the address space on a 32bit machine - const big_block = if (@sizeOf(usize) > 4) (block_size << 32) else maxInt(usize); + const block_size = (1 << 6); + // The full block size is greater than the address space on a 32bit machine + const big_block = if (@sizeOf(usize) > 4) (block_size << 32) else maxInt(usize); - // first partial big block - if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) { - chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c); - cursor = big_block - cursor; - c[1] += 1; - if (comptime @sizeOf(usize) > 4) { - // A big block is giant: 256 GiB, but we can avoid this limitation - var remaining_blocks: u32 = @intCast(u32, (in.len / big_block)); - var i: u32 = 0; - while (remaining_blocks > 0) : (remaining_blocks -= 1) { - chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c); - c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this. - cursor += big_block; + // first partial big block + if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) { + chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c); + cursor = big_block - cursor; + c[1] += 1; + if (comptime @sizeOf(usize) > 4) { + // A big block is giant: 256 GiB, but we can avoid this limitation + var remaining_blocks: u32 = @intCast(u32, (in.len / big_block)); + var i: u32 = 0; + while (remaining_blocks > 0) : (remaining_blocks -= 1) { + chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c); + c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this. + cursor += big_block; + } } } - } - chaCha20_internal(out[cursor..], in[cursor..], k, c); -} + chaCha20_internal(out[cursor..], in[cursor..], k, c); + } +}; // https://tools.ietf.org/html/rfc7539#section-2.4.2 test "crypto.chacha20 test vector sunscreen" { @@ -221,12 +225,12 @@ test "crypto.chacha20 test vector sunscreen" { 0, 0, 0, 0, }; - chaCha20IETF(result[0..], input[0..], 1, key, nonce); + ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce); testing.expectEqualSlices(u8, &expected_result, &result); // Chacha20 is self-reversing. var plaintext: [114]u8 = undefined; - chaCha20IETF(plaintext[0..], result[0..], 1, key, nonce); + ChaCha20IETF.xor(plaintext[0..], result[0..], 1, key, nonce); testing.expect(mem.order(u8, input, &plaintext) == .eq); } @@ -261,7 +265,7 @@ test "crypto.chacha20 test vector 1" { }; const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; - chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); + ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); testing.expectEqualSlices(u8, &expected_result, &result); } @@ -295,7 +299,7 @@ test "crypto.chacha20 test vector 2" { }; const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; - chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); + ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); testing.expectEqualSlices(u8, &expected_result, &result); } @@ -329,7 +333,7 @@ test "crypto.chacha20 test vector 3" { }; const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 }; - chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); + ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); testing.expectEqualSlices(u8, &expected_result, &result); } @@ -363,7 +367,7 @@ test "crypto.chacha20 test vector 4" { }; const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 }; - chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); + ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); testing.expectEqualSlices(u8, &expected_result, &result); } @@ -435,21 +439,21 @@ test "crypto.chacha20 test vector 5" { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, }; - chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); + ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce); testing.expectEqualSlices(u8, &expected_result, &result); } pub const chacha20poly1305_tag_size = 16; -pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { +fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { assert(ciphertext.len >= plaintext.len); // derive poly1305 key var polyKey = [_]u8{0} ** 32; - chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce); + ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce); // encrypt plaintext - chaCha20IETF(ciphertext[0..plaintext.len], plaintext, 1, key, nonce); + ChaCha20IETF.xor(ciphertext[0..plaintext.len], plaintext, 1, key, nonce); // construct mac var mac = Poly1305.init(polyKey[0..]); @@ -472,18 +476,18 @@ pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_ta mac.final(tag); } -pub fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { +fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void { return chacha20poly1305SealDetached(ciphertextAndTag[0..plaintext.len], ciphertextAndTag[plaintext.len..][0..chacha20poly1305_tag_size], plaintext, data, key, nonce); } /// Verifies and decrypts an authenticated message produced by chacha20poly1305SealDetached. -pub fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { +fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { // split ciphertext and tag assert(dst.len >= ciphertext.len); // derive poly1305 key var polyKey = [_]u8{0} ** 32; - chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce); + ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce); // construct mac var mac = Poly1305.init(polyKey[0..]); @@ -519,11 +523,11 @@ pub fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *con } // decrypt ciphertext - chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce); + ChaCha20IETF.xor(dst[0..ciphertext.len], ciphertext, 1, key, nonce); } /// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal. -pub fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { +fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void { if (ciphertextAndTag.len < chacha20poly1305_tag_size) { return error.InvalidMessage; } @@ -562,31 +566,33 @@ fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [12]u8 } { }; } -pub fn xChaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void { - const extended = extend(key, nonce); - chaCha20IETF(out, in, counter, extended.key, extended.nonce); -} +pub const XChaCha20IETF = struct { + pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void { + const extended = extend(key, nonce); + ChaCha20IETF.xor(out, in, counter, extended.key, extended.nonce); + } +}; pub const xchacha20poly1305_tag_size = 16; -pub fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void { +fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void { const extended = extend(key, nonce); return chacha20poly1305SealDetached(ciphertext, tag, plaintext, data, extended.key, extended.nonce); } -pub fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void { +fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void { const extended = extend(key, nonce); return chacha20poly1305Seal(ciphertextAndTag, plaintext, data, extended.key, extended.nonce); } /// Verifies and decrypts an authenticated message produced by xchacha20poly1305SealDetached. -pub fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void { +fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void { const extended = extend(key, nonce); return try chacha20poly1305OpenDetached(plaintext, ciphertext, tag, data, extended.key, extended.nonce); } /// Verifies and decrypts an authenticated message produced by xchacha20poly1305Seal. -pub fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void { +fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void { const extended = extend(key, nonce); return try chacha20poly1305Open(ciphertextAndTag, msgAndTag, data, extended.key, extended.nonce); } @@ -714,7 +720,7 @@ test "crypto.xchacha20" { const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; { var ciphertext: [input.len]u8 = undefined; - xChaCha20IETF(ciphertext[0..], input[0..], 0, key, nonce); + XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce); var buf: [2 * ciphertext.len]u8 = undefined; testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D"); } diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 33afae5f82..77ea74fe0e 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -109,17 +109,21 @@ pub const Hash = struct { state: State, buf_off: usize, + pub const block_length = State.RATE; + const Self = @This(); pub fn init() Self { return Self{ - .state = State{ - .data = [_]u32{0} ** (State.BLOCKBYTES / 4), - }, + .state = State{ .data = [_]u32{0} ** (State.BLOCKBYTES / 4) }, .buf_off = 0, }; } + pub fn reset(self: *Self) void { + self.* = init(); + } + /// Also known as 'absorb' pub fn update(self: *Self, data: []const u8) void { const buf = self.state.toSlice(); diff --git a/lib/std/crypto/hmac.zig b/lib/std/crypto/hmac.zig index 984dacc967..212bc3e7b0 100644 --- a/lib/std/crypto/hmac.zig +++ b/lib/std/crypto/hmac.zig @@ -8,10 +8,19 @@ const crypto = std.crypto; const debug = std.debug; const mem = std.mem; -pub const HmacMd5 = Hmac(crypto.Md5); -pub const HmacSha1 = Hmac(crypto.Sha1); -pub const HmacSha256 = Hmac(crypto.Sha256); -pub const HmacBlake2s256 = Hmac(crypto.Blake2s256); +pub const HmacMd5 = Hmac(crypto.hash.legacy.Md5); +pub const HmacSha1 = Hmac(crypto.hash.legacy.Sha1); + +pub const sha2 = struct { + pub const HmacSha224 = Hmac(crypto.hash.sha2.Sha224); + pub const HmacSha256 = Hmac(crypto.hash.sha2.Sha256); + pub const HmacSha384 = Hmac(crypto.hash.sha2.Sha384); + pub const HmacSha512 = Hmac(crypto.hash.sha2.Sha512); +}; + +pub const blake2 = struct { + pub const HmacBlake2s256 = Hmac(crypto.hash.blake2.Blake2s256); +}; pub fn Hmac(comptime Hash: type) type { return struct { @@ -95,10 +104,10 @@ test "hmac sha1" { } test "hmac sha256" { - var out: [HmacSha256.mac_length]u8 = undefined; - HmacSha256.create(out[0..], "", ""); + var out: [sha2.HmacSha256.mac_length]u8 = undefined; + sha2.HmacSha256.create(out[0..], "", ""); htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]); - HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key"); + sha2.HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key"); htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]); } diff --git a/lib/std/crypto/md5.zig b/lib/std/crypto/md5.zig index d578ba8b95..950e52e126 100644 --- a/lib/std/crypto/md5.zig +++ b/lib/std/crypto/md5.zig @@ -32,6 +32,9 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar }; } +/// The MD5 function is now considered cryptographically broken. +/// Namely, it is trivial to find multiple inputs producing the same hash. +/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3. pub const Md5 = struct { const Self = @This(); pub const block_length = 64; @@ -44,18 +47,21 @@ pub const Md5 = struct { total_len: u64, pub fn init() Self { - var d: Self = undefined; - d.reset(); - return d; + return Self{ + .s = [_]u32{ + 0x67452301, + 0xEFCDAB89, + 0x98BADCFE, + 0x10325476, + }, + .buf = undefined, + .buf_len = 0, + .total_len = 0, + }; } - pub fn reset(d: *Self) void { - d.s[0] = 0x67452301; - d.s[1] = 0xEFCDAB89; - d.s[2] = 0x98BADCFE; - d.s[3] = 0x10325476; - d.buf_len = 0; - d.total_len = 0; + pub fn reset(self: *Self) void { + self.* = init(); } pub fn hash(b: []const u8, out: []u8) void { diff --git a/lib/std/crypto/sha1.zig b/lib/std/crypto/sha1.zig index 7cb4fbcf36..ce18ca5ef7 100644 --- a/lib/std/crypto/sha1.zig +++ b/lib/std/crypto/sha1.zig @@ -29,6 +29,9 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { }; } +/// The SHA-1 function is now considered cryptographically broken. +/// Namely, it is feasible to find multiple inputs producing the same hash. +/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3. pub const Sha1 = struct { const Self = @This(); pub const block_length = 64; @@ -41,19 +44,22 @@ pub const Sha1 = struct { total_len: u64, pub fn init() Self { - var d: Self = undefined; - d.reset(); - return d; + return Self{ + .s = [_]u32{ + 0x67452301, + 0xEFCDAB89, + 0x98BADCFE, + 0x10325476, + 0xC3D2E1F0, + }, + .buf = undefined, + .buf_len = 0, + .total_len = 0, + }; } - pub fn reset(d: *Self) void { - d.s[0] = 0x67452301; - d.s[1] = 0xEFCDAB89; - d.s[2] = 0x98BADCFE; - d.s[3] = 0x10325476; - d.s[4] = 0xC3D2E1F0; - d.buf_len = 0; - d.total_len = 0; + pub fn reset(self: *Self) void { + self.* = init(); } pub fn hash(b: []const u8, out: []u8) void { diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index cac18ca1f7..e7d0135e81 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -93,22 +93,25 @@ fn Sha2_32(comptime params: Sha2Params32) type { total_len: u64, pub fn init() Self { - var d: Self = undefined; - d.reset(); - return d; + return Self{ + .s = [_]u32{ + params.iv0, + params.iv1, + params.iv2, + params.iv3, + params.iv4, + params.iv5, + params.iv6, + params.iv7, + }, + .buf = undefined, + .buf_len = 0, + .total_len = 0, + }; } - pub fn reset(d: *Self) void { - d.s[0] = params.iv0; - d.s[1] = params.iv1; - d.s[2] = params.iv2; - d.s[3] = params.iv3; - d.s[4] = params.iv4; - d.s[5] = params.iv5; - d.s[6] = params.iv6; - d.s[7] = params.iv7; - d.buf_len = 0; - d.total_len = 0; + pub fn reset(self: *Self) void { + self.* = init(); } pub fn hash(b: []const u8, out: []u8) void { diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 03635ca553..630a44fbfb 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -27,14 +27,14 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { pub fn init() Self { var d: Self = undefined; - d.reset(); - return d; - } - - pub fn reset(d: *Self) void { mem.set(u8, d.s[0..], 0); d.offset = 0; d.rate = 200 - (bits / 4); + return d; + } + + pub fn reset(self: *Self) void { + self.* = init(); } pub fn hash(b: []const u8, out: []u8) void { diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 85f11ff449..42726472bf 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -737,12 +737,12 @@ test "xoroshiro sequence" { // CSPRNG pub const Gimli = struct { random: Random, - state: std.crypto.gimli.State, + state: std.crypto.core.Gimli, pub fn init(init_s: u64) Gimli { var self = Gimli{ .random = Random{ .fillFn = fill }, - .state = std.crypto.gimli.State{ + .state = std.crypto.core.Gimli{ .data = [_]u32{0} ** (std.crypto.gimli.State.BLOCKBYTES / 4), }, }; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 34e03eb164..c93c22f257 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -26,7 +26,7 @@ pub fn hashSrc(src: []const u8) SrcHash { std.mem.copy(u8, &out, src); std.mem.set(u8, out[src.len..], 0); } else { - std.crypto.Blake3.hash(src, &out); + std.crypto.hash.Blake3.hash(src, &out); } return out; } diff --git a/tools/process_headers.zig b/tools/process_headers.zig index d17069401e..ba9fde6683 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -313,7 +313,7 @@ pub fn main() !void { var max_bytes_saved: usize = 0; var total_bytes: usize = 0; - var hasher = std.crypto.Sha256.init(); + var hasher = std.crypto.hash.sha2.Sha256.init(); for (libc_targets) |libc_target| { const dest_target = DestTarget{