From d14c15669338e4b3db259bf696f7b6c7e1e46584 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 31 Dec 2024 01:02:52 +0100 Subject: [PATCH] Address suggestions from Samuel's final proofreading --- draft-irtf-cfrg-aegis-aead.md | 160 ++++++++++++------------ reference-implementations/aegis128l.zig | 6 +- reference-implementations/aegis128x.zig | 6 +- 3 files changed, 87 insertions(+), 85 deletions(-) diff --git a/draft-irtf-cfrg-aegis-aead.md b/draft-irtf-cfrg-aegis-aead.md index 500181a..36325d9 100644 --- a/draft-irtf-cfrg-aegis-aead.md +++ b/draft-irtf-cfrg-aegis-aead.md @@ -317,19 +317,19 @@ Therefore, this document specifies the following variants: All variants are inverse-free and constructed from the AES encryption round function {{!FIPS-AES=FIPS.197.2001}}. -The AEGIS cipher family offers performance that significantly exceeds that of AES-GCM on CPUs with AES instructions. Similarly, software implementations not using AES instructions can also be faster, although to a lesser extent. +The AEGIS cipher family offers performance that significantly exceeds AES-GCM on CPUs with AES instructions. Similarly, software implementations not using AES instructions can also be faster, although to a lesser extent. Unlike with AES-GCM, nonces can be safely chosen at random with no practical limit when using AEGIS-256 and AEGIS-256X. AEGIS-128L and AEGIS-128X also allow for more messages to be safely encrypted when using random nonces. With some existing AEAD schemes, such as AES-GCM, an attacker can generate a ciphertext that successfully decrypts under multiple different keys (a partitioning oracle attack) {{LGR21}}. This ability to craft a (ciphertext, authentication tag) pair that verifies under multiple keys significantly reduces the number of required interactions with the oracle to perform an exhaustive search, making it practical if the key space is small. For example, with password-based encryption, an attacker can guess a large number of passwords at a time by recursively submitting such a ciphertext to an oracle, which speeds up a password search by reducing it to a binary search. -In AEGIS, finding distinct (key, nonce) pairs that successfully decrypt a given (associated data, ciphertext, authentication tag) tuple is believed to have a complexity that depends on the tag size. A 128-bit tag provides 64-bit committing security, which is generally acceptable for interactive protocols. With a 256-bit tag, finding a collision becomes impractical. +With AEGIS, finding distinct (key, nonce) pairs that successfully decrypt a given (associated data, ciphertext, authentication tag) tuple is believed to have a complexity that depends on the tag size. A 128-bit tag provides 64-bit committing security, which is generally acceptable for interactive protocols. With a 256-bit tag, finding a collision becomes impractical. Unlike most other AES-based AEAD constructions, leaking a state does not leak the key or previous states. Finally, an AEGIS key is not required after the initialization function, and there is no key schedule. Thus, ephemeral keys can be erased from memory before any data has been encrypted or decrypted, mitigating cold boot attacks. -Note that an earlier version of Hongjun Wu and Bart Preneel's paper introducing AEGIS specified AEGIS-128L and AEGIS-256 sporting differences with regards to the computation of the authentication tag and the number of state updates in the `Finalize()` function. We follow the specification of {{AEGIS}}, which can be found in the References section of this document. +Note that an earlier version of Hongjun Wu and Bart Preneel's paper introducing AEGIS specified AEGIS-128L and AEGIS-256 with a different `Finalize` function. We follow the specification of {{AEGIS}}, which can be found in the References section of this document. # Conventions and Definitions @@ -347,17 +347,17 @@ Primitives: - `a mod b`: the remainder of the Euclidean division between `a` as the dividend and `b` as the divisor. - `LE64(x)`: the little-endian encoding of unsigned 64-bit integer `x`. - `ZeroPad(x, n)`: padding operation. Trailing zeros are concatenated to `x` until the total length is a multiple of `n` bits. -- `Truncate(x, n)`: truncation operation. The first `n` bits of `x` are kept. +- `Truncate(x, n)`: truncation operation. Returns the first `n` bits of `x`. - `Split(x, n)`: splitting operation. `x` is split into `n`-bit blocks, ignoring partial blocks. - `Tail(x, n)`: returns the last `n` bits of `x`. -- `AESRound(in, rk)`: a single round of the AES encryption round function, which is the composition of the `SubBytes`, `ShiftRows`, `MixColums` and `AddRoundKey` transformations, as defined in section 5 of {{FIPS-AES}}. Here, `in` is the 128-bit AES input state, and `rk` is the 128-bit round key. +- `AESRound(in, rk)`: a single round of the AES encryption round function, which is the composition of the `SubBytes`, `ShiftRows`, `MixColums`, and `AddRoundKey` transformations, as defined in section 5 of {{FIPS-AES}}. Here, `in` is the 128-bit AES input state, and `rk` is the 128-bit round key. - `Repeat(n, F)`: `n` sequential evaluations of the function `F`. -- `CtEq(a, b)`: compares `a` and `b` in constant-time, returning `True` for an exact match, `False` otherwise. +- `CtEq(a, b)`: compares `a` and `b` in constant-time, returning `True` for an exact match, and `False` otherwise. AEGIS internal functions: -- `Update(M0, M1)` or `Update(M)`: the state update function. - `Init(key, nonce)`: the initialization function. +- `Update(M0, M1)` or `Update(M)`: the state update function. - `Absorb(ai)`: the input block absorption function. - `Enc(xi)`: the input block encryption function. - `Dec(ci)`: the input block decryption function. @@ -389,7 +389,7 @@ Input and output values: AEGIS-128L has a 1024-bit state, made of eight 128-bit blocks `{S0, ...S7}`. -The parameters for this algorithm, whose meaning is defined in {{!RFC5116, Section 4}} are: +The parameters for this algorithm, whose meaning is defined in {{!RFC5116, Section 4}}, are: - `K_LEN` (key length) is 16 bytes (128 bits). - `P_MAX` (maximum length of the plaintext) is 261 - 1 bytes (264 - 8 bits). @@ -397,7 +397,7 @@ The parameters for this algorithm, whose meaning is defined in {{!RFC5116, Secti - `N_MIN` (minimum nonce length) = `N_MAX` (maximum nonce length) = 16 bytes (128 bits). - `C_MAX` (maximum ciphertext length) = `P_MAX` + tag length = (261 - 1) + 16 or 32 bytes (in bits: (264 - 8) + 128 or 256 bits). -Distinct associated data inputs, as described in {{!RFC5116, Section 3}} shall be unambiguously encoded as a single input. +Distinct associated data inputs, as described in {{!RFC5116, Section 3}} MUST be unambiguously encoded as a single input. It is up to the application to create a structure in the associated data input if needed. ## Authenticated Encryption @@ -456,12 +456,12 @@ The `Decrypt` function decrypts a ciphertext, verifies that the authentication t Security: -- If tag verification fails, the decrypted message and wrong message authentication tag MUST NOT be given as output. The decrypted message MUST be overwritten with zeros before being returned. +- If tag verification fails, the decrypted message and wrong authentication tag MUST NOT be given as output. The decrypted message MUST be overwritten with zeros before the function returns. - The comparison of the input `tag` with the `expected_tag` MUST be done in constant time. Inputs: -- `ct`: the ciphertext to be decrypted (length MUST be less than or equal to `C_MAX`). +- `ct`: the ciphertext to decrypt (length MUST be less than or equal to `C_MAX`). - `tag`: the authentication tag. - `ad`: the associated data to authenticate (length MUST be less than or equal to `A_MAX`). - `key`: the encryption key. @@ -610,7 +610,7 @@ Outputs: Steps: ~~~ -z0 = S6 ^ S1 ^ (S2 & S3) +z0 = S1 ^ S6 ^ (S2 & S3) z1 = S2 ^ S5 ^ (S6 & S7) t0, t1 = Split(xi, 128) @@ -642,7 +642,7 @@ Outputs: Steps: ~~~ -z0 = S6 ^ S1 ^ (S2 & S3) +z0 = S1 ^ S6 ^ (S2 & S3) z1 = S2 ^ S5 ^ (S6 & S7) t0, t1 = Split(ci, 128) @@ -674,7 +674,7 @@ Outputs: Steps: ~~~ -z0 = S6 ^ S1 ^ (S2 & S3) +z0 = S1 ^ S6 ^ (S2 & S3) z1 = S2 ^ S5 ^ (S6 & S7) t0, t1 = Split(ZeroPad(cn, 256), 128) @@ -713,9 +713,9 @@ t = S2 ^ (LE64(ad_len_bits) || LE64(msg_len_bits)) Repeat(7, Update(t, t)) -if tag_length_bits == 128: +if tag_len_bits == 128: tag = S0 ^ S1 ^ S2 ^ S3 ^ S4 ^ S5 ^ S6 -else: # 256 bits +else: # 256 bits tag = (S0 ^ S1 ^ S2 ^ S3) || (S4 ^ S5 ^ S6 ^ S7) return tag @@ -725,15 +725,15 @@ return tag AEGIS-256 has a 768-bit state, made of six 128-bit blocks `{S0, ...S5}`. -The parameters for this algorithm, whose meaning is defined in {{!RFC5116, Section 4}} are: +The parameters for this algorithm, whose meaning is defined in {{!RFC5116, Section 4}}, are: - `K_LEN` (key length) is 32 bytes (256 bits). - `P_MAX` (maximum length of the plaintext) is 261 - 1 bytes (264 - 8 bits). -- `A_MAX` (maximum length of the associated data) is 261 bytes (264 bits). +- `A_MAX` (maximum length of the associated data) is 261 - 1 bytes (264 - 8 bits). - `N_MIN` (minimum nonce length) = `N_MAX` (maximum nonce length) = 32 bytes (256 bits). - `C_MAX` (maximum ciphertext length) = `P_MAX` + tag length = (261 - 1) + 16 or 32 bytes (in bits: (264 - 8) + 128 or 256 bits). -Distinct associated data inputs, as described in {{!RFC5116, Section 3}} shall be unambiguously encoded as a single input. +Distinct associated data inputs, as described in {{!RFC5116, Section 3}} MUST be unambiguously encoded as a single input. It is up to the application to create a structure in the associated data input if needed. ## Authenticated Encryption @@ -792,12 +792,12 @@ The `Decrypt` function decrypts a ciphertext, verifies that the authentication t Security: -- If tag verification fails, the decrypted message and wrong message authentication tag MUST NOT be given as output. The decrypted message MUST be overwritten with zeros before being returned. +- If tag verification fails, the decrypted message and wrong authentication tag MUST NOT be given as output. The decrypted message MUST be overwritten with zeros before the function returns. - The comparison of the input `tag` with the `expected_tag` MUST be done in constant time. Inputs: -- `ct`: the ciphertext to be decrypted (length MUST be less than or equal to `C_MAX`). +- `ct`: the ciphertext to decrypt (length MUST be less than or equal to `C_MAX`). - `tag`: the authentication tag. - `ad`: the associated data to authenticate (length MUST be less than or equal to `A_MAX`). - `key`: the encryption key. @@ -937,11 +937,11 @@ The `Enc` function encrypts a 128-bit input block `xi` using the state `{S0, ... Inputs: -- `xi`: the input block. +- `xi`: the 128-bit input block. Outputs: -- `ci`: the encrypted input block. +- `ci`: the 128-bit encrypted block. Steps: @@ -1039,9 +1039,9 @@ t = S3 ^ (LE64(ad_len_bits) || LE64(msg_len_bits)) Repeat(7, Update(t)) -if tag_length_bits == 128: +if tag_len_bits == 128: tag = S0 ^ S1 ^ S2 ^ S3 ^ S4 ^ S5 -else: # 256 bits +else: # 256 bits tag = (S0 ^ S1 ^ S2) || (S3 ^ S4 ^ S5) return tag @@ -1053,7 +1053,7 @@ Some CPUs, such as Intel and Intel-compatible CPUs with the VAES extensions, inc AEGIS-128X and AEGIS-256X are optional, specialized modes designed to take advantage of these instructions. They share the same properties as the ciphers they are based on but can be significantly faster on these platforms, even for short messages. -AEGIS-128X and AEGIS-256X are parallel evaluations of multiple AEGIS-128L and AEGIS-256 instances respectively, with distinct initial states. On CPUs with wide vector registers, different states can be stored in different 128-bit lanes of the same vector register, allowing parallel updates using vector instructions. +AEGIS-128X and AEGIS-256X are parallel evaluations of multiple AEGIS-128L and AEGIS-256 instances, respectively, with distinct initial states. On CPUs with wide vector registers, different states can be stored in different 128-bit lanes of the same vector register, allowing parallel updates using vector instructions. The modes are parameterized by the parallelism degree. With 256-bit registers, 2 parallel operations can be applied to 128-bit AES blocks. With 512-bit registers, the number of instances can be raised to 4. @@ -1063,9 +1063,9 @@ The state of a parallel mode is represented as a vector of AEGIS-128L or AEGIS-2 - `D`: the degree of parallelism. - `R`: the absorption and output rate of the mode. With AEGIS-128X, the rate is `256 * D` bits. With AEGIS-256X, the rate is `128 * D` bits. -- `V[j,i]`: the `j`-th AES block of the `i`-th state. `i` is in the `[0..D)` range. For AEGIS-128X, `j` is in the `[0..8)` range, while for AEGIS-256, `j` is in the `[0..6)` range. +- `V[j,i]`: the `j`-th AES block of the `i`-th state. `i` is in the `[0..D)` range. For AEGIS-128X, `j` is in the `[0..8)` range, while for AEGIS-256X, `j` is in the `[0..6)` range. - `V'[j,i]`: the `j`-th AES block of the next `i`-th state. -- `ctx[i]`: the `i`-th context separator. This is a 128-bit mask, made of a byte representing the state index, followed by a byte representing the highest index and 112 all-zero bits. +- `ctx[i]`: the `i`-th context separator. This is a 128-bit mask made of a byte representing the state index, followed by a byte representing the highest index and 112 all-zero bits. - `Byte(x)`: the value `x` encoded as 8 bits. ## Authenticated Encryption @@ -1181,7 +1181,7 @@ Repeat(10, Update(M0, M1) ~~~ -The AEGIS-128X `Update` function is similar to the AEGIS-128L `Update` function, but absorbs `R` (= `256 * D`) bits at once. `M0` and `M1` are `128 * D` bits instead of 128 bits but are split into 128-bit blocks, each of them updating a different AEGIS-128L state. +The AEGIS-128X `Update` function is similar to the AEGIS-128L `Update` function but absorbs `R` (= `256 * D`) bits at once. `M0` and `M1` are `128 * D` bits instead of 128 bits but are split into 128-bit blocks, each of them updating a different AEGIS-128L state. Steps: @@ -1215,7 +1215,7 @@ for i in 0..D: Absorb(ai) ~~~ -The `Absorb` function is similar to the AEGIS-128L `Absorb` function, but absorbs `R` bits instead of 256 bits. +The `Absorb` function is similar to the AEGIS-128L `Absorb` function but absorbs `R` bits instead of 256 bits. Steps: @@ -1230,7 +1230,7 @@ Update(t0, t1) Enc(xi) ~~~ -The `Enc` function is similar to the AEGIS-128L `Enc` function, but encrypts `R` bits instead of 256 bits. +The `Enc` function is similar to the AEGIS-128L `Enc` function but encrypts `R` bits instead of 256 bits. Steps: @@ -1257,7 +1257,7 @@ return ci Dec(ci) ~~~ -The `Dec` function is similar to the AEGIS-128L `Dec` function, but decrypts `R` bits instead of 256 bits. +The `Dec` function is similar to the AEGIS-128L `Dec` function but decrypts `R` bits instead of 256 bits. Steps: @@ -1284,7 +1284,7 @@ return xi DecPartial(cn) ~~~ -The `DecPartial` function is similar to the AEGIS-128L `DecPartial` function, but decrypts up to `R` bits instead of 256 bits. +The `DecPartial` function is similar to the AEGIS-128L `DecPartial` function but decrypts up to `R` bits instead of 256 bits. Steps: @@ -1325,13 +1325,13 @@ for i in 0..D: Repeat(7, Update(t, t)) -if tag_length_bits == 128: +if tag_len_bits == 128: tag = ZeroPad({}, 128) for i in 0..D: ti = V[0,i] ^ V[1,i] ^ V[2,i] ^ V[3,i] ^ V[4,i] ^ V[5,i] ^ V[6,i] tag = tag ^ ti -else: # 256 bits +else: # 256 bits ti0 = ZeroPad({}, 128) ti1 = ZeroPad({}, 128) for i in 0..D: @@ -1407,7 +1407,7 @@ Repeat(4, Update(M) ~~~ -The AEGIS-256X `Update` function is similar to the AEGIS-256 `Update` function, but absorbs `R` (`128 * D`) bits at once. `M` is `128 * D` bits instead of 128 bits and is split into 128-bit blocks, each of them updating a different AEGIS-256 state. +The AEGIS-256X `Update` function is similar to the AEGIS-256 `Update` function but absorbs `R` (`128 * D`) bits at once. `M` is `128 * D` bits instead of 128 bits and is split into 128-bit blocks, each of them updating a different AEGIS-256 state. Steps: @@ -1436,7 +1436,7 @@ for i in 0..D: Absorb(ai) ~~~ -The `Absorb` function is similar to the AEGIS-256 `Absorb` function, but absorbs `R` bits instead of 128 bits. +The `Absorb` function is similar to the AEGIS-256 `Absorb` function but absorbs `R` bits instead of 128 bits. Steps: @@ -1450,7 +1450,7 @@ Update(ai) Enc(xi) ~~~ -The `Enc` function is similar to the AEGIS-256 `Enc` function, but encrypts `R` bits instead of 128 bits. +The `Enc` function is similar to the AEGIS-256 `Enc` function but encrypts `R` bits instead of 128 bits. Steps: @@ -1472,7 +1472,7 @@ return ci Dec(ci) ~~~ -The `Dec` function is similar to the AEGIS-256 `Dec` function, but decrypts `R` bits instead of 128 bits. +The `Dec` function is similar to the AEGIS-256 `Dec` function but decrypts `R` bits instead of 128 bits. Steps: @@ -1494,7 +1494,7 @@ return xi DecPartial(cn) ~~~ -The `DecPartial` function is similar to the AEGIS-256 `DecPartial` function, but decrypts up to `R` bits instead of 128 bits. +The `DecPartial` function is similar to the AEGIS-256 `DecPartial` function but decrypts up to `R` bits instead of 128 bits. Steps: @@ -1532,13 +1532,13 @@ for i in 0..D: Repeat(7, Update(t)) -if tag_length_bits == 128: +if tag_len_bits == 128: tag = ZeroPad({}, 128) for i in 0..D: ti = V[0,i] ^ V[1,i] ^ V[2,i] ^ V[3,i] ^ V[4,i] ^ V[5,i] tag = tag ^ ti -else: # 256 bits +else: # 256 bits ti0 = ZeroPad({}, 128) ti1 = ZeroPad({}, 128) for i in 0..D: @@ -1551,7 +1551,7 @@ return tag ## Implementation Considerations -AEGIS-128X and AEGIS-256X with a degree of `1` are identical to AEGIS-128L and AEGIS-256. This property can be used to reduce the size of a generic implementation. +AEGIS-128X and AEGIS-256X with a degree of `1` are identical to AEGIS-128L and AEGIS-256, respectively. This property can be used to reduce the size of a generic implementation. In AEGIS-128X, `V` can be represented as eight 256-bit registers (when `D = 2`) or eight 512-bit registers (when `D = 4`). In AEGIS-256X, `V` can be represented as six 256-bit registers (when `D = 2`) or six 512-bit registers (when `D = 4`). With this representation, loops over `0..D` in the above pseudocode can be replaced by vector instructions. @@ -1561,7 +1561,7 @@ The AEGIS parallel modes are specialized and can only improve performance on spe The degrees of parallelism implementations are encouraged to support are `2` (for CPUs with 256-bit registers) and `4` (for CPUs with 512-bit registers). The resulting algorithms are called `AEGIS-128X2`, `AEGIS-128X4`, `AEGIS-256X2`, and `AEGIS-256X4`. -The following table summarizes how many bits are processed in parallel (rate), the memory requirements (state size), and the minimum vector register sizes a CPU should support for optimal performance. +The following table summarizes how many bits are processed in parallel (rate), the memory requirements (state size), and the minimum vector register size a CPU should support for optimal performance. | Algorithm | Rate (bits) | Optimal Register Size | State Size (bits) | | ----------- | ----------: | :-------------------: | ----------------: | @@ -1596,13 +1596,13 @@ All AEGIS variants can also be used as stream ciphers. Stream(len, key, nonce) ~~~ -The `Stream` function expands a key and an optional nonce into a variable-length, secure keystream. +The `Stream` function expands a key and an optional nonce into a variable-length keystream. Inputs: - `len`: the length of the keystream to generate in bits. - `key`: the AEGIS key. -- `nonce`: the nonce. If unspecified, it is set to `N_MAX` zero bytes. +- `nonce`: the AEGIS nonce. If unspecified, it is set to `N_MAX` zero bytes. Outputs: @@ -1616,9 +1616,9 @@ stream, tag = Encrypt(ZeroPad({}, len), {}, key, nonce) return stream ~~~ -This is equivalent to encrypting a `len` all-zero bits message without associated data, and discarding the authentication tag. +This is equivalent to encrypting a `len` all-zero bits message without associated data and discarding the authentication tag. -Instead of relying on the generic `Encrypt` function, implementations can skip the finalization step. +Instead of relying on the generic `Encrypt` function, implementations can omit the `Finalize` function. After initialization, the `Update` function is called with constant parameters, allowing further optimizations. @@ -1626,7 +1626,7 @@ After initialization, the `Update` function is called with constant parameters, All AEGIS variants can be used to construct a MAC. -For all the variants, the `Mac` function takes a key, a nonce, and data as input, and produces a 128- or 256-bit tag as output. +For all the variants, the `Mac` function takes a key, a nonce, and data as input and produces a 128- or 256-bit tag as output. ~~~ Mac(data, key, nonce) @@ -1636,7 +1636,7 @@ Security: - This is the only function that allows the reuse of `(key, nonce)` pairs with different inputs. - AEGIS-based MAC functions MUST NOT be used as hash functions: if the key is known, inputs causing state collisions can easily be crafted. -- Unlike hash-based MACs, tags MUST NOT be used for key derivation, as there is no guarantee that they are uniformly random. +- Unlike hash-based MACs, tags MUST NOT be used for key derivation as there is no guarantee that they are uniformly random. Inputs: @@ -1659,7 +1659,7 @@ Init(key, nonce) data_blocks = Split(ZeroPad(data, 256), 256) for di in data_blocks: Absorb(di) -tag = Finalize(|data|, tag_length_bits) +tag = Finalize(|data|, tag_len_bits) return tag ~~~ @@ -1674,7 +1674,7 @@ Init(key, nonce) data_blocks = Split(ZeroPad(data, 128), 128) for di in data_blocks: Absorb(di) -tag = Finalize(|data|, tag_length_bits) +tag = Finalize(|data|, tag_len_bits) return tag ~~~ @@ -1709,20 +1709,20 @@ Steps: ~~~ t = {} -u = LE64(data_len_bits) || LE64(tag_length_bits) +u = LE64(data_len_bits) || LE64(tag_len_bits) for i in 0..D: t = t || (V[2,i] ^ u) Repeat(7, Update(t, t)) tags = {} -if tag_length_bits == 128: - for i in 0..D: # tag from state 0 is included +if tag_len_bits == 128: + for i in 0..D: # tag from state 0 is included ti = V[0,i] ^ V[1,i] ^ V[2,i] ^ V[3,i] ^ V[4,i] ^ V[5,i] ^ V[6,i] tags = tags || ti -else: # 256 bits - for i in 1..D: # tag from state 0 is skipped +else: # 256 bits + for i in 1..D: # tag from state 0 is skipped ti0 = V[0,i] ^ V[1,i] ^ V[2,i] ^ V[3,i] ti1 = V[4,i] ^ V[5,i] ^ V[6,i] ^ V[7,i] tags = tags || (ti0 || ti1) @@ -1732,16 +1732,18 @@ if D > 1: for v in Split(tags, 256): Absorb(ZeroPad(v, R)) - u = LE64(D) || LE64(tag_length_bits) + u = LE64(D) || LE64(tag_len_bits) t = ZeroPad(V[2,0] ^ u, R) Repeat(7, Update(t, t)) -if tag_length_bits == 128: +if tag_len_bits == 128: tag = V[0,0] ^ V[1,0] ^ V[2,0] ^ V[3,0] ^ V[4,0] ^ V[5,0] ^ V[6,0] -else: # 256 bits +else: # 256 bits t0 = V[0,0] ^ V[1,0] ^ V[2,0] ^ V[3,0] t1 = V[4,0] ^ V[5,0] ^ V[6,0] ^ V[7,0] tag = t0 || t1 + +return tag ~~~ ## AEGISMAC-256X @@ -1773,20 +1775,20 @@ It finalizes all the instances, absorbs the resulting tags into the first state, ~~~ t = {} -u = LE64(data_len_bits) || LE64(tag_length_bits) +u = LE64(data_len_bits) || LE64(tag_len_bits) for i in 0..D: t = t || (V[3,i] ^ u) Repeat(7, Update(t)) tags = {} -if tag_length_bits == 128: +if tag_len_bits == 128: for i in 1..D: # tag from state 0 is skipped ti = V[0,i] ^ V[1,i] ^ V[2,i] ^ V[3,i] ^ V[4,i] ^ V[5,i] tags = tags || ti -else: # 256 bits - for i in 1..D: # tag from state 0 is skipped +else: # 256 bits + for i in 1..D: # tag from state 0 is skipped ti0 = V[0,i] ^ V[1,i] ^ V[2,i] ti1 = V[3,i] ^ V[4,i] ^ V[5,i] tags = tags || (ti0 || ti1) @@ -1796,13 +1798,13 @@ if D > 1: for v in Split(tags, 128): Absorb(ZeroPad(v, R)) - u = LE64(D) || LE64(tag_length_bits) + u = LE64(D) || LE64(tag_len_bits) t = ZeroPad(V[3,0] ^ u, R) Repeat(7, Update(t)) -if tag_length_bits == 128: +if tag_len_bits == 128: tag = V[0,0] ^ V[1,0] ^ V[2,0] ^ V[3,0] ^ V[4,0] ^ V[5,0] ^ V[6,0] -else: # 256 bits +else: # 256 bits t0 = V[0,0] ^ V[1,0] ^ V[2,0] ^ V[3,0] t1 = V[4,0] ^ V[5,0] ^ V[6,0] ^ V[7,0] tag = t0 || t1 @@ -1832,9 +1834,9 @@ With AEGIS-128L and AEGIS-128X, random nonces can safely encrypt up to 248< With AEGIS-256 and AEGIS-256X, random nonces can be used with no practical limits. -### Key Commitment +### Committing Security -An authentication tag may verify under multiple keys, nonces, or associated data, but AEGIS is assumed to be key committing in the receiver-binding game, preventing common attacks when used with low-entropy keys such as passwords. Finding distinct keys and/or nonces that successfully verify the same `(ad, ct, tag)` tuple is expected to require ~264 attempts with a 128-bit authentication tag and ~2128 attempts with a 256-bit tag. +An authentication tag may verify under multiple keys, nonces, or associated data, but AEGIS is assumed to be key committing in the receiver-binding game. This mitigates common attacks when used with low-entropy keys such as passwords. Finding distinct keys and/or nonces that successfully verify the same `(ad, ct, tag)` tuple is expected to require ~264 attempts with a 128-bit authentication tag and ~2128 attempts with a 256-bit tag. AEGIS is fully committing in the restricted setting where an adversary cannot control the associated data. As shown in {{IR23}}, with the ability to alter the associated data, it is possible to efficiently find multiple keys that will verify the same authenticated ciphertext. @@ -1846,19 +1848,19 @@ Alternatively, the associated data can be fed into a collision-resistant KDF, su AEGIS nonces match the size of the key. AEGIS-128L and AEGIS-128X feature 128-bit nonces, offering an extra 32 bits compared to the commonly used AEADs in IETF protocols. The AEGIS-256 and AEGIS-256X variants provide even larger nonces. With 192 random bits, 64 bits remain available to optionally encode additional information. -In all these variants, unused nonce bits can encode a key identifier, enhancing multi-user security. If every key has a unique identifier, multi-target attacks don't provide any advantage over single-target attacks. +In all these variants, unused nonce bits can encode a key identifier, enhancing multi-user security. If every key has a unique identifier, multi-target attacks do not provide any advantage over single-target attacks. ## Implementation Security -If tag verification fails, the unverified plaintext and the computed message authentication tag MUST NOT be released. As shown in {{VV18}}, even a partial leak of the plaintext without verification would facilitate chosen ciphertext attacks. +If tag verification fails, the unverified plaintext and computed authentication tag MUST NOT be released. As shown in {{VV18}}, even a partial leak of the plaintext without verification would facilitate chosen ciphertext attacks. -The security of AEGIS against timing and physical attacks is limited by the implementation of the underlying `AESRound()` function. Failure to implement `AESRound()` in a fashion safe against timing and physical attacks, such as differential power analysis, timing analysis, or fault injection attacks, may lead to leakage of secret key material or state information. The exact mitigations required for timing and physical attacks also depend on the threat model in question. +The security of AEGIS against timing and physical attacks is limited by the implementation of the underlying `AESRound` function. Failure to implement `AESRound` in a fashion safe against timing and physical attacks, such as differential power analysis, timing analysis, or fault injection attacks, may lead to leakage of secret key material or state information. The exact mitigations required for timing and physical attacks also depend on the threat model in question. Regardless of the variant, the `key` and `nonce` are only required by the `Init` function; other functions only depend on the resulting state. Therefore, implementations can overwrite ephemeral keys with zeros right after the last `Update` call of the initialization function. ## Security Guarantees -AEGIS-256 offers 256-bit message security against plaintext and state recovery, whereas AEGIS-128L offers 128-bit security. +AEGIS-256 offers 256-bit security against plaintext and state recovery, whereas AEGIS-128L offers 128-bit security. Under the assumption that the secret key is unknown to the attacker, all AEGIS variants offer at least 128-bit security against forgery attacks. @@ -1867,7 +1869,7 @@ However, `(key, nonce)` pairs MUST NOT be reused, even if the associated data di AEGIS has been shown to have reforgeability resilience in {{FLLW17}}. Without the ability to set the associated data, a successful forgery does not increase the probability of subsequent forgeries. -AEGIS-128X and AEGIS-256X share the same security properties and requirements as AEGIS-128L and AEGIS-256 respectively. In particular, the security level and usage limits remain the same {{D23}}. +AEGIS-128X and AEGIS-256X share the same security properties and requirements as AEGIS-128L and AEGIS-256, respectively. In particular, the security level and usage limits remain the same {{D23}}. AEGIS is considered secure against guess-and-determine attacks aimed at recovering the state from observed ciphertexts. @@ -2896,15 +2898,15 @@ tag256 : b36a16ef07c36d75a91f437502f24f54 # Acknowledgments {:numbered="false"} -The AEGIS authenticated encryption algorithm was invented by Hongjun Wu and Bart Preneel. +The AEGIS family of authenticated encryption algorithms was invented by Hongjun Wu and Bart Preneel. The state update function leverages the AES permutation invented by Joan Daemen and Vincent Rijmen. They also authored the Pelican MAC, which partly motivated the design of the AEGIS MAC. We would like to thank the following individuals for their contributions: -- Eric Lagergren and Daniel Bleichenbacher for catching a broken test vector and Daniel Bleichenbacher for many helpful suggestions. -- John Preuß Mattsson for his review of the draft, and for suggesting how AEGIS should be used in the context of DTLS and QUIC. -- Bart Mennink and Charlotte Lefevre as well as Takanori Isobe and Mostafizar Rahman for investigating the commitment security of the schemes specified in this document. -- Scott Fluhrer for his review of the draft as a member of the CFRG Crypto Panel. +- Eric Lagergren and Daniel Bleichenbacher for catching a broken test vector, and Daniel Bleichenbacher for many helpful suggestions. +- John Preuß Mattsson for his review of the draft and for suggesting how AEGIS should be used in the context of DTLS and QUIC. +- Bart Mennink and Charlotte Lefevre, as well as Takanori Isobe and Mostafizar Rahman for investigating the committing security of the schemes specified in this document. +- Scott Fluhrer for his review of the draft as a member of the CFRG Crypto Review Panel. - Yawning Angel, Chris Barber, and Neil Madden for their review of the draft. - Gilles Van Assche for reviewing the draft and providing insightful comments on the implications of nonce reuse in AEGIS-128X and AEGIS-256X. diff --git a/reference-implementations/aegis128l.zig b/reference-implementations/aegis128l.zig index bec4dcc..d44fd09 100644 --- a/reference-implementations/aegis128l.zig +++ b/reference-implementations/aegis128l.zig @@ -72,7 +72,7 @@ fn Aegis128L_(comptime tag_bits: u9) type { fn enc(self: *Self, xi: *const [32]u8) [32]u8 { const s = self.s; - const z0 = s[6].xorBlocks(s[1]).xorBlocks(s[2].andBlocks(s[3])); + const z0 = s[1].xorBlocks(s[6]).xorBlocks(s[2].andBlocks(s[3])); const z1 = s[2].xorBlocks(s[5]).xorBlocks(s[6].andBlocks(s[7])); const t0 = AesBlock.fromBytes(xi[0..16]); const t1 = AesBlock.fromBytes(xi[16..32]); @@ -87,7 +87,7 @@ fn Aegis128L_(comptime tag_bits: u9) type { fn dec(self: *Self, ci: *const [32]u8) [32]u8 { const s = self.s; - const z0 = s[6].xorBlocks(s[1]).xorBlocks(s[2].andBlocks(s[3])); + const z0 = s[1].xorBlocks(s[6]).xorBlocks(s[2].andBlocks(s[3])); const z1 = s[2].xorBlocks(s[5]).xorBlocks(s[6].andBlocks(s[7])); const t0 = AesBlock.fromBytes(ci[0..16]); const t1 = AesBlock.fromBytes(ci[16..32]); @@ -102,7 +102,7 @@ fn Aegis128L_(comptime tag_bits: u9) type { fn decLast(self: *Self, xn: []u8, cn: []const u8) void { const s = self.s; - const z0 = s[6].xorBlocks(s[1]).xorBlocks(s[2].andBlocks(s[3])); + const z0 = s[1].xorBlocks(s[6]).xorBlocks(s[2].andBlocks(s[3])); const z1 = s[2].xorBlocks(s[5]).xorBlocks(s[6].andBlocks(s[7])); var pad = [_]u8{0} ** 32; @memcpy(pad[0..cn.len], cn); diff --git a/reference-implementations/aegis128x.zig b/reference-implementations/aegis128x.zig index b89301d..82c383c 100644 --- a/reference-implementations/aegis128x.zig +++ b/reference-implementations/aegis128x.zig @@ -102,7 +102,7 @@ fn Aegis128X_(comptime degree: u7, comptime tag_bits: u9) type { fn enc(self: *Self, xi: *const [rate]u8) [rate]u8 { const s = self.s; - const z0 = s[6].xorBlocks(s[1]).xorBlocks(s[2].andBlocks(s[3])); + const z0 = s[1].xorBlocks(s[6]).xorBlocks(s[2].andBlocks(s[3])); const z1 = s[2].xorBlocks(s[5]).xorBlocks(s[6].andBlocks(s[7])); const t0 = AesBlockX.fromBytes(xi[0..blockx_length]); const t1 = AesBlockX.fromBytes(xi[blockx_length..rate]); @@ -117,7 +117,7 @@ fn Aegis128X_(comptime degree: u7, comptime tag_bits: u9) type { fn dec(self: *Self, ci: *const [rate]u8) [rate]u8 { const s = self.s; - const z0 = s[6].xorBlocks(s[1]).xorBlocks(s[2].andBlocks(s[3])); + const z0 = s[1].xorBlocks(s[6]).xorBlocks(s[2].andBlocks(s[3])); const z1 = s[2].xorBlocks(s[5]).xorBlocks(s[6].andBlocks(s[7])); const t0 = AesBlockX.fromBytes(ci[0..blockx_length]); const t1 = AesBlockX.fromBytes(ci[blockx_length..rate]); @@ -132,7 +132,7 @@ fn Aegis128X_(comptime degree: u7, comptime tag_bits: u9) type { fn decLast(self: *Self, xn: []u8, cn: []const u8) void { const s = self.s; - const z0 = s[6].xorBlocks(s[1]).xorBlocks(s[2].andBlocks(s[3])); + const z0 = s[1].xorBlocks(s[6]).xorBlocks(s[2].andBlocks(s[3])); const z1 = s[2].xorBlocks(s[5]).xorBlocks(s[6].andBlocks(s[7])); var pad = [_]u8{0} ** rate; @memcpy(pad[0..cn.len], cn);