From 4a0508e56c06456c367c57a7565b9f757f2ff663 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 12 Apr 2023 21:07:36 +0100 Subject: [PATCH 1/4] std: add kinfo_vmentry for FreeBSD --- lib/std/c/freebsd.zig | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index c877a11ea0a2..96843ee6fd9e 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -24,6 +24,7 @@ pub extern "c" fn malloc_usable_size(?*const anyopaque) usize; pub extern "c" fn getpid() pid_t; pub extern "c" fn kinfo_getfile(pid: pid_t, cntp: *c_int) ?[*]kinfo_file; +pub extern "c" fn kinfo_getvmmap(pid: pid_t, cntp: *c_int) ?[*]kinfo_vmentry; pub const sf_hdtr = extern struct { headers: [*]const iovec_const, @@ -565,6 +566,40 @@ comptime { std.debug.assert(@alignOf(kinfo_file) == @sizeOf(u64)); } +pub const kinfo_vmentry = extern struct { + kve_structsize: c_int, + kve_type: c_int, + kve_start: u64, + kve_end: u64, + kve_offset: u64, + kve_vn_fileid: u64, + kve_vn_fsid_freebsd11: u32, + kve_flags: c_int, + kve_resident: c_int, + kve_private_resident: c_int, + kve_protection: c_int, + kve_ref_count: c_int, + kve_shadow_count: c_int, + kve_vn_type: c_int, + kve_vn_size: u64, + kve_vn_rdev_freebsd11: u32, + kve_vn_mode: u16, + kve_status: u16, + kve_type_spec: extern union { + _kve_vn_fsid: u64, + _kve_obj: u64, + }, + kve_vn_rdev: u64, + _kve_ispare: [8]c_int, + kve_rpath: [PATH_MAX]u8, +}; + +pub const KINFO_VMENTRY_SIZE = 1160; + +comptime { + std.debug.assert(@sizeOf(kinfo_vmentry) == KINFO_VMENTRY_SIZE); +} + pub const CTL = struct { pub const KERN = 1; pub const DEBUG = 5; From 9adee806e346df4513561e1b55f35ba4468cfb3b Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Fri, 14 Apr 2023 06:06:00 +0200 Subject: [PATCH 2/4] secp256k1: Endormorphism.splitScalar() can return an error (#15270) Fixes #15267 --- lib/std/crypto/ecdsa.zig | 2 +- lib/std/crypto/pcurves/secp256k1.zig | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig index a0faa3da540c..919578c02e48 100644 --- a/lib/std/crypto/ecdsa.zig +++ b/lib/std/crypto/ecdsa.zig @@ -249,7 +249,7 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { } /// Verify that the signature is valid for the entire message. - pub fn verify(self: *Verifier) (IdentityElementError || SignatureVerificationError)!void { + pub fn verify(self: *Verifier) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void { const ht = Curve.scalar.encoded_length; const h_len = @max(Hash.digest_length, ht); var h: [h_len]u8 = [_]u8{0} ** h_len; diff --git a/lib/std/crypto/pcurves/secp256k1.zig b/lib/std/crypto/pcurves/secp256k1.zig index 12bd022b1177..9dd8f550512f 100644 --- a/lib/std/crypto/pcurves/secp256k1.zig +++ b/lib/std/crypto/pcurves/secp256k1.zig @@ -51,7 +51,7 @@ pub const Secp256k1 = struct { }; /// Compute r1 and r2 so that k = r1 + r2*lambda (mod L). - pub fn splitScalar(s: [32]u8, endian: std.builtin.Endian) SplitScalar { + pub fn splitScalar(s: [32]u8, endian: std.builtin.Endian) NonCanonicalError!SplitScalar { const b1_neg_s = comptime s: { var buf: [32]u8 = undefined; mem.writeIntLittle(u256, &buf, 303414439467246543595250775667605759171); @@ -73,15 +73,15 @@ pub const Secp256k1 = struct { var buf: [32]u8 = undefined; mem.writeIntLittle(u256, &buf, c1); - const c1x = scalar.mul(buf, b1_neg_s, .Little) catch unreachable; + const c1x = try scalar.mul(buf, b1_neg_s, .Little); mem.writeIntLittle(u256, &buf, c2); - const c2x = scalar.mul(buf, b2_neg_s, .Little) catch unreachable; + const c2x = try scalar.mul(buf, b2_neg_s, .Little); - const r2 = scalar.add(c1x, c2x, .Little) catch unreachable; + const r2 = try scalar.add(c1x, c2x, .Little); - var r1 = scalar.mul(r2, lambda_s, .Little) catch unreachable; - r1 = scalar.sub(s, r1, .Little) catch unreachable; + var r1 = try scalar.mul(r2, lambda_s, .Little); + r1 = try scalar.sub(s, r1, .Little); return SplitScalar{ .r1 = r1, .r2 = r2 }; } @@ -435,7 +435,7 @@ pub const Secp256k1 = struct { /// Multiply an elliptic curve point by a *PUBLIC* scalar *IN VARIABLE TIME* /// This can be used for signature verification. - pub fn mulPublic(p: Secp256k1, s_: [32]u8, endian: std.builtin.Endian) IdentityElementError!Secp256k1 { + pub fn mulPublic(p: Secp256k1, s_: [32]u8, endian: std.builtin.Endian) (IdentityElementError || NonCanonicalError)!Secp256k1 { const s = if (endian == .Little) s_ else Fe.orderSwap(s_); const zero = comptime scalar.Scalar.zero.toBytes(.Little); if (mem.eql(u8, &zero, &s)) { @@ -443,7 +443,7 @@ pub const Secp256k1 = struct { } const pc = precompute(p, 8); var lambda_p = try pcMul(&pc, Endormorphism.lambda_s, true); - var split_scalar = Endormorphism.splitScalar(s, .Little); + var split_scalar = try Endormorphism.splitScalar(s, .Little); var px = p; // If a key is negative, flip the sign to keep it half-sized, From 56d800ff7e65897d8ff9ede8e8194af4d08f0df5 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 13 Apr 2023 22:49:24 +0100 Subject: [PATCH 3/4] std add getrandom to solato solaris based systems --- lib/std/c/solaris.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index fe60c426e5e4..b1b789c77b4a 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -16,6 +16,7 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; +pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; pub extern "c" fn sysconf(sc: c_int) i64; pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int; From b42562be74ee7b88e39edc757643444de63d2df5 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Fri, 14 Apr 2023 11:20:36 +0200 Subject: [PATCH 4/4] std.crypto.aegis: support 256-bit tags (#15276) --- lib/std/crypto.zig | 2 + lib/std/crypto/aegis.zig | 434 ++++++++++++++++++++++----------------- 2 files changed, 243 insertions(+), 193 deletions(-) diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 6f0d1d9b6e92..ad59123a4a81 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -4,7 +4,9 @@ const root = @import("root"); pub const aead = struct { pub const aegis = struct { pub const Aegis128L = @import("crypto/aegis.zig").Aegis128L; + pub const Aegis128L_256 = @import("crypto/aegis.zig").Aegis128L_256; pub const Aegis256 = @import("crypto/aegis.zig").Aegis256; + pub const Aegis256_256 = @import("crypto/aegis.zig").Aegis256_256; }; pub const aes_gcm = struct { diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig index 4a030d7870ed..3dfaa50dcfc4 100644 --- a/lib/std/crypto/aegis.zig +++ b/lib/std/crypto/aegis.zig @@ -1,9 +1,39 @@ +//! AEGIS is a very fast authenticated encryption system built on top of the core AES function. +//! +//! The AEGIS-128L variant has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks. +//! The AEGIS-256 variant has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks. +//! +//! The AEGIS cipher family offers performance that significantly exceeds that of AES-GCM with +//! hardware support for parallelizable AES block encryption. +//! +//! Unlike with AES-GCM, nonces can be safely chosen at random with no practical limit when using AEGIS-256. +//! AEGIS-128L also allows for more messages to be safely encrypted when using random nonces. +//! +//! AEGIS is believed to be key-committing, making it a safer choice than most other AEADs +//! when the key has low entropy, or can be controlled by an attacker. +//! +//! Finally, leaking the state does not leak the key. +//! +//! https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ + const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; const AesBlock = std.crypto.core.aes.Block; const AuthenticationError = std.crypto.errors.AuthenticationError; +/// AEGIS-128L with a 128-bit authentication tag. +pub const Aegis128L = Aegis128LGeneric(128); + +/// AEGIS-128L with a 256-bit authentication tag. +pub const Aegis128L_256 = Aegis128LGeneric(256); + +/// AEGIS-256 with a 128-bit authentication tag. +pub const Aegis256 = Aegis256Generic(128); + +/// AEGIS-256 with a 256-bit authentication tag. +pub const Aegis256_256 = Aegis256Generic(256); + const State128L = struct { blocks: [8]AesBlock, @@ -72,7 +102,7 @@ const State128L = struct { state.update(msg0, msg1); } - fn mac(state: *State128L, adlen: usize, mlen: usize) [16]u8 { + fn mac(state: *State128L, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { const blocks = &state.blocks; var sizes: [16]u8 = undefined; mem.writeIntLittle(u64, sizes[0..8], adlen * 8); @@ -82,103 +112,109 @@ const State128L = struct { while (i < 7) : (i += 1) { state.update(tmp, tmp); } - return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]) - .xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(); + return switch (tag_bits) { + 128 => blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]) + .xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(), + 256 => tag: { + const t1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]); + const t2 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]); + break :tag t1.toBytes() ++ t2.toBytes(); + }, + else => unreachable, + }; } }; -/// AEGIS is a very fast authenticated encryption system built on top of the core AES function. -/// -/// The 128L variant of AEGIS has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks. -/// It was designed to fully exploit the parallelism and built-in AES support of recent Intel and ARM CPUs. -/// -/// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ -pub const Aegis128L = struct { - pub const tag_length = 16; - pub const nonce_length = 16; - pub const key_length = 16; - pub const block_length = 32; - - const State = State128L; - - /// c: ciphertext: output buffer should be of size m.len - /// tag: authentication tag: output MAC - /// m: message - /// ad: Associated Data - /// npub: public nonce - /// k: private key - pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { - assert(c.len == m.len); - var state = State128L.init(key, npub); - var src: [32]u8 align(16) = undefined; - var dst: [32]u8 align(16) = undefined; - var i: usize = 0; - while (i + 32 <= ad.len) : (i += 32) { - state.absorb(ad[i..][0..32]); - } - if (ad.len % 32 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); - state.absorb(&src); - } - i = 0; - while (i + 32 <= m.len) : (i += 32) { - state.enc(c[i..][0..32], m[i..][0..32]); - } - if (m.len % 32 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]); - state.enc(&dst, &src); - mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]); - } - tag.* = state.mac(ad.len, m.len); - } +fn Aegis128LGeneric(comptime tag_bits: u9) type { + comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits - /// m: message: output buffer should be of size c.len - /// c: ciphertext - /// tag: authentication tag - /// ad: Associated Data - /// npub: public nonce - /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { - assert(c.len == m.len); - var state = State128L.init(key, npub); - var src: [32]u8 align(16) = undefined; - var dst: [32]u8 align(16) = undefined; - var i: usize = 0; - while (i + 32 <= ad.len) : (i += 32) { - state.absorb(ad[i..][0..32]); - } - if (ad.len % 32 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); - state.absorb(&src); - } - i = 0; - while (i + 32 <= m.len) : (i += 32) { - state.dec(m[i..][0..32], c[i..][0..32]); - } - if (m.len % 32 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]); - state.dec(&dst, &src); - mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]); - mem.set(u8, dst[0 .. m.len % 32], 0); - const blocks = &state.blocks; - blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(dst[0..16])); - blocks[4] = blocks[4].xorBlocks(AesBlock.fromBytes(dst[16..32])); - } - const computed_tag = state.mac(ad.len, m.len); - var acc: u8 = 0; - for (computed_tag, 0..) |_, j| { - acc |= (computed_tag[j] ^ tag[j]); - } - if (acc != 0) { - @memset(m.ptr, undefined, m.len); - return error.AuthenticationFailed; + return struct { + pub const tag_length = tag_bits / 8; + pub const nonce_length = 16; + pub const key_length = 16; + pub const block_length = 32; + + const State = State128L; + + /// c: ciphertext: output buffer should be of size m.len + /// tag: authentication tag: output MAC + /// m: message + /// ad: Associated Data + /// npub: public nonce + /// k: private key + pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { + assert(c.len == m.len); + var state = State128L.init(key, npub); + var src: [32]u8 align(16) = undefined; + var dst: [32]u8 align(16) = undefined; + var i: usize = 0; + while (i + 32 <= ad.len) : (i += 32) { + state.absorb(ad[i..][0..32]); + } + if (ad.len % 32 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); + state.absorb(&src); + } + i = 0; + while (i + 32 <= m.len) : (i += 32) { + state.enc(c[i..][0..32], m[i..][0..32]); + } + if (m.len % 32 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]); + state.enc(&dst, &src); + mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]); + } + tag.* = state.mac(tag_bits, ad.len, m.len); + } + + /// m: message: output buffer should be of size c.len + /// c: ciphertext + /// tag: authentication tag + /// ad: Associated Data + /// npub: public nonce + /// k: private key + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { + assert(c.len == m.len); + var state = State128L.init(key, npub); + var src: [32]u8 align(16) = undefined; + var dst: [32]u8 align(16) = undefined; + var i: usize = 0; + while (i + 32 <= ad.len) : (i += 32) { + state.absorb(ad[i..][0..32]); + } + if (ad.len % 32 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); + state.absorb(&src); + } + i = 0; + while (i + 32 <= m.len) : (i += 32) { + state.dec(m[i..][0..32], c[i..][0..32]); + } + if (m.len % 32 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]); + state.dec(&dst, &src); + mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]); + mem.set(u8, dst[0 .. m.len % 32], 0); + const blocks = &state.blocks; + blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(dst[0..16])); + blocks[4] = blocks[4].xorBlocks(AesBlock.fromBytes(dst[16..32])); + } + const computed_tag = state.mac(tag_bits, ad.len, m.len); + var acc: u8 = 0; + for (computed_tag, 0..) |_, j| { + acc |= (computed_tag[j] ^ tag[j]); + } + if (acc != 0) { + @memset(m.ptr, undefined, m.len); + return error.AuthenticationFailed; + } } - } -}; + }; +} const State256 = struct { blocks: [6]AesBlock, @@ -243,7 +279,7 @@ const State256 = struct { state.update(msg); } - fn mac(state: *State256, adlen: usize, mlen: usize) [16]u8 { + fn mac(state: *State256, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { const blocks = &state.blocks; var sizes: [16]u8 = undefined; mem.writeIntLittle(u64, sizes[0..8], adlen * 8); @@ -253,8 +289,16 @@ const State256 = struct { while (i < 7) : (i += 1) { state.update(tmp); } - return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]) - .xorBlocks(blocks[5]).toBytes(); + return switch (tag_bits) { + 128 => blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]) + .xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(), + 256 => tag: { + const t1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]); + const t2 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]); + break :tag t1.toBytes() ++ t2.toBytes(); + }, + else => unreachable, + }; } }; @@ -263,111 +307,115 @@ const State256 = struct { /// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks. /// /// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ -pub const Aegis256 = struct { - pub const tag_length = 16; - pub const nonce_length = 32; - pub const key_length = 32; - pub const block_length = 16; - - const State = State256; - - /// c: ciphertext: output buffer should be of size m.len - /// tag: authentication tag: output MAC - /// m: message - /// ad: Associated Data - /// npub: public nonce - /// k: private key - pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { - assert(c.len == m.len); - var state = State256.init(key, npub); - var src: [16]u8 align(16) = undefined; - var dst: [16]u8 align(16) = undefined; - var i: usize = 0; - while (i + 16 <= ad.len) : (i += 16) { - state.enc(&dst, ad[i..][0..16]); - } - if (ad.len % 16 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); - state.enc(&dst, &src); - } - i = 0; - while (i + 16 <= m.len) : (i += 16) { - state.enc(c[i..][0..16], m[i..][0..16]); - } - if (m.len % 16 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]); - state.enc(&dst, &src); - mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]); - } - tag.* = state.mac(ad.len, m.len); - } +fn Aegis256Generic(comptime tag_bits: u9) type { + comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits - /// m: message: output buffer should be of size c.len - /// c: ciphertext - /// tag: authentication tag - /// ad: Associated Data - /// npub: public nonce - /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { - assert(c.len == m.len); - var state = State256.init(key, npub); - var src: [16]u8 align(16) = undefined; - var dst: [16]u8 align(16) = undefined; - var i: usize = 0; - while (i + 16 <= ad.len) : (i += 16) { - state.enc(&dst, ad[i..][0..16]); - } - if (ad.len % 16 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); - state.enc(&dst, &src); - } - i = 0; - while (i + 16 <= m.len) : (i += 16) { - state.dec(m[i..][0..16], c[i..][0..16]); - } - if (m.len % 16 != 0) { - mem.set(u8, src[0..], 0); - mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]); - state.dec(&dst, &src); - mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]); - mem.set(u8, dst[0 .. m.len % 16], 0); - const blocks = &state.blocks; - blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(&dst)); - } - const computed_tag = state.mac(ad.len, m.len); - var acc: u8 = 0; - for (computed_tag, 0..) |_, j| { - acc |= (computed_tag[j] ^ tag[j]); - } - if (acc != 0) { - @memset(m.ptr, undefined, m.len); - return error.AuthenticationFailed; + return struct { + pub const tag_length = tag_bits / 8; + pub const nonce_length = 32; + pub const key_length = 32; + pub const block_length = 16; + + const State = State256; + + /// c: ciphertext: output buffer should be of size m.len + /// tag: authentication tag: output MAC + /// m: message + /// ad: Associated Data + /// npub: public nonce + /// k: private key + pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { + assert(c.len == m.len); + var state = State256.init(key, npub); + var src: [16]u8 align(16) = undefined; + var dst: [16]u8 align(16) = undefined; + var i: usize = 0; + while (i + 16 <= ad.len) : (i += 16) { + state.enc(&dst, ad[i..][0..16]); + } + if (ad.len % 16 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); + state.enc(&dst, &src); + } + i = 0; + while (i + 16 <= m.len) : (i += 16) { + state.enc(c[i..][0..16], m[i..][0..16]); + } + if (m.len % 16 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]); + state.enc(&dst, &src); + mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]); + } + tag.* = state.mac(tag_bits, ad.len, m.len); + } + + /// m: message: output buffer should be of size c.len + /// c: ciphertext + /// tag: authentication tag + /// ad: Associated Data + /// npub: public nonce + /// k: private key + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { + assert(c.len == m.len); + var state = State256.init(key, npub); + var src: [16]u8 align(16) = undefined; + var dst: [16]u8 align(16) = undefined; + var i: usize = 0; + while (i + 16 <= ad.len) : (i += 16) { + state.enc(&dst, ad[i..][0..16]); + } + if (ad.len % 16 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); + state.enc(&dst, &src); + } + i = 0; + while (i + 16 <= m.len) : (i += 16) { + state.dec(m[i..][0..16], c[i..][0..16]); + } + if (m.len % 16 != 0) { + mem.set(u8, src[0..], 0); + mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]); + state.dec(&dst, &src); + mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]); + mem.set(u8, dst[0 .. m.len % 16], 0); + const blocks = &state.blocks; + blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(&dst)); + } + const computed_tag = state.mac(tag_bits, ad.len, m.len); + var acc: u8 = 0; + for (computed_tag, 0..) |_, j| { + acc |= (computed_tag[j] ^ tag[j]); + } + if (acc != 0) { + @memset(m.ptr, undefined, m.len); + return error.AuthenticationFailed; + } } - } -}; + }; +} -/// The AEGIS-128L message authentication function outputs 128 bit tags. +/// The `Aegis128LMac` message authentication function outputs 256 bit tags. /// In addition to being extremely fast, its large state, non-linearity /// and non-invertibility provides the following properties: /// - 128 bit security, stronger than GHash/Polyval/Poly1305. /// - Recovering the secret key from the state would require ~2^128 attempts, /// which is infeasible for any practical adversary. /// - It has a large security margin against internal collisions. -pub const Aegis128LMac = AegisMac(Aegis128L); +pub const Aegis128LMac = AegisMac(Aegis128L_256); -/// The AEGIS-256 message authentication function has a 256-bit key size, -/// but outputs 128 bit tags. Unless theoretical multi-target attacks are a +/// The `Aegis256Mac` message authentication function has a 256-bit key size, +/// and outputs 256 bit tags. Unless theoretical multi-target attacks are a /// concern, the AEGIS-128L variant should be preferred. /// AEGIS' large state, non-linearity and non-invertibility provides the /// following properties: -/// - 128 bit security, stronger than GHash/Polyval/Poly1305. -/// - Recovering the secret key from the state would require ~2^128 attempts, +/// - 256 bit security against forgery. +/// - Recovering the secret key from the state would require ~2^256 attempts, /// which is infeasible for any practical adversary. /// - It has a large security margin against internal collisions. -pub const Aegis256Mac = AegisMac(Aegis256); +pub const Aegis256Mac = AegisMac(Aegis256_256); fn AegisMac(comptime T: type) type { return struct { @@ -420,7 +468,7 @@ fn AegisMac(comptime T: type) type { mem.copy(u8, pad[0..], self.buf[0..self.off]); self.state.absorb(&pad); } - out.* = self.state.mac(self.msg_len, 0); + out.* = self.state.mac(T.tag_length * 8, self.msg_len, 0); } /// Return an authentication tag for a message and a key @@ -572,23 +620,23 @@ test "Aegis MAC" { st.update(msg[0..32]); st.update(msg[32..]); st.final(&tag); - try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); + try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); st = st_init; st.update(msg[0..31]); st.update(msg[31..]); st.final(&tag); - try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); + try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); st = st_init; st.update(msg[0..14]); st.update(msg[14..30]); st.update(msg[30..]); st.final(&tag); - try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); + try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); var empty: [0]u8 = undefined; - const nonce = [_]u8{0x00} ** Aegis128L.nonce_length; - Aegis128L.encrypt(&empty, &tag, &empty, &msg, nonce, key); - try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); + const nonce = [_]u8{0x00} ** Aegis128L_256.nonce_length; + Aegis128L_256.encrypt(&empty, &tag, &empty, &msg, nonce, key); + try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); }