typed-rust: AES-256-GCM seal/open as crypto-prims (FFI to aes-gcm)
ober
eccee32bf0d725ce43907cf417221fb80aaaa2e5
--- a/lib/jerboa/typed/checker.ss +++ b/lib/jerboa/typed/checker.ss @@ -392,6 +392,15 @@ (cons 'sha256 (make-typed-call-sig (list 'Bytes) 'Bytes '() 'crypto-prim (list (cons 'prim 'sha256)))) + ;; AES-256-GCM AEAD (NIST SP 800-38D). (key nonce data aad): 32-byte key, + ;; 12-byte nonce, aad may be empty. seal → ciphertext‖tag; open → + ;; (Option Bytes), None on authentication failure (wrong key/tamper). + (cons 'aes-256-gcm-seal + (make-typed-call-sig (list 'Bytes 'Bytes 'Bytes 'Bytes) 'Bytes '() + 'crypto-prim (list (cons 'prim 'aes-256-gcm-seal)))) + (cons 'aes-256-gcm-open + (make-typed-call-sig (list 'Bytes 'Bytes 'Bytes 'Bytes) (list 'Option 'Bytes) '() + 'crypto-prim (list (cons 'prim 'aes-256-gcm-open)))) (cons 'hmac-sha256 (make-typed-call-sig (list 'Bytes 'Bytes) 'Bytes '() 'crypto-prim (list (cons 'prim 'hmac-sha256)))) --- a/lib/jerboa/typed/rust.ss +++ b/lib/jerboa/typed/rust.ss @@ -1422,6 +1422,40 @@ "{ let __k: [u8; 32] = " (x25519-key32 (car args)) "; x25519_dalek::x25519(__k, x25519_dalek::X25519_BASEPOINT_BYTES).to_vec() }")) + ;; (aes-256-gcm-seal key nonce plaintext aad) : (Bytes*4) -> Bytes. + ;; AES-256-GCM AEAD encrypt; the returned buffer is ciphertext with the + ;; 16-byte authentication tag appended (RustCrypto convention). 32-byte key, + ;; 12-byte nonce, aad may be empty. A wrong key/nonce length panics and is + ;; caught by the C-ABI wrapper's catch_unwind. + (def (emit-aes-256-gcm-seal args) + (unless (= (length args) 4) + (error 'typed-rust + "aes-256-gcm-seal expects key, nonce, plaintext and aad Bytes" args)) + (string-append + "{ use aes_gcm::aead::{Aead, KeyInit, Payload}; " + "let __c = aes_gcm::Aes256Gcm::new_from_slice(" (bytes-slice (car args)) + ").expect(\"AES-256-GCM key is 32 bytes\"); " + "let __n = aes_gcm::Nonce::from_slice(" (bytes-slice (cadr args)) + "); __c.encrypt(__n, Payload { msg: " (bytes-slice (caddr args)) + ", aad: " (bytes-slice (cadddr args)) + " }).expect(\"AES-256-GCM encryption within size limits\") }")) + + ;; (aes-256-gcm-open key nonce ciphertext aad) : (Bytes*4) -> (Option Bytes). + ;; Decrypt and verify; None on authentication failure (wrong key, tampered + ;; ciphertext/aad, or bad tag). `ciphertext` is the ct‖tag buffer seal made. + (def (emit-aes-256-gcm-open args) + (unless (= (length args) 4) + (error 'typed-rust + "aes-256-gcm-open expects key, nonce, ciphertext and aad Bytes" args)) + (string-append + "{ use aes_gcm::aead::{Aead, KeyInit, Payload}; " + "let __c = aes_gcm::Aes256Gcm::new_from_slice(" (bytes-slice (car args)) + ").expect(\"AES-256-GCM key is 32 bytes\"); " + "let __n = aes_gcm::Nonce::from_slice(" (bytes-slice (cadr args)) + "); match __c.decrypt(__n, Payload { msg: " (bytes-slice (caddr args)) + ", aad: " (bytes-slice (cadddr args)) + " }) { Ok(__pt) => Some(__pt), Err(_) => None } }")) + (def (emit-crypto-prim prim args) (case prim [(sha256) (emit-sha256 args)] @@ -1429,6 +1463,8 @@ [(hkdf-sha256) (emit-hkdf-sha256 args)] [(x25519-dh) (emit-x25519-dh args)] [(x25519-base) (emit-x25519-base args)] + [(aes-256-gcm-seal) (emit-aes-256-gcm-seal args)] + [(aes-256-gcm-open) (emit-aes-256-gcm-open args)] [else (error 'typed-rust "unknown crypto primitive" prim)])) ;; (bytevector-append a b) -> a fresh owned buffer a ++ b. Clone the first so