1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
open State
module Aead128 = struct
let key_size = 16
let nonce_size = 16
let tag_size = 16
let iv = 0x00001000808c0001L
module Key = struct
type t = bytes
let of_bytes key =
if Bytes.length key = key_size then Ok (Bytes.copy key)
else Error `Invalid_length
let of_string key = of_bytes (Bytes.of_string key)
end
module Nonce = struct
type t = bytes
let of_bytes nonce =
if Bytes.length nonce = nonce_size then Ok (Bytes.copy nonce)
else Error `Invalid_length
let of_string nonce = of_bytes (Bytes.of_string nonce)
end
let initialize key nonce =
let k0 = Endian.load64_le key 0 in
let k1 = Endian.load64_le key 8 in
let n0 = Endian.load64_le nonce 0 in
let n1 = Endian.load64_le nonce 8 in
let state = State.create iv k0 k1 n0 n1 in
Permutation.p12 state;
state.x3 <- Int64.logxor state.x3 k0;
state.x4 <- Int64.logxor state.x4 k1;
(state, k0, k1)
let absorb_associated_data state associated_data =
let length = Bytes.length associated_data in
if length <> 0 then (
let offset = ref 0 in
while length - !offset >= 16 do
state.State.x0 <-
Int64.logxor state.x0 (Endian.load64_le associated_data !offset);
state.x1 <-
Int64.logxor state.x1
(Endian.load64_le associated_data (!offset + 8));
Permutation.p8 state;
offset := !offset + 16
done;
let remaining = length - !offset in
if remaining >= 8 then (
state.x0 <-
Int64.logxor state.x0
(Endian.load64_le associated_data !offset);
state.x1 <-
Int64.logxor state.x1
(Endian.load_partial_le associated_data (!offset + 8) (remaining - 8));
state.x1 <- Int64.logxor state.x1 (Endian.padding (remaining - 8)))
else (
state.x0 <-
Int64.logxor state.x0
(Endian.load_partial_le associated_data !offset remaining);
state.x0 <- Int64.logxor state.x0 (Endian.padding remaining));
Permutation.p8 state);
state.State.x4 <- Int64.logxor state.x4 Int64.min_int
let finalize state k0 k1 =
state.State.x2 <- Int64.logxor state.x2 k0;
state.x3 <- Int64.logxor state.x3 k1;
Permutation.p12 state;
state.x3 <- Int64.logxor state.x3 k0;
state.x4 <- Int64.logxor state.x4 k1;
let tag = Bytes.create tag_size in
Endian.store64_le tag 0 state.x3;
Endian.store64_le tag 8 state.x4;
tag
let encrypt ~key ~nonce ~associated_data ~plaintext =
let state, k0, k1 = initialize key nonce in
absorb_associated_data state associated_data;
let length = Bytes.length plaintext in
let ciphertext = Bytes.create length in
let offset = ref 0 in
while length - !offset >= 16 do
state.x0 <- Int64.logxor state.x0 (Endian.load64_le plaintext !offset);
state.x1 <-
Int64.logxor state.x1 (Endian.load64_le plaintext (!offset + 8));
Endian.store64_le ciphertext !offset state.x0;
Endian.store64_le ciphertext (!offset + 8) state.x1;
Permutation.p8 state;
offset := !offset + 16
done;
let remaining = length - !offset in
if remaining >= 8 then (
state.x0 <- Int64.logxor state.x0 (Endian.load64_le plaintext !offset);
state.x1 <-
Int64.logxor state.x1
(Endian.load_partial_le plaintext (!offset + 8) (remaining - 8));
Endian.store64_le ciphertext !offset state.x0;
Endian.store_partial_le ciphertext (!offset + 8) state.x1 (remaining - 8);
state.x1 <- Int64.logxor state.x1 (Endian.padding (remaining - 8)))
else (
state.x0 <-
Int64.logxor state.x0
(Endian.load_partial_le plaintext !offset remaining);
Endian.store_partial_le ciphertext !offset state.x0 remaining;
state.x0 <- Int64.logxor state.x0 (Endian.padding remaining));
let tag = finalize state k0 k1 in
(ciphertext, tag)
let decrypt ~key ~nonce ~associated_data ~ciphertext ~tag =
if Bytes.length tag <> tag_size then Error `Invalid_tag_length
else
let state, k0, k1 = initialize key nonce in
absorb_associated_data state associated_data;
let length = Bytes.length ciphertext in
let plaintext = Bytes.create length in
let offset = ref 0 in
while length - !offset >= 16 do
let c0 = Endian.load64_le ciphertext !offset in
let c1 = Endian.load64_le ciphertext (!offset + 8) in
Endian.store64_le plaintext !offset (Int64.logxor state.x0 c0);
Endian.store64_le plaintext (!offset + 8) (Int64.logxor state.x1 c1);
state.x0 <- c0;
state.x1 <- c1;
Permutation.p8 state;
offset := !offset + 16
done;
let remaining = length - !offset in
if remaining >= 8 then (
let c0 = Endian.load64_le ciphertext !offset in
let tail = remaining - 8 in
let c1 = Endian.load_partial_le ciphertext (!offset + 8) tail in
Endian.store64_le plaintext !offset (Int64.logxor state.x0 c0);
Endian.store_partial_le plaintext (!offset + 8)
(Int64.logxor state.x1 c1) tail;
state.x0 <- c0;
state.x1 <- Endian.replace_low_bytes state.x1 c1 tail;
state.x1 <- Int64.logxor state.x1 (Endian.padding tail))
else (
let c0 = Endian.load_partial_le ciphertext !offset remaining in
Endian.store_partial_le plaintext !offset (Int64.logxor state.x0 c0)
remaining;
state.x0 <- Endian.replace_low_bytes state.x0 c0 remaining;
state.x0 <- Int64.logxor state.x0 (Endian.padding remaining));
let expected_tag = finalize state k0 k1 in
if Constant_time.equal expected_tag tag then Ok plaintext
else (
Bytes.fill plaintext 0 length '\000';
Error `Authentication_failure)
let encrypt_combined ~key ~nonce ~associated_data ~plaintext =
let ciphertext, tag = encrypt ~key ~nonce ~associated_data ~plaintext in
let ciphertext_length = Bytes.length ciphertext in
if ciphertext_length > Sys.max_string_length - tag_size then
invalid_arg "Ascon combined ciphertext is too long";
let combined = Bytes.create (ciphertext_length + tag_size) in
Bytes.blit ciphertext 0 combined 0 ciphertext_length;
Bytes.blit tag 0 combined ciphertext_length tag_size;
combined
let decrypt_combined ~key ~nonce ~associated_data combined =
let length = Bytes.length combined in
if length < tag_size then Error `Invalid_tag_length
else
let ciphertext_length = length - tag_size in
let ciphertext = Bytes.sub combined 0 ciphertext_length in
let tag = Bytes.sub combined ciphertext_length tag_size in
decrypt ~key ~nonce ~associated_data ~ciphertext ~tag
end
module Hash256 = struct
type ctx = Sponge.absorbing
let digest_size = 32
let iv = 0x0000080100cc0002L
let init () = Sponge.init ~iv
let feed = Sponge.absorb
let feed_string context input = feed context (Bytes.of_string input)
let get context =
let squeezing = Sponge.finish context in
snd (Sponge.squeeze squeezing digest_size)
let digest input = get (feed (init ()) input)
let digest_string input = digest (Bytes.of_string input)
end
module Xof128 = struct
type absorbing = Sponge.absorbing
type squeezing = Sponge.squeezing
type length_error = [ `Invalid_length ]
let iv = 0x0000080000cc0003L
let init () = Sponge.init ~iv
let absorb = Sponge.absorb
let start_squeezing = Sponge.finish
let valid_length length = length >= 0 && length <= Sys.max_string_length
let valid_digest_length length = length > 0 && length <= Sys.max_string_length
let squeeze state ~length =
if valid_length length then Ok (Sponge.squeeze state length)
else Error `Invalid_length
let digest input ~length =
if not (valid_digest_length length) then Error `Invalid_length
else
let state = start_squeezing (absorb (init ()) input) in
Ok (snd (Sponge.squeeze state length))
end
module Cxof128 = struct
type absorbing = Sponge.absorbing
type squeezing = Sponge.squeezing
type error = [ `Customization_too_long | `Invalid_length ]
let iv = 0x0000080000cc0004L
let init ~customization =
let length = Bytes.length customization in
if length > 256 then Error `Customization_too_long
else
let initial_state = State.create iv 0L 0L 0L 0L in
Permutation.p12 initial_state;
initial_state.x0 <-
Int64.logxor initial_state.x0 (Int64.of_int (length * 8));
Permutation.p12 initial_state;
let customization_context = Sponge.of_state initial_state in
let customization_state =
Sponge.finish_state (Sponge.absorb customization_context customization)
in
Ok (Sponge.of_state customization_state)
let absorb = Sponge.absorb
let start_squeezing = Sponge.finish
let valid_length length = length >= 0 && length <= Sys.max_string_length
let valid_digest_length length = length > 0 && length <= Sys.max_string_length
let squeeze state ~length =
if valid_length length then Ok (Sponge.squeeze state length)
else Error `Invalid_length
let digest ~customization ~message ~length =
if not (valid_digest_length length) then Error `Invalid_length
else
match init ~customization with
| Error `Customization_too_long -> Error `Customization_too_long
| Ok state ->
let state = start_squeezing (absorb state message) in
Ok (snd (Sponge.squeeze state length))
end