proof/AES_IdentityProof.bend

qasim-bend-kit@0.1.0.2 · 161 lines · 14 definitions

1import Base
2import qasim-bend-kit@0.1.0.2/libs/AES256GCM.bend as AES
3import qasim-bend-kit@0.1.0.2/libs/AES256GCMCore.bend as Core
4import qasim-bend-kit@0.1.0.2/proof/AES_EnvelopeProof.bend as Encoding
5import qasim-bend-kit@0.1.0.2/proof/AES_ConstructorProof.bend as Constructor
6import qasim-bend-kit@0.1.0.2/proof/AES_KnownAnswerProof.bend as Known
7import qasim-bend-kit@0.1.0.2/proof/AES_MaskedXorProof.bend as Masked
8import qasim-bend-kit@0.1.0.2/proof/AES_StringEqProof.bend as Logic
9
10def mask_word_same(+value: U32) ->
11    {AES.hex_mask(value, 255) == Core.gcm_hex_mask(value, 255) : U32}:
12    match value:
13        case U32{WCon{b0, WCon{b1, WCon{b2, WCon{b3, WCon{b4, WCon{b5, WCon{b6, WCon{b7, WCon{b8, WCon{b9, WCon{b10, WCon{b11, WCon{b12, WCon{b13, WCon{b14, WCon{b15, WCon{b16, WCon{b17, WCon{b18, WCon{b19, WCon{b20, WCon{b21, WCon{b22, WCon{b23, WCon{b24, WCon{b25, WCon{b26, WCon{b27, WCon{b28, WCon{b29, WCon{b30, WCon{b31, WNil{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}: {==}
14
15def mask_same(+bytes: List<&2, U32>) ->
16    {Encoding.aes_hex_mask_bytes(bytes) == Core.gcm_mask_bytes(bytes) : List<&2, U32>}:
17    match bytes:
18        case Nil{}: {==}
19        case byte <> tail:
20            Equal.trans(List<&2, U32>, Encoding.aes_hex_mask_bytes(byte <> tail),
21                Core.gcm_hex_mask(byte, 255) <> Encoding.aes_hex_mask_bytes(tail), Core.gcm_mask_bytes(byte <> tail),
22                Equal.cong(U32, List<&2, U32>, value => value <> Encoding.aes_hex_mask_bytes(tail),
23                    AES.hex_mask(byte, 255), Core.gcm_hex_mask(byte, 255), mask_word_same(byte)),
24                Equal.cong(List<&2, U32>, List<&2, U32>, xs => Core.gcm_hex_mask(byte, 255) <> xs,
25                    Encoding.aes_hex_mask_bytes(tail), Core.gcm_mask_bytes(tail), mask_same(tail)))
26
27def mask_identity(+bytes: List<&2, U32>, valid: {AES.bytes_valid(bytes) == True{} : Bool}) ->
28    {Encoding.aes_hex_mask_bytes(bytes) == bytes : List<&2, U32>}:
29    core_valid = Equal.trans(Bool, Core.gcm_bytes_valid(bytes), AES.bytes_valid(bytes), True{},
30        Equal.sym(Bool, AES.bytes_valid(bytes), Core.gcm_bytes_valid(bytes), AES.core_bytes_valid_eq(bytes)), valid)
31    Equal.trans(List<&2, U32>, Encoding.aes_hex_mask_bytes(bytes), Core.gcm_mask_bytes(bytes), bytes,
32        mask_same(bytes), Masked.mask_bytes_identity(bytes, core_valid))
33
34def decoded_or_empty(value: Maybe<&2, List<&2, U32>>) -> List<&2, U32>:
35    match value:
36        case None{}: Nil{}
37        case Some{bytes}: bytes
38
39def hex_injective(+first: List<&2, U32>, +second: List<&2, U32>,
40    first_valid: {AES.bytes_valid(first) == True{} : Bool},
41    second_valid: {AES.bytes_valid(second) == True{} : Bool},
42    same: {AES.hex_bytes(first) == AES.hex_bytes(second) : String}) ->
43    {first == second : List<&2, U32>}:
44    decoded = Equal.trans(Maybe<&2, List<&2, U32>>,
45        Some{Encoding.aes_hex_mask_bytes(first)}, AES.decode_hex(AES.hex_bytes(first)),
46        Some{Encoding.aes_hex_mask_bytes(second)},
47        Equal.sym(Maybe<&2, List<&2, U32>>, AES.decode_hex(AES.hex_bytes(first)),
48            Some{Encoding.aes_hex_mask_bytes(first)}, Encoding.aes_decode_hex_bytes(first)),
49        Equal.trans(Maybe<&2, List<&2, U32>>, AES.decode_hex(AES.hex_bytes(first)),
50            AES.decode_hex(AES.hex_bytes(second)), Some{Encoding.aes_hex_mask_bytes(second)},
51            Equal.cong(String, Maybe<&2, List<&2, U32>>, AES.decode_hex,
52                AES.hex_bytes(first), AES.hex_bytes(second), same), Encoding.aes_decode_hex_bytes(second)))
53    masks_equal = Equal.cong(Maybe<&2, List<&2, U32>>, List<&2, U32>, decoded_or_empty,
54        Some{Encoding.aes_hex_mask_bytes(first)}, Some{Encoding.aes_hex_mask_bytes(second)}, decoded)
55    Equal.trans(List<&2, U32>, first, Encoding.aes_hex_mask_bytes(first), second,
56        Equal.sym(List<&2, U32>, Encoding.aes_hex_mask_bytes(first), first, mask_identity(first, first_valid)),
57        Equal.trans(List<&2, U32>, Encoding.aes_hex_mask_bytes(first), Encoding.aes_hex_mask_bytes(second),
58            second, masks_equal, mask_identity(second, second_valid)))
59
60def fields(+envelope: AES.Envelope) -> List<&2, String>:
61    ["v1", AES.hex_bytes(AES.fixed_bytes(12n, AES.nonce_bytes(AES.envelope_nonce(envelope)))),
62        AES.hex_bytes(AES.fixed_bytes(16n, AES.tag_bytes(AES.envelope_tag(envelope)))),
63        AES.hex_bytes(AES.ciphertext(envelope))]
64
65def optional_text(value: Maybe<&2, String>) -> String:
66    match value:
67        case None{}: ""
68        case Some{text}: text
69
70def field_text(index: Nat, parts: List<&2, String>) -> String:
71    optional_text(List.get(&2, String, parts, index))
72
73def result_text(value: Result<&2, &2, AES.Error, String>) -> String:
74    match value:
75        case Fail{error}: ""
76        case Done{text}: text
77
78def fields_equal(+first: AES.Envelope, +second: AES.Envelope,
79    same: {AES.encode(first) == AES.encode(second) : String}) ->
80    {fields(first) == fields(second) : List<&2, String>}:
81    Equal.trans(List<&2, String>, fields(first), String.split(AES.encode(first), Chr{46}), fields(second),
82        Equal.sym(List<&2, String>, String.split(AES.encode(first), Chr{46}), fields(first),
83            Encoding.aes_encode_fields_split(first)),
84        Equal.trans(List<&2, String>, String.split(AES.encode(first), Chr{46}),
85            String.split(AES.encode(second), Chr{46}), fields(second),
86            Equal.cong(String, List<&2, String>, text => String.split(text, Chr{46}),
87                AES.encode(first), AES.encode(second), same), Encoding.aes_encode_fields_split(second)))
88
89def fixed_hex_equal(+n: Nat, +first: List<&2, U32>, +second: List<&2, U32>,
90    first_size: {List.length(&2, U32, first) == n : Nat},
91    second_size: {List.length(&2, U32, second) == n : Nat},
92    same: {AES.hex_bytes(AES.fixed_bytes(n, first)) == AES.hex_bytes(AES.fixed_bytes(n, second)) : String}) ->
93    {AES.hex_bytes(first) == AES.hex_bytes(second) : String}:
94    first_path = Equal.cong(List<&2, U32>, String, bytes => AES.hex_bytes(bytes),
95        AES.fixed_bytes(n, first), first, Encoding.aes_fixed_bytes_identity(n, first, first_size))
96    second_path = Equal.cong(List<&2, U32>, String, bytes => AES.hex_bytes(bytes),
97        AES.fixed_bytes(n, second), second, Encoding.aes_fixed_bytes_identity(n, second, second_size))
98    Equal.trans(String, AES.hex_bytes(first), AES.hex_bytes(AES.fixed_bytes(n, first)), AES.hex_bytes(second),
99        Equal.sym(String, AES.hex_bytes(AES.fixed_bytes(n, first)), AES.hex_bytes(first), first_path),
100        Equal.trans(String, AES.hex_bytes(AES.fixed_bytes(n, first)), AES.hex_bytes(AES.fixed_bytes(n, second)),
101            AES.hex_bytes(second), same, second_path))
102
103def encode_injective(+first: AES.Envelope, +second: AES.Envelope,
104    same: {AES.encode(first) == AES.encode(second) : String}) -> {first == second : AES.Envelope}:
105    match first second:
106        case AES.Envelope{AES.Nonce{+bytes_a, +size_a, +nonce_valid_a},
107                 +cipher_a, AES.Tag{+tag_bytes_a, +tag_size_a, +tag_valid_a}, +valid_a}
108             AES.Envelope{AES.Nonce{+bytes_b, +size_b, +nonce_valid_b},
109                 +cipher_b, AES.Tag{+tag_bytes_b, +tag_size_b, +tag_valid_b}, +valid_b}:
110            +size_a_copy = size_a
111            +size_b_copy = size_b
112            +nonce_valid_a_copy = nonce_valid_a
113            +nonce_valid_b_copy = nonce_valid_b
114            +nonce_a = {AES.Nonce{bytes_a, size_a_copy, nonce_valid_a_copy} : AES.Nonce}
115            +nonce_b = {AES.Nonce{bytes_b, size_b_copy, nonce_valid_b_copy} : AES.Nonce}
116            +tag_a = {AES.Tag{tag_bytes_a, tag_size_a, tag_valid_a} : AES.Tag}
117            +tag_b = {AES.Tag{tag_bytes_b, tag_size_b, tag_valid_b} : AES.Tag}
118            +parts_same = fields_equal(first, second, same)
119            nonce_hex = Equal.cong(List<&2, String>, String, parts => field_text(1n, parts),
120                fields(first), fields(second), parts_same)
121            tag_hex = Equal.cong(List<&2, String>, String, parts => field_text(2n, parts),
122                fields(first), fields(second), parts_same)
123            cipher_hex = Equal.cong(List<&2, String>, String, parts => field_text(3n, parts),
124                fields(first), fields(second), parts_same)
125            nonce_bytes_same = hex_injective(bytes_a, bytes_b, nonce_valid_a_copy, nonce_valid_b_copy,
126                fixed_hex_equal(12n, bytes_a, bytes_b, size_a_copy, size_b_copy, nonce_hex))
127            tag_bytes_same = hex_injective(tag_bytes_a, tag_bytes_b, tag_valid_a, tag_valid_b,
128                fixed_hex_equal(16n, tag_bytes_a, tag_bytes_b, tag_size_a, tag_size_b, tag_hex))
129            cipher_same = hex_injective(cipher_a, cipher_b, valid_a, valid_b, cipher_hex)
130            nonce_same = Constructor.nonce_equal(bytes_a, size_a_copy, nonce_valid_a_copy, bytes_b,
131                nonce_bytes_same, size_b_copy, nonce_valid_b_copy)
132            tag_same = Known.tag_equal(tag_bytes_a, tag_size_a, tag_valid_a, tag_bytes_b,
133                tag_bytes_same, tag_size_b, tag_valid_b)
134            fields_same = Known.envelope_equal(nonce_a, cipher_a, tag_a, valid_a,
135                cipher_b, tag_b, cipher_same, tag_same, valid_b)
136            Equal.trans(AES.Envelope, first, AES.Envelope{nonce_a, cipher_b, tag_b, valid_b}, second,
137                fields_same, Equal.cong(AES.Nonce, AES.Envelope,
138                    nonce => AES.Envelope{nonce, cipher_b, tag_b, valid_b}, nonce_a, nonce_b, nonce_same))
139
140def exact_roundtrip_result(+envelope: AES.Envelope,
141    result: Result<&2, &2, AES.Error, AES.Envelope>,
142    encoded: {Result.map(&2, &2, AES.Error, AES.Envelope, String, AES.encode, result) ==
143        Done{AES.encode(envelope)} : Result<&2, &2, AES.Error, String>}) ->
144    {result == Done{envelope} : Result<&2, &2, AES.Error, AES.Envelope>}:
145    match result:
146        case Fail{error}:
147            status = Equal.cong(Result<&2, &2, AES.Error, String>, Bool,
148                value => Result.is_done(&2, &2, AES.Error, String, value),
149                Fail{error}, Done{AES.encode(envelope)}, encoded)
150            Empty.absurd({Fail{error} == Done{envelope} : Result<&2, &2, AES.Error, AES.Envelope>},
151                Logic.false_true(status))
152        case Done{+found}:
153            text_same = Equal.cong(Result<&2, &2, AES.Error, String>, String,
154                result_text,
155                Done{AES.encode(found)}, Done{AES.encode(envelope)}, encoded)
156            Equal.cong(AES.Envelope, Result<&2, &2, AES.Error, AES.Envelope>, value => Done{value},
157                found, envelope, encode_injective(found, envelope, text_same))
158
159def exact_roundtrip(+envelope: AES.Envelope) ->
160    {AES.parse(AES.encode(envelope)) == Done{envelope} : Result<&2, &2, AES.Error, AES.Envelope>}:
161    exact_roundtrip_result(envelope, AES.parse(AES.encode(envelope)), Encoding.aes_envelope_roundtrip(envelope))