Skip to content

Commit 5d4b307

Browse files
authored
x.crypto.chacha20poly1305: make implementation use fixed sized arrays more (#25627)
1 parent 7801f5b commit 5d4b307

File tree

2 files changed

+90
-93
lines changed

2 files changed

+90
-93
lines changed

vlib/x/crypto/chacha20poly1305/psiv.v

Lines changed: 86 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,21 @@ pub fn new_psiv(key []u8) !&Chacha20Poly1305RE {
2121
return error('new_psiv: bad key size')
2222
}
2323
// derives and initializes the new key for later purposes
24-
mac_key, enc_key, po := psiv_init(key)!
24+
pol_key := fk_k(key)
25+
mac_key := fm_k(key)
26+
enc_key := fe_k(key)
27+
28+
mut s := chacha20.State{}
29+
mut x64 := [64]u8{}
30+
unsafe { vmemcpy(x64, pol_key[0], 36) }
31+
unpack_into_state(mut s, x64)
32+
ws := chacha20_core(s)
33+
34+
// For poly1305 mac, we only take a first 32-bytes of the state as a key
35+
mut poly1305_key := []u8{len: 32}
36+
pack32_from_state(mut poly1305_key, ws)
37+
po := poly1305.new(poly1305_key)!
38+
2539
// set the values
2640
c := &Chacha20Poly1305RE{
2741
key: key.clone()
@@ -64,8 +78,8 @@ mut:
6478
key []u8
6579
// flags that tells derivation keys has been precomputed
6680
precomp bool
67-
mac_key []u8
68-
enc_key []u8
81+
mac_key [36]u8
82+
enc_key [36]u8
6983
po &poly1305.Poly1305 = unsafe { nil }
7084
}
7185

@@ -74,8 +88,9 @@ mut:
7488
pub fn (mut c Chacha20Poly1305RE) free() {
7589
unsafe {
7690
c.key.free()
77-
c.mac_key.free()
78-
c.enc_key.free()
91+
// we reset derived keys
92+
vmemset(c.mac_key, 0, 36)
93+
vmemset(c.enc_key, 0, 36)
7994
c.po = nil
8095
}
8196
c.precomp = false
@@ -165,15 +180,15 @@ pub fn (c Chacha20Poly1305RE) decrypt(ciphertext []u8, nonce []u8, ad []u8) ![]u
165180
// psiv_encrypt_internal is an internal encryption routine used by the core of psiv construct
166181
// for encrypting (or decrypting) message.
167182
@[direct_array_access]
168-
fn psiv_encrypt_internal(mut dst []u8, plaintext []u8, key []u8, tag []u8, nonce []u8) ! {
183+
fn psiv_encrypt_internal(mut dst []u8, plaintext []u8, dkey [36]u8, tag []u8, nonce []u8) ! {
169184
// loads the counter from the first 8-bytes of the tag input
170185
mut ctr := binary.little_endian_u64(tag[0..8])
171186

172187
// setup some temporary vars
173188
mut tc := []u8{len: 8} // counter buffer
174189
mut s := chacha20.State{}
175-
mut b64 := []u8{len: 64} // state buffer
176-
mut tt := merge_drv_key(key, nonce, tag[0..8], tag[8..16])
190+
mut b64 := [64]u8{} // state buffer
191+
mut tt := merge_drv_key(dkey, nonce, tag[0..8], tag[8..16])
177192

178193
mut j := 0
179194
mut n := 0
@@ -208,15 +223,15 @@ fn psiv_encrypt_internal(mut dst []u8, plaintext []u8, key []u8, tag []u8, nonce
208223
unsafe {
209224
tc.free()
210225
s.reset()
211-
b64.free()
212-
tt.free()
226+
vmemset(b64, 0, b64.len)
227+
vmemset(tt, 0, tt.len)
213228
}
214229
}
215230

216231
// psiv_gen_tag computes a tag from the key, nonce, and Poly1305 tag of the associated data
217232
// and plaintext using the ChaCha20 permutation with the feed-forward, truncating the output.
218233
@[direct_array_access]
219-
fn psiv_gen_tag(mut out []u8, mut po poly1305.Poly1305, input []u8, ad_len int, mac_key []u8, nonce []u8) {
234+
fn psiv_gen_tag(mut out []u8, mut po poly1305.Poly1305, input []u8, ad_len int, mac_key [36]u8, nonce []u8) {
220235
// updates poly1305 mac by input message, associated data length and input length.
221236
update_with_padding(mut po, input)
222237
po.update(length_to_block(ad_len, input.len))
@@ -237,39 +252,12 @@ fn psiv_gen_tag(mut out []u8, mut po poly1305.Poly1305, input []u8, ad_len int,
237252

238253
// explicitly releases (reset) temporary allocated resources
239254
unsafe {
240-
drv_key.free()
255+
vmemset(drv_key, 0, 36)
241256
ws.reset()
242257
x.reset()
243258
}
244259
}
245260

246-
// psiv_init initializes and expands master key into desired psiv needed construct.
247-
@[direct_array_access; inline]
248-
fn psiv_init(key []u8) !([]u8, []u8, &poly1305.Poly1305) {
249-
// derives some keys
250-
pol_key := fk_k(key)
251-
mac_key := fm_k(key)
252-
enc_key := fe_k(key)
253-
254-
mut x := chacha20.State{}
255-
unpack_into_state(mut x, merge_drvk_zeros(pol_key))
256-
ws := chacha20_core(x)
257-
258-
// For poly1305 mac, we only take a first 32-bytes of the state as a key
259-
mut poly1305_key := []u8{len: 32}
260-
pack32_from_state(mut poly1305_key, ws)
261-
po := poly1305.new(poly1305_key)!
262-
263-
// reset (release) temporary allocated resources
264-
unsafe {
265-
x.reset()
266-
ws.reset()
267-
pol_key.free()
268-
poly1305_key.free()
269-
}
270-
return mac_key, enc_key, po
271-
}
272-
273261
// update_with_padding updates poly1305 mac with data, padding the tail block if necessary.
274262
@[direct_array_access; inline]
275263
fn update_with_padding(mut po poly1305.Poly1305, data []u8) {
@@ -283,76 +271,67 @@ fn update_with_padding(mut po poly1305.Poly1305, data []u8) {
283271

284272
// merge_drv_key merges provided bytes into 64-bytes key
285273
@[direct_array_access; inline]
286-
fn merge_drv_key(dkey []u8, nonce []u8, tag_ctr []u8, tag_rest []u8) []u8 {
287-
mut x := []u8{len: 64}
274+
fn merge_drv_key(dkey [36]u8, nonce []u8, tag_ctr []u8, tag_rest []u8) [64]u8 {
275+
mut x64 := [64]u8{}
288276

289277
// 0..36
290278
for i := 0; i < dkey.len; i++ {
291-
x[i] = dkey[i]
279+
x64[i] = dkey[i]
292280
}
293281
// 36..48
294282
for i := 0; i < nonce.len; i++ {
295-
x[36 + i] = nonce[i]
283+
x64[36 + i] = nonce[i]
296284
}
297285
// 48..56
298286
for i := 0; i < tag_ctr.len; i++ {
299-
x[i + 48] = tag_ctr[i]
287+
x64[i + 48] = tag_ctr[i]
300288
}
301289
// 56..64
302290
for i := 0; i < tag_rest.len; i++ {
303-
x[i + 56] = tag_rest[i]
291+
x64[i + 56] = tag_rest[i]
304292
}
305293

306-
return x
307-
}
308-
309-
// merge_drvk_zeros merges derived key in dkey with zeros nonce and zeros tag into 64-bytes of key.
310-
@[direct_array_access; inline]
311-
fn merge_drvk_zeros(dkey []u8) []u8 {
312-
mut x := []u8{len: 64}
313-
_ := copy(mut x, dkey)
314-
// the others was null bytes
315-
return x
294+
return x64
316295
}
317296

318297
// fk_k maps and transforms 32-bytes of key into 36-bytes of new key used to
319298
// derive a poly1305 construction.
320299
// See the papers doc on the 3.3 Additional Details part, on page 12-13
321300
@[direct_array_access; inline]
322-
fn fk_k(k []u8) []u8 {
301+
fn fk_k(key []u8) [36]u8 {
323302
// fk(K) = K1 ∥ K2 ∥ K3 ∥ 03 ∥ K5 ∥ K6 ∥ K7 ∥ 0c ∥ K9 ∥ K10 ∥ K11 ∥ 30
324303
// ∥ K4 ∥ K8 ∥ K12 ∥ c0 ∥ K13 ∥ K14 ∥ · · · ∥ K32
325304
// with 0-based index
326305
// K0 ∥ K1 ∥ K2 ∥ 03 ∥ K4 ∥ K5 ∥ K6 ∥ 0c ∥ K8 ∥ K9 ∥ K10 ∥ 30
327306
// ∥ K3 ∥ K7 ∥ K11 ∥ c0 ∥ K12 ∥ K13 ∥ · · · ∥ K31
328-
mut x := []u8{len: 36}
307+
mut x := [36]u8{}
329308
// 0 .. 4
330309
for i := 0; i < 3; i++ {
331-
x[i] = k[i]
310+
x[i] = key[i]
332311
}
333312
x[3] = u8(0x03)
334313

335314
// 4 .. 8
336315
for i := 4; i < 7; i++ {
337-
x[i] = k[i]
316+
x[i] = key[i]
338317
}
339318
x[7] = 0x0c
340319

341320
// 8 .. 12
342321
for i := 8; i < 11; i++ {
343-
x[i] = k[i]
322+
x[i] = key[i]
344323
}
345324
x[11] = 0x30
346325

347326
// 12 .. 16
348-
x[12] = k[3]
349-
x[13] = k[7]
350-
x[14] = k[11]
327+
x[12] = key[3]
328+
x[13] = key[7]
329+
x[14] = key[11]
351330
x[15] = 0xc0
352331

353332
// 16 .. 36
354333
for i := 16; i < 36; i++ {
355-
x[i] = k[i - 4]
334+
x[i] = key[i - 4]
356335
}
357336

358337
return x
@@ -361,115 +340,131 @@ fn fk_k(k []u8) []u8 {
361340
// fm_k maps and transforms 32-bytes of key into 36-bytes of message authentication key.
362341
// It later used for psiv tag generation.
363342
@[direct_array_access; inline]
364-
fn fm_k(k []u8) []u8 {
343+
fn fm_k(key []u8) [36]u8 {
365344
// fm(K) = K1 ∥ K2 ∥ K3 ∥ 05 ∥ K5 ∥ K6 ∥ K7 ∥ 0a ∥ K9 ∥ K10 ∥ K11 ∥ 50 ∥
366345
// K4 ∥ K8 ∥ K12 ∥ a0 ∥ K13 ∥ K14 ∥ · · · ∥ K32 ,
367346
// Or, with 0-based index
368347
// fm(K) = K0 ∥ K1 ∥ K2 ∥ 05 ∥ K4 ∥ K5 ∥ K6 ∥ 0a ∥ K8 ∥ K9 ∥ K10 ∥ 50 ∥
369348
// K3 ∥ K7 ∥ K11 ∥ a0 ∥ K12 ∥ K13 ∥ · · · ∥ K31
370-
mut x := []u8{len: 36}
349+
mut x := [36]u8{}
371350
// 0 .. 4
372351
for i := 0; i < 3; i++ {
373-
x[i] = k[i]
352+
x[i] = key[i]
374353
}
375354
x[3] = u8(0x05)
376355

377356
// 4 .. 8
378357
for i := 4; i < 7; i++ {
379-
x[i] = k[i]
358+
x[i] = key[i]
380359
}
381360
x[7] = 0x0a
382361

383362
// 8 .. 12
384363
for i := 8; i < 11; i++ {
385-
x[i] = k[i]
364+
x[i] = key[i]
386365
}
387366
x[11] = 0x50
388367

389368
// 12 .. 16
390-
x[12] = k[3]
391-
x[13] = k[7]
392-
x[14] = k[11]
369+
x[12] = key[3]
370+
x[13] = key[7]
371+
x[14] = key[11]
393372
x[15] = 0xa0
394373

395374
// 16 .. 36
396375
for i := 16; i < 36; i++ {
397-
x[i] = k[i - 4]
376+
x[i] = key[i - 4]
398377
}
399378

400379
return x
401380
}
402381

403382
// fe_k maps and transforms 32-bytes of key into 36-bytes of new encryption key
404383
@[direct_array_access; inline]
405-
fn fe_k(k []u8) []u8 {
384+
fn fe_k(key []u8) [36]u8 {
406385
// fe(K) = K1 ∥ K2 ∥ K3 ∥ 06 ∥ K5 ∥ K6 ∥ K7 ∥ 09 ∥ K9 ∥ K10 ∥ K11 ∥ 60 ∥
407386
// K4 ∥ K8 ∥ K12 ∥ 90 ∥ K13 ∥ K14 ∥ · · · ∥ K32
408387
// Or, with 0-based index
409388
// fe(K) = K0 ∥ K1 ∥ K2 ∥ 06 ∥ K4 ∥ K5 ∥ K6 ∥ 09 ∥ K8 ∥ K9 ∥ K10 ∥ 60 ∥
410389
// K3 ∥ K7 ∥ K11 ∥ 90 ∥ K12 ∥ K13 ∥ · · · ∥ K31
411-
mut x := []u8{len: 36}
390+
mut x := [36]u8{}
412391
// 0 .. 4
413392
for i := 0; i < 3; i++ {
414-
x[i] = k[i]
393+
x[i] = key[i]
415394
}
416395
x[3] = u8(0x06)
417396

418397
// 4 .. 8
419398
for i := 4; i < 7; i++ {
420-
x[i] = k[i]
399+
x[i] = key[i]
421400
}
422401
x[7] = 0x09
423402

424403
// 8 .. 12
425404
for i := 8; i < 11; i++ {
426-
x[i] = k[i]
405+
x[i] = key[i]
427406
}
428407
x[11] = 0x60
429408

430409
// 12 .. 16
431-
x[12] = k[3]
432-
x[13] = k[7]
433-
x[14] = k[11]
410+
x[12] = key[3]
411+
x[13] = key[7]
412+
x[14] = key[11]
434413
x[15] = 0x90
435414

436415
// 16 .. 36
437416
for i := 16; i < 36; i++ {
438-
x[i] = k[i - 4]
417+
x[i] = key[i - 4]
439418
}
440419

441420
return x
442421
}
443422

444423
// unpack_into_state deserializes (in little-endian form) 64-bytes of data in x into state s.
445424
@[direct_array_access; inline]
446-
fn unpack_into_state(mut s chacha20.State, x []u8) {
425+
fn unpack_into_state(mut s chacha20.State, x [64]u8) {
447426
for i := 0; i < 16; i++ {
448-
s[i] = binary.little_endian_u32(x[i * 4..(i + 1) * 4])
427+
s[i] = u32(x[i * 4]) | (u32(x[i * 4 + 1]) << u32(8)) | (u32(x[i * 4 + 2]) << u32(16)) | (u32(x[
428+
i * 4 + 3]) << u32(24))
449429
}
450430
}
451431

452432
// pack64_from_state serializes state s into 64-bytes output in little-endian form.
453433
@[direct_array_access; inline]
454-
fn pack64_from_state(mut out []u8, s chacha20.State) {
455-
for i, v in s {
456-
binary.little_endian_put_u32(mut out[i * 4..(i + 1) * 4], v)
434+
fn pack64_from_state(mut out [64]u8, s chacha20.State) {
435+
mut j := 0
436+
for v in s {
437+
out[j] = u8(v)
438+
out[j + 1] = u8(v >> u32(8))
439+
out[j + 2] = u8(v >> u32(16))
440+
out[j + 3] = u8(v >> u32(24))
441+
j += 4
457442
}
458443
}
459444

460445
// pack32_from_state serializes only a half of state s into 32-bytes output in little-endian form.
461446
@[direct_array_access; inline]
462447
fn pack32_from_state(mut out []u8, s chacha20.State) {
463-
for i := 0; i < 8; i++ {
464-
binary.little_endian_put_u32(mut out[i * 4..(i + 1) * 4], s[i])
448+
mut j := 0
449+
for i in 0 .. 8 {
450+
out[j] = u8(s[i])
451+
out[j + 1] = u8(s[i] >> u32(8))
452+
out[j + 2] = u8(s[i] >> u32(16))
453+
out[j + 3] = u8(s[i] >> u32(24))
454+
j += 4
465455
}
466456
}
467457

468458
// pack16_from_state serializes the first quartet of state s into 16-bytes output in little-endian form.
469459
@[direct_array_access; inline]
470460
fn pack16_from_state(mut out []u8, s chacha20.State) {
471-
for i := 0; i < 4; i++ {
472-
binary.little_endian_put_u32(mut out[i * 4..(i + 1) * 4], s[i])
461+
mut j := 0
462+
for i in 0 .. 4 {
463+
out[j] = u8(s[i])
464+
out[j + 1] = u8(s[i] >> u32(8))
465+
out[j + 2] = u8(s[i] >> u32(16))
466+
out[j + 3] = u8(s[i] >> u32(24))
467+
j += 4
473468
}
474469
}
475470

vlib/x/crypto/chacha20poly1305/psiv_test.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,18 @@ fn test_psiv_insternal_encryption_of_encrypted_text_is_plaintext() ! {
136136
for i := 0; i < 1024; i++ {
137137
input := rand.bytes(i)!
138138
key := rand.bytes(36)!
139+
mut dkey := [36]u8{}
140+
unsafe { vmemcpy(dkey, key.data, key.len) }
139141
tag := rand.bytes(16)!
140142
nonce := rand.bytes(12)!
141143

142144
mut out := []u8{len: input.len}
143-
psiv_encrypt_internal(mut out, input, key, tag, nonce)!
145+
psiv_encrypt_internal(mut out, input, dkey, tag, nonce)!
144146

145147
// encrypting this output with the same params was result in original input
146148
// make a clone of ciphertext output as an input into internal encrypt routine
147149
text := out.clone()
148-
psiv_encrypt_internal(mut out, text, key, tag, nonce)!
150+
psiv_encrypt_internal(mut out, text, dkey, tag, nonce)!
149151
assert out == input
150152
}
151153
}

0 commit comments

Comments
 (0)