Skip to content

Commit 75e78ab

Browse files
authored
x.crypto.chacha20poly1305: move up responsibility for allocs into higher caller (#25574)
1 parent b68897c commit 75e78ab

2 files changed

Lines changed: 37 additions & 34 deletions

File tree

vlib/x/crypto/chacha20poly1305/psiv.v

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,22 @@ pub fn (c Chacha20Poly1305RE) encrypt(plaintext []u8, nonce []u8, ad []u8) ![]u8
102102
return error('Chacha20Poly1305RE.encrypt: bad nonce length, only support 12-bytes nonce')
103103
}
104104

105-
// clone the initial poly1305
105+
// clone the initial poly1305 state and updates it with additional data ad
106106
mut po_ad := c.po.clone()
107107
update_with_padding(mut po_ad, ad)
108-
108+
// make a clone of updated poly1305
109109
mut po_ad_clone := po_ad.clone()
110-
// build the tag
111-
tag := psiv_gen_tag(mut po_ad_clone, plaintext, ad.len, c.mac_key, nonce)
112-
enc := psiv_encrypt_internal(plaintext, c.enc_key, tag, nonce)!
113110

114-
// setup destination buffer
115-
mut out := []u8{cap: plaintext.len + tag_size}
116-
out << enc
117-
out << tag
111+
// setup output buffer
112+
mut out := []u8{len: plaintext.len + tag_size}
113+
// write out an authentication tag into the last tag_size bytes of output
114+
psiv_gen_tag(mut out[plaintext.len..], mut po_ad_clone, plaintext, ad.len, c.mac_key,
115+
nonce)
116+
// write out authenticated encrypted plaintext into the first plaintext.len bytes of output
117+
psiv_encrypt_internal(mut out[0..plaintext.len], plaintext, c.enc_key, out[plaintext.len..],
118+
nonce)!
118119

120+
// return the result
119121
return out
120122
}
121123

@@ -133,19 +135,27 @@ pub fn (c Chacha20Poly1305RE) decrypt(ciphertext []u8, nonce []u8, ad []u8) ![]u
133135
enc := ciphertext[0..ciphertext.len - c.overhead()]
134136
tag := ciphertext[ciphertext.len - c.overhead()..]
135137

138+
// updates a clone of poly1305 with additional data
136139
mut po_with_ad := c.po.clone()
137140
update_with_padding(mut po_with_ad, ad)
138-
139-
out := psiv_encrypt_internal(enc, c.enc_key, tag, nonce)!
140141
mut poad_clone := po_with_ad.clone()
141-
mac := psiv_gen_tag(mut poad_clone, out, ad.len, c.mac_key, nonce)
142+
143+
// generates authenticated encrypted plaintext with associated mac
144+
mut out := []u8{len: enc.len}
145+
psiv_encrypt_internal(mut out, enc, c.enc_key, tag, nonce)!
146+
147+
mut mac := []u8{len: tag_size}
148+
psiv_gen_tag(mut mac, mut poad_clone, out, ad.len, c.mac_key, nonce)
149+
150+
// check if authentication tag was matching or error on fails.
142151
if subtle.constant_time_compare(mac, tag) != 1 {
143152
unsafe {
144153
out.free()
145154
mac.free()
146155
}
147156
return error('unmatching tag')
148157
}
158+
// return the decrypted ciphertext
149159
return out
150160
}
151161

@@ -155,12 +165,11 @@ pub fn (c Chacha20Poly1305RE) decrypt(ciphertext []u8, nonce []u8, ad []u8) ![]u
155165
// psiv_encrypt_internal is an internal encryption routine used by the core of psiv construct
156166
// for encrypting (or decrypting) message.
157167
@[direct_array_access]
158-
fn psiv_encrypt_internal(plaintext []u8, key []u8, tag []u8, nonce []u8) ![]u8 {
168+
fn psiv_encrypt_internal(mut dst []u8, plaintext []u8, key []u8, tag []u8, nonce []u8) ! {
159169
// loads the counter from the first 8-bytes of the tag input
160170
mut ctr := binary.little_endian_u64(tag[0..8])
161171

162172
// setup some temporary vars
163-
mut dst := []u8{len: plaintext.len}
164173
mut tc := []u8{len: 8} // counter buffer
165174
mut s := chacha20.State{}
166175
mut b64 := []u8{len: 64} // state buffer
@@ -195,46 +204,43 @@ fn psiv_encrypt_internal(plaintext []u8, key []u8, tag []u8, nonce []u8) ![]u8 {
195204
}
196205
n += want_len
197206
}
198-
// reset (release) temporary allocated resources and return the result.
207+
// explicitly reset (release) temporary allocated resources and return the result.
199208
unsafe {
200209
tc.free()
201210
s.reset()
202211
b64.free()
203212
tt.free()
204213
}
205-
return dst
206214
}
207215

208216
// psiv_gen_tag computes a tag from the key, nonce, and Poly1305 tag of the associated data
209217
// and plaintext using the ChaCha20 permutation with the feed-forward, truncating the output.
210218
@[direct_array_access]
211-
fn psiv_gen_tag(mut po poly1305.Poly1305, input []u8, ad_len int, mac_key []u8, nonce []u8) []u8 {
219+
fn psiv_gen_tag(mut out []u8, mut po poly1305.Poly1305, input []u8, ad_len int, mac_key []u8, nonce []u8) {
212220
// updates poly1305 mac by input message, associated data length and input length.
213221
update_with_padding(mut po, input)
214222
po.update(length_to_block(ad_len, input.len))
215223

216224
// produces 16-bytes of mac from current poly1305 state.
217-
mut digest := []u8{len: tag_size}
218-
po.finish(mut digest)
225+
po.finish(mut out)
219226

220227
// The tag was produced from derived key scrambled with chacha20 quarter round routine,
221228
// and then truncating the output into 16-bytes tag.
222-
drv_key := merge_drv_key(mac_key, nonce, digest[0..8], digest[8..16])
229+
drv_key := merge_drv_key(mac_key, nonce, out[0..8], out[8..16])
223230
mut x := chacha20.State{}
224231
unpack_into_state(mut x, drv_key)
225232
ws := chacha20_core(x)
226233

227-
// truncating state output into tag_sized bytes. As a note, we reuse digest buffer allocated
228-
// on previous step to store the result.
229-
pack16_from_state(mut digest, ws)
234+
// truncating state output into tag_sized bytes. As a note, we reuse buffer previously allocated
235+
// to store the result.
236+
pack16_from_state(mut out, ws)
230237

231-
// releases (reset) temporary allocated resources and return the result.
238+
// explicitly releases (reset) temporary allocated resources
232239
unsafe {
233240
drv_key.free()
234241
ws.reset()
235242
x.reset()
236243
}
237-
return digest
238244
}
239245

240246
// psiv_init initializes and expands master key into desired psiv needed construct.
@@ -479,12 +485,6 @@ fn chacha20_core(s chacha20.State) chacha20.State {
479485
return ws
480486
}
481487

482-
// split_tag splits 16-bytes of tag into two's 8-bytes block.
483-
@[direct_array_access; inline]
484-
fn split_tag(tag []u8) ([]u8, []u8) {
485-
return tag[0..8].clone(), tag[8..16].clone()
486-
}
487-
488488
// length_to_block transforms two's length in len1 and len2 into 16-bytes block
489489
@[inline]
490490
fn length_to_block(len1 int, len2 int) []u8 {

vlib/x/crypto/chacha20poly1305/psiv_test.v

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,14 @@ fn test_psiv_insternal_encryption_of_encrypted_text_is_plaintext() ! {
139139
tag := rand.bytes(16)!
140140
nonce := rand.bytes(12)!
141141

142-
out := psiv_encrypt_internal(input, key, tag, nonce)!
142+
mut out := []u8{len: input.len}
143+
psiv_encrypt_internal(mut out, input, key, tag, nonce)!
143144

144145
// encrypting this output with the same params was result in original input
145-
awal := psiv_encrypt_internal(out, key, tag, nonce)!
146-
assert awal == input
146+
// make a clone of ciphertext output as an input into internal encrypt routine
147+
text := out.clone()
148+
psiv_encrypt_internal(mut out, text, key, tag, nonce)!
149+
assert out == input
147150
}
148151
}
149152

0 commit comments

Comments
 (0)