silk.crypto.aes_gcm.
Encrypt and authenticate data with AES-128-GCM or AES-256-GCM (NIST SP 800-38D).
When to use#
Use AesGcm.seal to encrypt a message so that only holders of the key can read it and any
change is detected. Use AesGcm.open to check and decrypt it. If the protocol does not name
an algorithm, silk.crypto.chacha20_poly1305 is also a good choice.
Details#
The key is 16 bytes (AES-128) or 32 bytes (AES-256), the nonce is 12 bytes, and the tag is 16
bytes. The tag is separate from the ciphertext ("detached"), so store or send both. The
associated data (aad) is authenticated but not encrypted. Use it for headers that must stay
readable. The ciphertext has the same length as the plaintext. Nothing allocates.
Errors#
The functions return an AesGcmError. All checks come before the first write, and on any
error the output buffers do not change. AesGcm.open writes no plaintext unless the tag is
correct.
Gotchas#
The same key and nonce must never encrypt two messages. If they do, an attacker can recover the
XOR of the two plaintexts and forge tags. Use a counter, or 12 random bytes from silk.system.random
for a small number of messages per key.
Authentication does not stop replay: an attacker can send an old valid message again. The functions do not erase their copies of the key.
Examples#
Encrypt and decrypt a message#
import silk.crypto.aes_gcm { AesGcm, AesGcmError }
import silk.core.result { Result }
pub fn main() -> i32 {
let key: [u8; 16] = [0; 16]
let nonce: [u8; 12] = [0; 12]
let plaintext: [u8; 16] = [0; 16]
let mut ciphertext: [u8; 16] = [0; 16]
let mut tag: [u8; 16] = [0; 16]
match AesGcm.seal(&key, &nonce, b"header", &plaintext, &mut ciphertext, &mut tag) {
Result<(), AesGcmError>.Success { value: _ } => {}
Result<(), AesGcmError>.Failure { error: _ } => {
return 1
}
}
// For this key and nonce, the ciphertext starts with 0388dace.
if ciphertext[0] != 0x03 || ciphertext[1] != 0x88 {
return 1
}
let mut decrypted: [u8; 16] = [1; 16]
return match AesGcm.open(&key, &nonce, b"header", &ciphertext, &tag, &mut decrypted) {
Result<(), AesGcmError>.Success { value: _ } => {
if decrypted[0] != 0 || decrypted[15] != 0 {
return 1
}
return 0
}
Result<(), AesGcmError>.Failure { error: _ } => 1
}
}Detect a changed message#
import silk.crypto.aes_gcm { AesGcm, AesGcmError }
import silk.core.result { Result }
pub fn main() -> i32 {
let key: [u8; 32] = [7; 32]
let nonce: [u8; 12] = [1; 12]
let mut ciphertext: [u8; 5] = [0; 5]
let mut tag: [u8; 16] = [0; 16]
match AesGcm.seal(&key, &nonce, b"", b"hello", &mut ciphertext, &mut tag) {
Result<(), AesGcmError>.Success { value: _ } => {}
Result<(), AesGcmError>.Failure { error: _ } => {
return 1
}
}
ciphertext[0] = ciphertext[0] + 1
let mut plaintext: [u8; 5] = [0; 5]
return match AesGcm.open(&key, &nonce, b"", &ciphertext, &tag, &mut plaintext) {
Result<(), AesGcmError>.Failure { error: AesGcmError.AuthenticationFailed } => {
// The output does not change when the tag is wrong.
if plaintext[0] != 0 {
return 1
}
return 0
}
_ => 1
}
}Import as AesGcm with import silk.crypto.aes_gcm { AesGcm }.
Public declarations: 2.
AesGcmError#
pub union AesGcmErrorThe reason that AesGcm.seal or AesGcm.open rejected a call. The output buffers do not
change.
InvalidKeyLength#
InvalidKeyLengthThe key is not 16 or 32 bytes.
InvalidNonceLength#
InvalidNonceLengthThe nonce is not 12 bytes.
InvalidTagLength#
InvalidTagLengthThe tag, or the tag output, is not 16 bytes.
OutputTooSmall#
OutputTooSmallThe output is shorter than the input message.
LimitExceeded#
LimitExceededThe message is longer than 2^36 - 32 bytes, or the associated data is longer than 2^61 - 1 bytes.
AuthenticationFailed#
AuthenticationFailedThe tag is not correct for the key, nonce, associated data, and ciphertext. The message was changed, or a value is wrong.
Implementation Debug for AesGcmError#
impl Debug for AesGcmErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant and its fields, such as InvalidKeyLength.
AesGcm#
pub struct AesGcmFunctions that encrypt and authenticate with AES-128-GCM or AES-256-GCM (NIST SP 800-38D).
When to use#
Use AesGcm.seal to encrypt a message so that only holders of the key can read it and any
change is detected. Use AesGcm.open to check and decrypt it.
Details#
The key is 16 bytes (AES-128) or 32 bytes (AES-256), the nonce is 12 bytes, and the tag is 16 bytes. The tag is separate from the ciphertext, so store or send both. The ciphertext has the same length as the plaintext. Nothing allocates. All checks come before the first write, and on any error the output buffers do not change.
Gotchas#
The same key and nonce must never encrypt two messages. If they do, an attacker can recover the XOR of the two plaintexts and forge tags. Authentication does not stop replay. The functions do not erase their copies of the key.
Implementation AesGcm#
impl AesGcmAssociated function seal#
pub fn seal(
key: &[u8],
nonce: &[u8],
aad: &[u8],
plaintext: &[u8],
ciphertext: &mut [u8],
tag: &mut [u8],
) -> Result<(), AesGcmError>Encrypts plaintext into ciphertext and writes the 16-byte authentication tag to tag.
The key is 16 bytes (AES-128) or 32 bytes (AES-256), and the nonce is 12 bytes. aad is
authenticated but not encrypted. Use it for headers that must stay readable. The ciphertext
has the same length as plaintext. ciphertext can be longer than plaintext. The bytes
after the written prefix do not change. The buffers must not overlap. Nothing allocates.
Errors#
All checks come before the first write. On an error, ciphertext and tag do not change.
InvalidKeyLengthifkeyis not 16 or 32 bytes.InvalidNonceLengthifnonceis not 12 bytes.InvalidTagLengthiftagis not 16 bytes.OutputTooSmallifciphertextis shorter thanplaintext.LimitExceededifplaintextis longer than 2^36 - 32 bytes oraadis longer than 2^61 - 1 bytes.
Gotchas#
Use a new nonce for each message under the same key. The function does not make one. If the
same key and nonce encrypt two messages, an attacker can recover the XOR of the two
plaintexts and forge tags. Use a counter, or 12 random bytes from silk.system.random for a small
number of messages per key. The function does not erase its copy of the key.
Associated function open#
pub fn open(
key: &[u8],
nonce: &[u8],
aad: &[u8],
ciphertext: &[u8],
tag: &[u8],
plaintext: &mut [u8],
) -> Result<(), AesGcmError>Checks tag, and if it is correct, decrypts ciphertext into plaintext.
The key is 16 bytes (AES-128) or 32 bytes (AES-256), the nonce is 12 bytes, and the tag is 16
bytes. aad must be the same bytes that were given to AesGcm.seal. The function checks
all 16 tag bytes before it writes plaintext. The bytes after the written prefix do not
change. Nothing allocates.
Errors#
All checks come before the first write. On an error, plaintext does not change.
AuthenticationFailedif the tag is not correct for the key, nonce,aad, and ciphertext.InvalidKeyLengthifkeyis not 16 or 32 bytes.InvalidNonceLengthifnonceis not 12 bytes.InvalidTagLengthiftagis not 16 bytes.OutputTooSmallifplaintextis shorter thanciphertext.LimitExceededifciphertextis longer than 2^36 - 32 bytes oraadis longer than 2^61 - 1 bytes.
Gotchas#
Authentication does not stop replay: an attacker can send an old valid message again. The function does not erase its copy of the key.
Category: Cryptography.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.