1 import Base 2 import ../libs/AES256GCMCore.bend as Core 3 4 # Keep fuel symbolic while substituting the certified initial state. 5 def rounds_input_matches(+fuel: Nat, +words: List<&2, U32>, 6 +block: List<&2, U32>, +initial: List<&2, U32>, +expected: List<&2, U32>, 7 initial_match: {Core.aes_add_key(block, words, 0n) == initial : List<&2, U32>}, 8 rounds_match: {Core.aes_rounds.go(fuel, initial, words, 1n) == expected : List<&2, U32>}) -> 9 {Core.aes_rounds.go(fuel, Core.aes_add_key(block, words, 0n), words, 1n) == expected : List<&2, U32>}: 10 Equal.trans(List<&2, U32>, 11 Core.aes_rounds.go(fuel, Core.aes_add_key(block, words, 0n), words, 1n), 12 Core.aes_rounds.go(fuel, initial, words, 1n), expected, 13 Equal.cong(List<&2, U32>, List<&2, U32>, 14 value => Core.aes_rounds.go(fuel, value, words, 1n), 15 Core.aes_add_key(block, words, 0n), initial, initial_match), rounds_match) 16 17 # Establish the public-function conversion while inputs remain symbolic. 18 # A concrete caller then compares identical public applications instead of 19 # unfolding all fourteen rounds to recognize the internal implementation. 20 def expanded_block_from_initial(+words: List<&2, U32>, +block: List<&2, U32>, 21 +initial: List<&2, U32>, +expected: List<&2, U32>, 22 initial_match: {Core.aes_add_key(block, words, 0n) == initial : List<&2, U32>}, 23 rounds_match: {Core.aes_rounds.go(13n, initial, words, 1n) == expected : List<&2, U32>}) -> 24 {Core.aes256_encrypt_expanded(words, block) == expected : List<&2, U32>}: 25 match words: 26 case Nil{}: 27 rounds_input_matches(13n, words, block, initial, expected, 28 initial_match, rounds_match) 29 case head <> tail: 30 rounds_input_matches(13n, words, block, initial, expected, 31 initial_match, rounds_match)