Add NIST SP 800-38D GCM specification with KATs#389
Draft
sgmenda wants to merge 8 commits into
Draft
Conversation
Add HOL Light definitions for AES-128 and AES-256 following NIST FIPS 197
exactly, independent from the hardware-oriented model in common/aes.ml.
Uses NIST ordering (SubBytes before ShiftRows) throughout.
New files:
- common/fips197.ml: key expansion, cipher steps, full cipher, and KATs
- {arm,x86}/proofs/fips197.ml: loaders for standard build-proof.sh validation
Definitions: aes128_kexp_round, aes256_kexp_round, aes256_kexp_last,
fips197_sub_bytes, fips197_shift_rows, fips197_mix_columns,
fips197_add_round_key, fips197_round, fips197_final_round,
aes128_cipher, aes256_cipher.
KATs: FIPS 197 Appendices A.1, A.3, B; NIST AES_Core256; AESAVS GFSbox.
To validate: cd x86 && make proofs/fips197.correct
or: cd arm && make proofs/fips197.correct
Precompute all 768 lookup table entries (256 each for S-box, FFmul02, FFmul03) as proven theorems at load time (~48s). This eliminates the word_subword bottleneck on 2048-bit constants, reducing AES-128 evaluation from ~40s to ~7s per call. Add FIPS197_ENCRYPT_FAST_CONV and supporting fast conversion chain. Uncomment all KATs (key expansion, SubBytes, ShiftRows, MixColumns, AddRoundKey, full AES-128 and AES-256 cipher) using fast conversions. Remove fips197_add_round_key (just word_xor, unused).
Cherry-pick common/misc.ml from s2n-bignum#381 to get word_pmul and WORD_PMUL_CONV, needed by ghash_reduce in common/ghash.ml. This file should be identical to the version in awslabs#381 and will be a no-op when awslabs#381 merges.
Cherry-pick common/ghash.ml from s2n-bignum#382 to get ghash_reduce1, ghash_reduce, and their correctness proofs. Needed by gf128_mul in common/gcm.ml. This file should be identical to the version in awslabs#382 and will be a no-op when awslabs#382 merges.
common/gcm.ml: High-level GCM spec close to NIST SP 800-38D: - gf128_mul (via word_pmul + ghash_reduce), ghash, inc32, gctr, gcm_ae (Algorithm 4), gcm_ad (Algorithm 5). - Simplified to 96-bit IV, full 128-bit blocks, 128-bit tag. - KATs: inc32, GCTR, gf128_mul, GHASH, plus GCM-AE/AD for TC1-TC3 from the GCM revised spec (McGrew & Viega, Appendix B).
sgmenda
commented
Apr 20, 2026
Comment on lines
+197
to
+272
| (* ========================================================================= *) | ||
| (* GHASH: NIST SP 800-38D Algorithm 2. *) | ||
| (* *) | ||
| (* GHASH(H, X_1 || ... || X_m) iterates gf128_mul: *) | ||
| (* Y_0 = 0, Y_i = gf128_mul(Y_{i-1} XOR X_i, H). *) | ||
| (* Takes an initial accumulator Y (normally word 0) for generality. *) | ||
| (* ========================================================================= *) | ||
|
|
||
| let ghash = define | ||
| `ghash (H:128 word) (Y:128 word) ([] : (128 word) list) = Y /\ | ||
| ghash H Y (CONS x rest) = ghash H (gf128_mul (word_xor Y x) H) rest`;; | ||
|
|
||
| (* Conversion for evaluating ghash on concrete values *) | ||
| let GHASH_STEP_CONV = | ||
| ONCE_REWRITE_CONV [ghash] THENC | ||
| DEPTH_CONV WORD_RED_CONV THENC | ||
| ONCE_DEPTH_CONV GF128_MUL_CONV;; | ||
|
|
||
| let rec GHASH_CONV tm = | ||
| (GHASH_STEP_CONV THENC TRY_CONV GHASH_CONV) tm;; | ||
|
|
||
| prove(`ghash (word 0x66E94BD4EF8A2C3B884CFA59CA342B2E) (word 0) | ||
| [word 0 : 128 word] = word 0`, | ||
| CONV_TAC(LAND_CONV GHASH_CONV) THEN REFL_TAC);; | ||
|
|
||
| prove(`ghash (word 0x66E94BD4EF8A2C3B884CFA59CA342B2E) (word 0) | ||
| [word 0x0388DACE60B6A392F328C2B971B2FE78 : 128 word] = | ||
| word 0x5E2EC746917062882C85B0685353DEB7`, | ||
| CONV_TAC(LAND_CONV GHASH_CONV) THEN REFL_TAC);; | ||
|
|
||
| prove(`ghash (word 0xB83B533708BF535D0AA6E52980D53B78) (word 0) | ||
| [ word 0x42831EC2217774244B7221B784D0D49C | ||
| ; word 0xE3AA212F2C02A4E035C17E2329ACA12E : 128 word] = | ||
| word 0xB714C9048389AFD9F9BC5C1D4378E052`, | ||
| CONV_TAC(LAND_CONV GHASH_CONV) THEN REFL_TAC);; | ||
|
|
||
| (* ========================================================================= *) | ||
| (* GCM-AE: NIST SP 800-38D Algorithm 4 (authenticated encryption). *) | ||
| (* *) | ||
| (* Simplified to 96-bit IV, full 128-bit blocks, and 128-bit tag. *) | ||
| (* Key schedule is pre-expanded. Returns (ciphertext, tag). *) | ||
| (* ========================================================================= *) | ||
|
|
||
| let gcm_ae = new_definition | ||
| `gcm_ae (ks:(128 word) list) (iv:96 word) | ||
| (P:(128 word) list) (A:(128 word) list) = | ||
| let H = aes128_cipher (word 0) ks in | ||
| let J0 : 128 word = word_join iv (word 1 : 32 word) in | ||
| let C = gctr ks (inc32 J0) P in | ||
| let len_block : 128 word = | ||
| word_join (word (128 * LENGTH A) : 64 word) | ||
| (word (128 * LENGTH C) : 64 word) in | ||
| let S = ghash H (word 0) (APPEND A (APPEND C [len_block])) in | ||
| let tag = word_xor S (aes128_cipher J0 ks) in | ||
| (C, tag)`;; | ||
|
|
||
| (* ========================================================================= *) | ||
| (* GCM-AD: NIST SP 800-38D Algorithm 5 (authenticated decryption). *) | ||
| (* *) | ||
| (* Returns SOME plaintext if tag verifies, NONE otherwise. *) | ||
| (* ========================================================================= *) | ||
|
|
||
| let gcm_ad = new_definition | ||
| `gcm_ad (ks:(128 word) list) (iv:96 word) | ||
| (C:(128 word) list) (A:(128 word) list) (tag:128 word) = | ||
| let H = aes128_cipher (word 0) ks in | ||
| let J0 : 128 word = word_join iv (word 1 : 32 word) in | ||
| let P = gctr ks (inc32 J0) C in | ||
| let len_block : 128 word = | ||
| word_join (word (128 * LENGTH A) : 64 word) | ||
| (word (128 * LENGTH C) : 64 word) in | ||
| let S = ghash H (word 0) (APPEND A (APPEND C [len_block])) in | ||
| let tag' = word_xor S (aes128_cipher J0 ks) in | ||
| if tag' = tag then SOME P else NONE`;; | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a high-level AES-GCM specification, following NIST SP 800-38D. It will be the correctness target for forthcoming AES-GCM assembly proofs.
New files
common/gcm.ml— GCM definitions and KATs:inc32— increment rightmost 32 bits of a 128-bit block (Section 6.2)gctr— counter-mode encryption (Algorithm 3)gf128_mul— GF(2^128) multiplication (Algorithm 1)ghash— GHASH universal hash (Algorithm 2)gcm_ae— authenticated encryption (Algorithm 4)gcm_ad— authenticated decryption (Algorithm 5)Simplified to 96-bit IVs, full 128-bit blocks, 128-bit tags, and pre-expanded key schedules.
KATs validated against test vectors from the GCM revised spec (McGrew & Viega, Appendix B):
TC1 is proved end-to-end (
prove()). TC2 and TC3 are deconstructed KATs where each step (AES, GCTR, GHASH, tag) is proved as an individual theorem.