| ... | ... | @@ -1,9 +1,39 @@ |
| 1 | //! AEGIS is a very fast authenticated encryption system built on top of the core AES function. |
| 2 | //! |
| 3 | //! The AEGIS-128L variant has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks. |
| 4 | //! The AEGIS-256 variant has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks. |
| 5 | //! |
| 6 | //! The AEGIS cipher family offers performance that significantly exceeds that of AES-GCM with |
| 7 | //! hardware support for parallelizable AES block encryption. |
| 8 | //! |
| 9 | //! Unlike with AES-GCM, nonces can be safely chosen at random with no practical limit when using AEGIS-256. |
| 10 | //! AEGIS-128L also allows for more messages to be safely encrypted when using random nonces. |
| 11 | //! |
| 12 | //! AEGIS is believed to be key-committing, making it a safer choice than most other AEADs |
| 13 | //! when the key has low entropy, or can be controlled by an attacker. |
| 14 | //! |
| 15 | //! Finally, leaking the state does not leak the key. |
| 16 | //! |
| 17 | //! https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ |
| 18 | |
| 1 | 19 | const std = @import("std"); |
| 2 | 20 | const mem = std.mem; |
| 3 | 21 | const assert = std.debug.assert; |
| 4 | 22 | const AesBlock = std.crypto.core.aes.Block; |
| 5 | 23 | const AuthenticationError = std.crypto.errors.AuthenticationError; |
| 6 | 24 | |
| 25 | /// AEGIS-128L with a 128-bit authentication tag. |
| 26 | pub const Aegis128L = Aegis128LGeneric(128); |
| 27 | |
| 28 | /// AEGIS-128L with a 256-bit authentication tag. |
| 29 | pub const Aegis128L_256 = Aegis128LGeneric(256); |
| 30 | |
| 31 | /// AEGIS-256 with a 128-bit authentication tag. |
| 32 | pub const Aegis256 = Aegis256Generic(128); |
| 33 | |
| 34 | /// AEGIS-256 with a 256-bit authentication tag. |
| 35 | pub const Aegis256_256 = Aegis256Generic(256); |
| 36 | |
| 7 | 37 | const State128L = struct { |
| 8 | 38 | blocks: [8]AesBlock, |
| 9 | 39 | |
| ... | ... | @@ -72,7 +102,7 @@ const State128L = struct { |
| 72 | 102 | state.update(msg0, msg1); |
| 73 | 103 | } |
| 74 | 104 | |
| 75 | | fn mac(state: *State128L, adlen: usize, mlen: usize) [16]u8 { |
| 105 | fn mac(state: *State128L, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { |
| 76 | 106 | const blocks = &state.blocks; |
| 77 | 107 | var sizes: [16]u8 = undefined; |
| 78 | 108 | mem.writeIntLittle(u64, sizes[0..8], adlen * 8); |
| ... | ... | @@ -82,103 +112,109 @@ const State128L = struct { |
| 82 | 112 | while (i < 7) : (i += 1) { |
| 83 | 113 | state.update(tmp, tmp); |
| 84 | 114 | } |
| 85 | | return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]) |
| 86 | | .xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(); |
| 115 | return switch (tag_bits) { |
| 116 | 128 => blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]) |
| 117 | .xorBlocks(blocks[4]).xorBlocks(blocks[5]).xorBlocks(blocks[6]).toBytes(), |
| 118 | 256 => tag: { |
| 119 | const t1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]); |
| 120 | const t2 = blocks[4].xorBlocks(blocks[5]).xorBlocks(blocks[6]).xorBlocks(blocks[7]); |
| 121 | break :tag t1.toBytes() ++ t2.toBytes(); |
| 122 | }, |
| 123 | else => unreachable, |
| 124 | }; |
| 87 | 125 | } |
| 88 | 126 | }; |
| 89 | 127 | |
| 90 | | /// AEGIS is a very fast authenticated encryption system built on top of the core AES function. |
| 91 | | /// |
| 92 | | /// The 128L variant of AEGIS has a 128 bit key, a 128 bit nonce, and processes 256 bit message blocks. |
| 93 | | /// It was designed to fully exploit the parallelism and built-in AES support of recent Intel and ARM CPUs. |
| 94 | | /// |
| 95 | | /// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ |
| 96 | | pub const Aegis128L = struct { |
| 97 | | pub const tag_length = 16; |
| 98 | | pub const nonce_length = 16; |
| 99 | | pub const key_length = 16; |
| 100 | | pub const block_length = 32; |
| 101 | | |
| 102 | | const State = State128L; |
| 103 | | |
| 104 | | /// c: ciphertext: output buffer should be of size m.len |
| 105 | | /// tag: authentication tag: output MAC |
| 106 | | /// m: message |
| 107 | | /// ad: Associated Data |
| 108 | | /// npub: public nonce |
| 109 | | /// k: private key |
| 110 | | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { |
| 111 | | assert(c.len == m.len); |
| 112 | | var state = State128L.init(key, npub); |
| 113 | | var src: [32]u8 align(16) = undefined; |
| 114 | | var dst: [32]u8 align(16) = undefined; |
| 115 | | var i: usize = 0; |
| 116 | | while (i + 32 <= ad.len) : (i += 32) { |
| 117 | | state.absorb(ad[i..][0..32]); |
| 118 | | } |
| 119 | | if (ad.len % 32 != 0) { |
| 120 | | mem.set(u8, src[0..], 0); |
| 121 | | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); |
| 122 | | state.absorb(&src); |
| 123 | | } |
| 124 | | i = 0; |
| 125 | | while (i + 32 <= m.len) : (i += 32) { |
| 126 | | state.enc(c[i..][0..32], m[i..][0..32]); |
| 127 | | } |
| 128 | | if (m.len % 32 != 0) { |
| 129 | | mem.set(u8, src[0..], 0); |
| 130 | | mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]); |
| 131 | | state.enc(&dst, &src); |
| 132 | | mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]); |
| 133 | | } |
| 134 | | tag.* = state.mac(ad.len, m.len); |
| 135 | | } |
| 128 | fn Aegis128LGeneric(comptime tag_bits: u9) type { |
| 129 | comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits |
| 136 | 130 | |
| 137 | | /// m: message: output buffer should be of size c.len |
| 138 | | /// c: ciphertext |
| 139 | | /// tag: authentication tag |
| 140 | | /// ad: Associated Data |
| 141 | | /// npub: public nonce |
| 142 | | /// k: private key |
| 143 | | 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 { |
| 144 | | assert(c.len == m.len); |
| 145 | | var state = State128L.init(key, npub); |
| 146 | | var src: [32]u8 align(16) = undefined; |
| 147 | | var dst: [32]u8 align(16) = undefined; |
| 148 | | var i: usize = 0; |
| 149 | | while (i + 32 <= ad.len) : (i += 32) { |
| 150 | | state.absorb(ad[i..][0..32]); |
| 151 | | } |
| 152 | | if (ad.len % 32 != 0) { |
| 153 | | mem.set(u8, src[0..], 0); |
| 154 | | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); |
| 155 | | state.absorb(&src); |
| 156 | | } |
| 157 | | i = 0; |
| 158 | | while (i + 32 <= m.len) : (i += 32) { |
| 159 | | state.dec(m[i..][0..32], c[i..][0..32]); |
| 160 | | } |
| 161 | | if (m.len % 32 != 0) { |
| 162 | | mem.set(u8, src[0..], 0); |
| 163 | | mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]); |
| 164 | | state.dec(&dst, &src); |
| 165 | | mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]); |
| 166 | | mem.set(u8, dst[0 .. m.len % 32], 0); |
| 167 | | const blocks = &state.blocks; |
| 168 | | blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(dst[0..16])); |
| 169 | | blocks[4] = blocks[4].xorBlocks(AesBlock.fromBytes(dst[16..32])); |
| 170 | | } |
| 171 | | const computed_tag = state.mac(ad.len, m.len); |
| 172 | | var acc: u8 = 0; |
| 173 | | for (computed_tag, 0..) |_, j| { |
| 174 | | acc |= (computed_tag[j] ^ tag[j]); |
| 175 | | } |
| 176 | | if (acc != 0) { |
| 177 | | @memset(m.ptr, undefined, m.len); |
| 178 | | return error.AuthenticationFailed; |
| 131 | return struct { |
| 132 | pub const tag_length = tag_bits / 8; |
| 133 | pub const nonce_length = 16; |
| 134 | pub const key_length = 16; |
| 135 | pub const block_length = 32; |
| 136 | |
| 137 | const State = State128L; |
| 138 | |
| 139 | /// c: ciphertext: output buffer should be of size m.len |
| 140 | /// tag: authentication tag: output MAC |
| 141 | /// m: message |
| 142 | /// ad: Associated Data |
| 143 | /// npub: public nonce |
| 144 | /// k: private key |
| 145 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { |
| 146 | assert(c.len == m.len); |
| 147 | var state = State128L.init(key, npub); |
| 148 | var src: [32]u8 align(16) = undefined; |
| 149 | var dst: [32]u8 align(16) = undefined; |
| 150 | var i: usize = 0; |
| 151 | while (i + 32 <= ad.len) : (i += 32) { |
| 152 | state.absorb(ad[i..][0..32]); |
| 153 | } |
| 154 | if (ad.len % 32 != 0) { |
| 155 | mem.set(u8, src[0..], 0); |
| 156 | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); |
| 157 | state.absorb(&src); |
| 158 | } |
| 159 | i = 0; |
| 160 | while (i + 32 <= m.len) : (i += 32) { |
| 161 | state.enc(c[i..][0..32], m[i..][0..32]); |
| 162 | } |
| 163 | if (m.len % 32 != 0) { |
| 164 | mem.set(u8, src[0..], 0); |
| 165 | mem.copy(u8, src[0 .. m.len % 32], m[i .. i + m.len % 32]); |
| 166 | state.enc(&dst, &src); |
| 167 | mem.copy(u8, c[i .. i + m.len % 32], dst[0 .. m.len % 32]); |
| 168 | } |
| 169 | tag.* = state.mac(tag_bits, ad.len, m.len); |
| 170 | } |
| 171 | |
| 172 | /// m: message: output buffer should be of size c.len |
| 173 | /// c: ciphertext |
| 174 | /// tag: authentication tag |
| 175 | /// ad: Associated Data |
| 176 | /// npub: public nonce |
| 177 | /// k: private key |
| 178 | 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 { |
| 179 | assert(c.len == m.len); |
| 180 | var state = State128L.init(key, npub); |
| 181 | var src: [32]u8 align(16) = undefined; |
| 182 | var dst: [32]u8 align(16) = undefined; |
| 183 | var i: usize = 0; |
| 184 | while (i + 32 <= ad.len) : (i += 32) { |
| 185 | state.absorb(ad[i..][0..32]); |
| 186 | } |
| 187 | if (ad.len % 32 != 0) { |
| 188 | mem.set(u8, src[0..], 0); |
| 189 | mem.copy(u8, src[0 .. ad.len % 32], ad[i .. i + ad.len % 32]); |
| 190 | state.absorb(&src); |
| 191 | } |
| 192 | i = 0; |
| 193 | while (i + 32 <= m.len) : (i += 32) { |
| 194 | state.dec(m[i..][0..32], c[i..][0..32]); |
| 195 | } |
| 196 | if (m.len % 32 != 0) { |
| 197 | mem.set(u8, src[0..], 0); |
| 198 | mem.copy(u8, src[0 .. m.len % 32], c[i .. i + m.len % 32]); |
| 199 | state.dec(&dst, &src); |
| 200 | mem.copy(u8, m[i .. i + m.len % 32], dst[0 .. m.len % 32]); |
| 201 | mem.set(u8, dst[0 .. m.len % 32], 0); |
| 202 | const blocks = &state.blocks; |
| 203 | blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(dst[0..16])); |
| 204 | blocks[4] = blocks[4].xorBlocks(AesBlock.fromBytes(dst[16..32])); |
| 205 | } |
| 206 | const computed_tag = state.mac(tag_bits, ad.len, m.len); |
| 207 | var acc: u8 = 0; |
| 208 | for (computed_tag, 0..) |_, j| { |
| 209 | acc |= (computed_tag[j] ^ tag[j]); |
| 210 | } |
| 211 | if (acc != 0) { |
| 212 | @memset(m.ptr, undefined, m.len); |
| 213 | return error.AuthenticationFailed; |
| 214 | } |
| 179 | 215 | } |
| 180 | | } |
| 181 | | }; |
| 216 | }; |
| 217 | } |
| 182 | 218 | |
| 183 | 219 | const State256 = struct { |
| 184 | 220 | blocks: [6]AesBlock, |
| ... | ... | @@ -243,7 +279,7 @@ const State256 = struct { |
| 243 | 279 | state.update(msg); |
| 244 | 280 | } |
| 245 | 281 | |
| 246 | | fn mac(state: *State256, adlen: usize, mlen: usize) [16]u8 { |
| 282 | fn mac(state: *State256, comptime tag_bits: u9, adlen: usize, mlen: usize) [tag_bits / 8]u8 { |
| 247 | 283 | const blocks = &state.blocks; |
| 248 | 284 | var sizes: [16]u8 = undefined; |
| 249 | 285 | mem.writeIntLittle(u64, sizes[0..8], adlen * 8); |
| ... | ... | @@ -253,8 +289,16 @@ const State256 = struct { |
| 253 | 289 | while (i < 7) : (i += 1) { |
| 254 | 290 | state.update(tmp); |
| 255 | 291 | } |
| 256 | | return blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]).xorBlocks(blocks[4]) |
| 257 | | .xorBlocks(blocks[5]).toBytes(); |
| 292 | return switch (tag_bits) { |
| 293 | 128 => blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]).xorBlocks(blocks[3]) |
| 294 | .xorBlocks(blocks[4]).xorBlocks(blocks[5]).toBytes(), |
| 295 | 256 => tag: { |
| 296 | const t1 = blocks[0].xorBlocks(blocks[1]).xorBlocks(blocks[2]); |
| 297 | const t2 = blocks[3].xorBlocks(blocks[4]).xorBlocks(blocks[5]); |
| 298 | break :tag t1.toBytes() ++ t2.toBytes(); |
| 299 | }, |
| 300 | else => unreachable, |
| 301 | }; |
| 258 | 302 | } |
| 259 | 303 | }; |
| 260 | 304 | |
| ... | ... | @@ -263,111 +307,115 @@ const State256 = struct { |
| 263 | 307 | /// The 256 bit variant of AEGIS has a 256 bit key, a 256 bit nonce, and processes 128 bit message blocks. |
| 264 | 308 | /// |
| 265 | 309 | /// https://datatracker.ietf.org/doc/draft-irtf-cfrg-aegis-aead/ |
| 266 | | pub const Aegis256 = struct { |
| 267 | | pub const tag_length = 16; |
| 268 | | pub const nonce_length = 32; |
| 269 | | pub const key_length = 32; |
| 270 | | pub const block_length = 16; |
| 271 | | |
| 272 | | const State = State256; |
| 273 | | |
| 274 | | /// c: ciphertext: output buffer should be of size m.len |
| 275 | | /// tag: authentication tag: output MAC |
| 276 | | /// m: message |
| 277 | | /// ad: Associated Data |
| 278 | | /// npub: public nonce |
| 279 | | /// k: private key |
| 280 | | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { |
| 281 | | assert(c.len == m.len); |
| 282 | | var state = State256.init(key, npub); |
| 283 | | var src: [16]u8 align(16) = undefined; |
| 284 | | var dst: [16]u8 align(16) = undefined; |
| 285 | | var i: usize = 0; |
| 286 | | while (i + 16 <= ad.len) : (i += 16) { |
| 287 | | state.enc(&dst, ad[i..][0..16]); |
| 288 | | } |
| 289 | | if (ad.len % 16 != 0) { |
| 290 | | mem.set(u8, src[0..], 0); |
| 291 | | mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); |
| 292 | | state.enc(&dst, &src); |
| 293 | | } |
| 294 | | i = 0; |
| 295 | | while (i + 16 <= m.len) : (i += 16) { |
| 296 | | state.enc(c[i..][0..16], m[i..][0..16]); |
| 297 | | } |
| 298 | | if (m.len % 16 != 0) { |
| 299 | | mem.set(u8, src[0..], 0); |
| 300 | | mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]); |
| 301 | | state.enc(&dst, &src); |
| 302 | | mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]); |
| 303 | | } |
| 304 | | tag.* = state.mac(ad.len, m.len); |
| 305 | | } |
| 310 | fn Aegis256Generic(comptime tag_bits: u9) type { |
| 311 | comptime assert(tag_bits == 128 or tag_bits == 256); // tag must be 128 or 256 bits |
| 306 | 312 | |
| 307 | | /// m: message: output buffer should be of size c.len |
| 308 | | /// c: ciphertext |
| 309 | | /// tag: authentication tag |
| 310 | | /// ad: Associated Data |
| 311 | | /// npub: public nonce |
| 312 | | /// k: private key |
| 313 | | 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 { |
| 314 | | assert(c.len == m.len); |
| 315 | | var state = State256.init(key, npub); |
| 316 | | var src: [16]u8 align(16) = undefined; |
| 317 | | var dst: [16]u8 align(16) = undefined; |
| 318 | | var i: usize = 0; |
| 319 | | while (i + 16 <= ad.len) : (i += 16) { |
| 320 | | state.enc(&dst, ad[i..][0..16]); |
| 321 | | } |
| 322 | | if (ad.len % 16 != 0) { |
| 323 | | mem.set(u8, src[0..], 0); |
| 324 | | mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); |
| 325 | | state.enc(&dst, &src); |
| 326 | | } |
| 327 | | i = 0; |
| 328 | | while (i + 16 <= m.len) : (i += 16) { |
| 329 | | state.dec(m[i..][0..16], c[i..][0..16]); |
| 330 | | } |
| 331 | | if (m.len % 16 != 0) { |
| 332 | | mem.set(u8, src[0..], 0); |
| 333 | | mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]); |
| 334 | | state.dec(&dst, &src); |
| 335 | | mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]); |
| 336 | | mem.set(u8, dst[0 .. m.len % 16], 0); |
| 337 | | const blocks = &state.blocks; |
| 338 | | blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(&dst)); |
| 339 | | } |
| 340 | | const computed_tag = state.mac(ad.len, m.len); |
| 341 | | var acc: u8 = 0; |
| 342 | | for (computed_tag, 0..) |_, j| { |
| 343 | | acc |= (computed_tag[j] ^ tag[j]); |
| 344 | | } |
| 345 | | if (acc != 0) { |
| 346 | | @memset(m.ptr, undefined, m.len); |
| 347 | | return error.AuthenticationFailed; |
| 313 | return struct { |
| 314 | pub const tag_length = tag_bits / 8; |
| 315 | pub const nonce_length = 32; |
| 316 | pub const key_length = 32; |
| 317 | pub const block_length = 16; |
| 318 | |
| 319 | const State = State256; |
| 320 | |
| 321 | /// c: ciphertext: output buffer should be of size m.len |
| 322 | /// tag: authentication tag: output MAC |
| 323 | /// m: message |
| 324 | /// ad: Associated Data |
| 325 | /// npub: public nonce |
| 326 | /// k: private key |
| 327 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { |
| 328 | assert(c.len == m.len); |
| 329 | var state = State256.init(key, npub); |
| 330 | var src: [16]u8 align(16) = undefined; |
| 331 | var dst: [16]u8 align(16) = undefined; |
| 332 | var i: usize = 0; |
| 333 | while (i + 16 <= ad.len) : (i += 16) { |
| 334 | state.enc(&dst, ad[i..][0..16]); |
| 335 | } |
| 336 | if (ad.len % 16 != 0) { |
| 337 | mem.set(u8, src[0..], 0); |
| 338 | mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); |
| 339 | state.enc(&dst, &src); |
| 340 | } |
| 341 | i = 0; |
| 342 | while (i + 16 <= m.len) : (i += 16) { |
| 343 | state.enc(c[i..][0..16], m[i..][0..16]); |
| 344 | } |
| 345 | if (m.len % 16 != 0) { |
| 346 | mem.set(u8, src[0..], 0); |
| 347 | mem.copy(u8, src[0 .. m.len % 16], m[i .. i + m.len % 16]); |
| 348 | state.enc(&dst, &src); |
| 349 | mem.copy(u8, c[i .. i + m.len % 16], dst[0 .. m.len % 16]); |
| 350 | } |
| 351 | tag.* = state.mac(tag_bits, ad.len, m.len); |
| 352 | } |
| 353 | |
| 354 | /// m: message: output buffer should be of size c.len |
| 355 | /// c: ciphertext |
| 356 | /// tag: authentication tag |
| 357 | /// ad: Associated Data |
| 358 | /// npub: public nonce |
| 359 | /// k: private key |
| 360 | 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 { |
| 361 | assert(c.len == m.len); |
| 362 | var state = State256.init(key, npub); |
| 363 | var src: [16]u8 align(16) = undefined; |
| 364 | var dst: [16]u8 align(16) = undefined; |
| 365 | var i: usize = 0; |
| 366 | while (i + 16 <= ad.len) : (i += 16) { |
| 367 | state.enc(&dst, ad[i..][0..16]); |
| 368 | } |
| 369 | if (ad.len % 16 != 0) { |
| 370 | mem.set(u8, src[0..], 0); |
| 371 | mem.copy(u8, src[0 .. ad.len % 16], ad[i .. i + ad.len % 16]); |
| 372 | state.enc(&dst, &src); |
| 373 | } |
| 374 | i = 0; |
| 375 | while (i + 16 <= m.len) : (i += 16) { |
| 376 | state.dec(m[i..][0..16], c[i..][0..16]); |
| 377 | } |
| 378 | if (m.len % 16 != 0) { |
| 379 | mem.set(u8, src[0..], 0); |
| 380 | mem.copy(u8, src[0 .. m.len % 16], c[i .. i + m.len % 16]); |
| 381 | state.dec(&dst, &src); |
| 382 | mem.copy(u8, m[i .. i + m.len % 16], dst[0 .. m.len % 16]); |
| 383 | mem.set(u8, dst[0 .. m.len % 16], 0); |
| 384 | const blocks = &state.blocks; |
| 385 | blocks[0] = blocks[0].xorBlocks(AesBlock.fromBytes(&dst)); |
| 386 | } |
| 387 | const computed_tag = state.mac(tag_bits, ad.len, m.len); |
| 388 | var acc: u8 = 0; |
| 389 | for (computed_tag, 0..) |_, j| { |
| 390 | acc |= (computed_tag[j] ^ tag[j]); |
| 391 | } |
| 392 | if (acc != 0) { |
| 393 | @memset(m.ptr, undefined, m.len); |
| 394 | return error.AuthenticationFailed; |
| 395 | } |
| 348 | 396 | } |
| 349 | | } |
| 350 | | }; |
| 397 | }; |
| 398 | } |
| 351 | 399 | |
| 352 | | /// The AEGIS-128L message authentication function outputs 128 bit tags. |
| 400 | /// The `Aegis128LMac` message authentication function outputs 256 bit tags. |
| 353 | 401 | /// In addition to being extremely fast, its large state, non-linearity |
| 354 | 402 | /// and non-invertibility provides the following properties: |
| 355 | 403 | /// - 128 bit security, stronger than GHash/Polyval/Poly1305. |
| 356 | 404 | /// - Recovering the secret key from the state would require ~2^128 attempts, |
| 357 | 405 | /// which is infeasible for any practical adversary. |
| 358 | 406 | /// - It has a large security margin against internal collisions. |
| 359 | | pub const Aegis128LMac = AegisMac(Aegis128L); |
| 407 | pub const Aegis128LMac = AegisMac(Aegis128L_256); |
| 360 | 408 | |
| 361 | | /// The AEGIS-256 message authentication function has a 256-bit key size, |
| 362 | | /// but outputs 128 bit tags. Unless theoretical multi-target attacks are a |
| 409 | /// The `Aegis256Mac` message authentication function has a 256-bit key size, |
| 410 | /// and outputs 256 bit tags. Unless theoretical multi-target attacks are a |
| 363 | 411 | /// concern, the AEGIS-128L variant should be preferred. |
| 364 | 412 | /// AEGIS' large state, non-linearity and non-invertibility provides the |
| 365 | 413 | /// following properties: |
| 366 | | /// - 128 bit security, stronger than GHash/Polyval/Poly1305. |
| 367 | | /// - Recovering the secret key from the state would require ~2^128 attempts, |
| 414 | /// - 256 bit security against forgery. |
| 415 | /// - Recovering the secret key from the state would require ~2^256 attempts, |
| 368 | 416 | /// which is infeasible for any practical adversary. |
| 369 | 417 | /// - It has a large security margin against internal collisions. |
| 370 | | pub const Aegis256Mac = AegisMac(Aegis256); |
| 418 | pub const Aegis256Mac = AegisMac(Aegis256_256); |
| 371 | 419 | |
| 372 | 420 | fn AegisMac(comptime T: type) type { |
| 373 | 421 | return struct { |
| ... | ... | @@ -420,7 +468,7 @@ fn AegisMac(comptime T: type) type { |
| 420 | 468 | mem.copy(u8, pad[0..], self.buf[0..self.off]); |
| 421 | 469 | self.state.absorb(&pad); |
| 422 | 470 | } |
| 423 | | out.* = self.state.mac(self.msg_len, 0); |
| 471 | out.* = self.state.mac(T.tag_length * 8, self.msg_len, 0); |
| 424 | 472 | } |
| 425 | 473 | |
| 426 | 474 | /// Return an authentication tag for a message and a key |
| ... | ... | @@ -572,23 +620,23 @@ test "Aegis MAC" { |
| 572 | 620 | st.update(msg[0..32]); |
| 573 | 621 | st.update(msg[32..]); |
| 574 | 622 | st.final(&tag); |
| 575 | | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| 623 | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 576 | 624 | |
| 577 | 625 | st = st_init; |
| 578 | 626 | st.update(msg[0..31]); |
| 579 | 627 | st.update(msg[31..]); |
| 580 | 628 | st.final(&tag); |
| 581 | | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| 629 | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 582 | 630 | |
| 583 | 631 | st = st_init; |
| 584 | 632 | st.update(msg[0..14]); |
| 585 | 633 | st.update(msg[14..30]); |
| 586 | 634 | st.update(msg[30..]); |
| 587 | 635 | st.final(&tag); |
| 588 | | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| 636 | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 589 | 637 | |
| 590 | 638 | var empty: [0]u8 = undefined; |
| 591 | | const nonce = [_]u8{0x00} ** Aegis128L.nonce_length; |
| 592 | | Aegis128L.encrypt(&empty, &tag, &empty, &msg, nonce, key); |
| 593 | | try htest.assertEqual("b4e8e46cee04a401ec67bad73df4aa60", &tag); |
| 639 | const nonce = [_]u8{0x00} ** Aegis128L_256.nonce_length; |
| 640 | Aegis128L_256.encrypt(&empty, &tag, &empty, &msg, nonce, key); |
| 641 | try htest.assertEqual("f8840849602738d81037cbaa0f584ea95759e2ac60263ce77346bcdc79fe4319", &tag); |
| 594 | 642 | } |