4 Symmetric Encryption
A cipher (or symmetric-key encryption algorithm) is a reversable function from variable-length messages to messages. The input is called the “plaintext” and the output is called the “ciphertext.” For a good cipher, the ciphertext reveals no information about the contents of the plaintext (only its length or approximate length); in particular, it is infeasible to compute the plaintext corresponding to a given ciphertext without knowing the secret key.
Ciphers are organized into families that share common encryption and decryption algorithms but differ in parameters such as key length and block mode. For example, the AES family supports key lengths of 128, 192, and 256 bits, and it supports a wide range of modes, including ECB, CBC, and CTR.
This library provides both high-level, all-at-once encryption and decryption operations and low-level, incremental operations.
procedure
(cipher-spec? v) → boolean?
v : any/c
(list stream-cipher 'stream)—
where stream-cipher is one of the following symbols: 'chacha20, 'chacha20-poly1305, 'chacha20-poly1305/iv8, 'rc4, 'salsa20, 'salsa20r12, 'salsa20r8, 'xchacha20-poly1305. (list block-cipher block-mode)—
where block-cipher is one of the following symbols: 'aes, 'blowfish, 'camellia, 'cast128, 'des, 'des-ede2, 'des-ede3, 'idea, 'serpent, 'twofish, and block-mode is one of the following symbols: 'ecb, 'cbc, 'ofb, 'cfb, 'ctr, 'gcm, 'ocb, 'eax.
Note that the key length is not considered part of the cipher specifier; it is determined implicitly from the key provided to encrypt, make-encrypt-ctx, etc.
The CCM block mode is not supported because of its burdensome requirements: it requires the message and AAD lengths to be known in advance.
Future versions of this library may add more ciphers to the lists above and other forms of cipher specifiers.
procedure
(cipher-impl? v) → boolean?
v : any/c
procedure
(get-cipher ci [factories]) → (or/c cipher-impl? #f)
ci : cipher-spec?
factories : (or/c crypto-factory? (listof crypto-factory?)) = (crypto-factories)
procedure
ci : (or/c cipher-spec? cipher-impl? cipher-ctx?)
A cipher produces a ciphertext that is a multiple of its block size. If a cipher is used without padding, the plaintext must be a multiple of the block size.
> (cipher-block-size '(aes cbc)) 16
> (cipher-block-size '(aes ctr)) 1
> (cipher-block-size '(salsa20 stream)) 1
procedure
ci : (or/c cipher-spec? cipher-impl?)
> (cipher-default-key-size '(aes cbc)) 16
> (cipher-default-key-size '(chacha20 stream)) 32
procedure
ci : (or/c cipher-spec? cipher-impl?)
> (cipher-key-sizes '(aes cbc)) '(16 24 32)
> (cipher-key-sizes '(chacha20 stream)) '(32)
procedure
ci : (or/c cipher-spec? cipher-impl? cipher-ctx?)
This library uses a broad interpretation of the term “IV”. For example, if ci is a block cipher in CTR mode, this function returns the size of the counter.
> (cipher-iv-size '(aes cbc)) 16
> (cipher-iv-size '(aes ctr)) 16
> (cipher-iv-size '(aes gcm)) 12
> (cipher-iv-size '(aes ecb)) 0
> (cipher-iv-size '(chacha20-poly1305 stream)) 12
> (cipher-iv-size '(chacha20-poly1305/iv8 stream)) 8
procedure
(cipher-aead? ci) → boolean?
ci : (or/c cipher-spec? cipher-impl? cipher-ctx?)
An authenticated encryption cipher (with additionally authenticated data, AEAD) produces an authentication tag in addition to the ciphertext. An AEAD cipher provides both confidentiality and integrity, whereas a non-AEAD cipher only provides confidentiality.
> (cipher-aead? '(aes ctr)) #f
> (cipher-aead? '(aes gcm)) #t
> (cipher-aead? '(chacha20-poly1305 stream)) #t
procedure
ci : (or/c cipher-spec? cipher-impl? cipher-ctx?)
> (cipher-default-auth-size '(aes gcm)) 16
> (cipher-default-auth-size '(aes ctr)) 0
procedure
(generate-cipher-key ci [#:size size]) → bytes?
ci : (or/c cipher-spec? cipher-impl?) size : exact-positive-integer? = (cipher-default-key-size ci)
procedure
(generate-cipher-iv ci [#:size size]) → bytes?
ci : (or/c cipher-spec? cipher-impl?) size : exact-positive-integer? = (cipher-iv-size ci)
The random bytes are generated with crypto-random-bytes.
4.1 High-level Cipher Operations
procedure
(encrypt ci key iv input [ #:aad additional-authenticated-data #:auth-size auth-size #:pad pad-mode]) → bytes? ci : (or/c cipher-spec? cipher-impl?) key : bytes? iv : (or/c bytes? #f) input : input/c additional-authenticated-data : input/c = #""
auth-size : exact-nonnegative-integer? = (cipher-default-auth-size ci) pad-mode : boolean? = #t
procedure
(decrypt ci key iv input [ #:aad additional-authenticated-data #:auth-size auth-size #:pad pad-mode]) → bytes? ci : (or/c cipher-spec? cipher-impl?) key : bytes? iv : (or/c bytes? #f) input : (or/c bytes? input-port?) additional-authenticated-data : input/c = #""
auth-size : exact-nonnegative-integer? = (cipher-default-auth-size ci) pad-mode : boolean? = #t
If ci is a block cipher and if pad-mode is #t, then the input is padded using PKCS#7 padding during encryption, and the padding is checked and removed during decryption; otherwise if pad-mode is #f, then the input is not padded, and its length must by divisible by ci’s block size. If ci is a stream cipher (including block ciphers using a stream mode), pad is ignored and no padding is added. Future versions of this library may support additional kinds of padding.
If ci is an authenticated encryption (AEAD) cipher, the authentication tag it produces is attached to the ciphertext. That is, encrypt appends the authentication tag to the end of the ciphertext, and decrypt extracts the authentication tag from the end of the ciphertext. The auth-size argument controls the length of the authentication tag. If authenticated decryption fails, an exception is raised.
> (define key (generate-cipher-key '(aes ctr))) > (define iv (generate-cipher-iv '(aes ctr))) > (define ciphertext (encrypt '(aes ctr) key iv "Hello world!")) > ciphertext #"\331 >\303\0\370\240Mf\177\345\204"
> (decrypt '(aes ctr) key iv ciphertext) #"Hello world!"
procedure
(encrypt/auth ci key iv input [ #:aad additional-authenticated-data #:auth-size auth-size #:pad pad-mode])
→
bytes? bytes? ci : (or/c cipher-spec? cipher-impl?) key : bytes? iv : (or/c bytes? #f) input : input/c additional-authenticated-data : input/c = #""
auth-size : exact-nonnegative-integer? = (cipher-default-auth-size ci) pad-mode : boolean? = #t
procedure
(decrypt/auth ci key iv input [ #:aad additional-authenticated-data #:auth-tag auth-tag #:pad pad-mode]) → bytes? ci : (or/c cipher-spec? cipher-impl?) key : bytes? iv : (or/c bytes? #f) input : (or/c bytes? input-port?) additional-authenticated-data : input/c = #"" auth-tag : bytes? = #"" pad-mode : boolean? = #t
If ci is not an AEAD cipher, the authentication tag is always #"".
> (define key (generate-cipher-key '(aes gcm))) > (define iv (generate-cipher-iv '(aes gcm)))
> (define-values (ciphertext auth-tag) (encrypt/auth '(aes gcm) key iv "Hello world!" #:aad #"greeting")) > (decrypt/auth '(aes gcm) key iv ciphertext #:aad #"greeting" #:auth-tag auth-tag) #"Hello world!"
> (decrypt/auth '(aes gcm) key iv ciphertext #:aad #"INVALID" #:auth-tag auth-tag) decrypt: authenticated decryption failed
4.2 Low-level Cipher Operations
procedure
(make-encrypt-ctx ci key iv [ #:auth-size auth-size #:auth-attached? auth-attached? #:pad pad-mode]) → encrypt-ctx? ci : (or/c cipher-spec? cipher-impl?) key : bytes? iv : (or/c bytes? #f)
auth-size : exact-nonnegative-integer? = (cipher-default-auth-size ci) auth-attached? : boolean? = #t pad-mode : boolean? = #t
procedure
(make-decrypt-ctx ci key iv [ #:auth-size auth-size #:auth-attached? auth-attached? #:pad pad-mode]) → decrypt-ctx? ci : (or/c cipher-spec? cipher-impl?) key : bytes? iv : (or/c bytes? #f)
auth-size : exact-nonnegative-integer? = (cipher-default-auth-size ci) auth-attached? : boolean? = #t pad-mode : boolean? = #t
procedure
(cipher-ctx? v) → boolean?
v : any/c
Equivalent to (or (encrypt-ctx? v) (decrypt-ctx? v)).
procedure
(encrypt-ctx? v) → boolean?
v : any/c
procedure
(decrypt-ctx? v) → boolean?
v : any/c
procedure
(cipher-update cctx input) → bytes?
cctx : cipher-ctx? input : input/c
procedure
(cipher-update-aad cctx input) → void?
cctx : cipher-ctx? input : input/c
If cctx is not a context for authenticated encryption or decryption, an exception is raised.
procedure
(cipher-final cctx [auth-tag]) → bytes?
cctx : cipher-ctx? auth-tag : (or/c bytes? #f) = #f
If cctx is an authenticated decryption context in detached mode (that is, created with #:auth-attached? #f), then auth-tag is checked against the decryption’s final authentication tag and an exception is raised if they do not match.
Otherwise—
procedure
(cipher-get-auth-tag cctx) → bytes?
cctx : cipher-ctx?