| License | BSD-style |
|---|---|
| Maintainer | Kazu Yamamoto <kazu@iij.ad.jp> |
| Stability | experimental |
| Portability | unknown |
| Safe Haskell | None |
| Language | Haskell2010 |
Crypto.Cipher.AES.GCM
Contents
Description
AES-GCM for callers that send many short messages under one key, which is what a datagram transport does.
The interface in Crypto.Cipher.Types builds a state from the key and
the nonce and then walks it through appending the additional data,
encrypting and finalizing, copying the state at each step. For a stream
that is nothing next to the encryption. For a QUIC packet it is most of
the work: the key schedule and the table of multiples of H depend on the
key alone, and rebuilding them for every nonce costs more than encrypting
1440 bytes.
So here a Context is built from the key once and holds both, and
encrypt takes a nonce and a whole message and answers in one call.
ctx <- throwCryptoError <$> pure (newContext key) let packet = encrypt ctx nonce header plaintext 16
This runs on AES-NI and carry-less multiply, or on the ARMv8 cryptographic
extension, and makes no branch and no memory access that depends on the key
or on the data. Where the processor has neither, AES falls back to a table
driven implementation that is not constant time; see the side channels
section of the README, and processorOptions for which is
in use.
The result is the ciphertext with the tag after it, which is the shape a
packet wants. decrypt takes that shape back, compares the tag itself and
answers Nothing when it does not match.
This computes the same thing as the general interface; the tests hold it to that on the same vectors.
Synopsis
- data Context
- newContext :: ByteArrayAccess key => key -> CryptoFailable Context
- encrypt :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArrayAccess ba, ByteArray output) => Context -> nonce -> aad -> ba -> Int -> output
- decrypt :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArray ba) => Context -> nonce -> aad -> ba -> Int -> Maybe ba
- decryptWithTag :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArray ba) => Context -> nonce -> aad -> ba -> Int -> (ba, AuthTag)
- data HeaderKey
- newHeaderKey :: ByteArrayAccess key => key -> CryptoFailable HeaderKey
- encryptWithMask :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArrayAccess ba) => Context -> HeaderKey -> nonce -> aad -> ba -> Int -> Int -> Ptr Word8 -> Ptr Word8 -> IO Bool
Documentation
Everything a key determines: the AES key schedule and the table of
multiples of H. Build it once and encrypt as many messages under it as
the key is good for.
newContext :: ByteArrayAccess key => key -> CryptoFailable Context Source #
Take a key of 16, 24 or 32 bytes. Any other length is reported as
CryptoError_KeySizeInvalid.
encrypt :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArrayAccess ba, ByteArray output) => Context -> nonce -> aad -> ba -> Int -> output Source #
Encrypt one message: the nonce, the additional data that is
authenticated but not encrypted, the plaintext, and how many bytes of tag
to produce, which GCM allows between 4 and 16. Any other length throws
CryptoError_AuthenticationTagSizeInvalid, and so does decryptWithTag.
The answer is the ciphertext followed by the tag.
A nonce must not be used twice with the same Context. Twelve bytes is
the size GCM is defined for and the only one that does not cost a further
pass. A nonce of no bytes is refused: it would hand out the key GCM
authenticates with, so encrypt and decryptWithTag throw
CryptoError_IvSizeInvalid for it, decrypt gives Nothing and
encryptWithMask gives False.
decrypt :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArray ba) => Context -> nonce -> aad -> ba -> Int -> Maybe ba Source #
Decrypt one message, in the shape encrypt produced: the ciphertext with
its tag after it. The tag is compared here, every byte of it whatever the
answer, and a message whose tag does not match gives Nothing rather than
the plaintext.
Nothing also comes back when the nonce has no bytes, the input is
shorter than the tag, or the tag length is outside 4 to 16.
decryptWithTag :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArray ba) => Context -> nonce -> aad -> ba -> Int -> (ba, AuthTag) Source #
Decrypt one message, the tag kept apart, and hand back the tag this end computed.
For a caller whose protocol hands it the tag separately from the
ciphertext, so that decrypt -- which wants the two together and compares
them itself -- does not fit. Compare the two tags with ==; the Eq
instance of AuthTag is a constant-time comparison, and taking them apart
to compare the bytes is how this goes wrong.
Nothing here says whether the message is authentic. Until the comparison is made and has come out equal, what this returns is not plaintext, it is what the ciphertext turns into, and a caller must not act on it.
Header protection
The key schedule for header protection, which QUIC keeps separately from
the one it encrypts with. Built once, like a Context.
newHeaderKey :: ByteArrayAccess key => key -> CryptoFailable HeaderKey Source #
Take a header protection key of 16, 24 or 32 bytes.
Arguments
| :: (ByteArrayAccess nonce, ByteArrayAccess aad, ByteArrayAccess ba) | |
| => Context | |
| -> HeaderKey | |
| -> nonce | |
| -> aad | |
| -> ba | |
| -> Int | tag length |
| -> Int | sample offset |
| -> Ptr Word8 | where the sealed message goes |
| -> Ptr Word8 | where the sixteen bytes of mask go |
| -> IO Bool |
Encrypt one message and, from a sample of the ciphertext it just produced, make the header protection mask -- in one call, into two buffers the caller already has.
QUIC takes its sample from the ciphertext, so the mask cannot be had before the encryption. It can be had before coming back, and with the buffers already there nothing is allocated for either. On an Apple M4 the mask then costs about 0.02 us, where asking for it separately costs 0.11.
The sealed message wants length input + taglen bytes and the mask
sixteen. sampleOffset says where the sixteen bytes of sample begin in
the sealed message, counting the tag as part of it.
False comes back, and nothing is written, when the nonce has no bytes,
the sample would not fit, or the tag length is outside 4 to 16.