~/bend-docscommunity

src/crypto/aead.bend source

src/crypto/aead.bend on the hub · documented module

import Baseimport ./aead/chacha20poly1305.bend as CPimport ./aesgcm.bend as GCM# Authenticated encryption with associated data (RFC 5116 shape).##   encrypt(alg, key, nonce, aad, plaintext) -> Maybe(ciphertext || tag)#   decrypt(alg, key, nonce, aad, ciphertext || tag) -> Maybe(plaintext)## Bytes are U32 values < 256. Every algorithm has a 16-byte tag appended to# the ciphertext. encrypt returns None unless the key and nonce have the# algorithm's lengths (key_size, nonce_size); decrypt also returns None for# an input shorter than the tag and whenever the tag is not the one computed# over aad and ciphertext (a forgery, a wrong key/nonce/aad, a modified# ciphertext), before any plaintext is produced.##   algorithm            key  nonce  tag   standard#   CHACHA20_POLY1305    32   12     16    RFC 8439 2.8#   XCHACHA20_POLY1305   32   24     16    draft-irtf-cfrg-xchacha-03#   AES_128_GCM          16   12     16    FIPS 197 + SP 800-38D#   AES_256_GCM          32   12     16    FIPS 197 + SP 800-38D## Adding an algorithm: a constructor of Alg, its row in key_size and# nonce_size, and its case in seal and open (the per-algorithm functions,# which may do their own length checks); encrypt and decrypt check the# lengths and dispatch, and the laws in proofs/crypto/aead/laws.bend are# proved by one case per algorithm.## Proved (proofs/crypto/aead/): each algorithm equals its specification# (spec/crypto/chacha20poly1305.bend, spec/crypto/aes/gcm.bend),# decrypt(encrypt(x)) == Some(x) for every key, nonce, aad and plaintext of# the right lengths, and decrypt rejects ciphertext || t for every 16-byte t# other than the tag the specification computes. Constant time is not# provable in Bend (no timing model); the code does not branch or index on# secret bytes except for the final accept/reject.type Alg is Data:  CHACHA20_POLY1305{}  XCHACHA20_POLY1305{}  AES_128_GCM{}  AES_256_GCM{}def key_size(alg: Alg) -> Nat:  match alg:    case CHACHA20_POLY1305{}: 32n    case XCHACHA20_POLY1305{}: 32n    case AES_128_GCM{}: 16n    case AES_256_GCM{}: 32ndef nonce_size(alg: Alg) -> Nat:  match alg:    case CHACHA20_POLY1305{}: 12n    case XCHACHA20_POLY1305{}: 24n    case AES_128_GCM{}: 12n    case AES_256_GCM{}: 12ndef tag_size() -> Nat:  16n# ciphertext || tag (encrypt checks the key and nonce lengths first).def seal(alg: Alg, +key: List<&2, U32>, +nonce: List<&2, U32>, +aad: List<&2, U32>, +pt: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match alg:    case CHACHA20_POLY1305{}: Some{CP.seal(key, nonce, aad, pt)}    case XCHACHA20_POLY1305{}: Some{CP.xseal(key, nonce, aad, pt)}    case AES_128_GCM{}: GCM.aes128_gcm_encrypt(key, nonce, aad, pt)    case AES_256_GCM{}: GCM.aes256_gcm_encrypt(key, nonce, aad, pt)# The plaintext of ciphertext || tag, or None (decrypt checks the lengths first).def open(alg: Alg, +key: List<&2, U32>, +nonce: List<&2, U32>, +aad: List<&2, U32>, +data: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  match alg:    case CHACHA20_POLY1305{}: CP.open(key, nonce, aad, data)    case XCHACHA20_POLY1305{}: CP.xopen(key, nonce, aad, data)    case AES_128_GCM{}: GCM.aes128_gcm_decrypt(key, nonce, aad, data)    case AES_256_GCM{}: GCM.aes256_gcm_decrypt(key, nonce, aad, data)def valid(+alg: Alg, +key: List<&2, U32>, +nonce: List<&2, U32>) -> Bool:  Bool.and(Nat.is_eq(List.length(&2, U32, key), key_size(alg)), Nat.is_eq(List.length(&2, U32, nonce), nonce_size(alg)))def when_open(ok: Bool, r: Maybe<&2, List<&2, U32>>) -> Maybe<&2, List<&2, U32>>:  match ok:    case True{}: r    case False{}: None{}def encrypt(+alg: Alg, +key: List<&2, U32>, +nonce: List<&2, U32>, +aad: List<&2, U32>, +pt: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  when_open(valid(alg, key, nonce), seal(alg, key, nonce, aad, pt))def decrypt(+alg: Alg, +key: List<&2, U32>, +nonce: List<&2, U32>, +aad: List<&2, U32>, +data: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:  when_open(valid(alg, key, nonce), open(alg, key, nonce, aad, data))