Skip to content

Commit 287e9a8

Browse files
authored
x.crypto.chacha20: clean up as preliminary work to support 64-bit counter (#24038)
1 parent 1b52538 commit 287e9a8

File tree

4 files changed

+29
-79
lines changed

4 files changed

+29
-79
lines changed

vlib/x/crypto/chacha20/chacha.v

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,23 @@ pub fn new_cipher(key []u8, nonce []u8) !&Cipher {
6161
// and XChaCha20 with 192 bits nonce. Internally, encrypt start with 0's counter value.
6262
// If you want more control, use Cipher instance and setup the counter by your self.
6363
pub fn encrypt(key []u8, nonce []u8, plaintext []u8) ![]u8 {
64-
return encrypt_with_counter(key, nonce, u32(0), plaintext)
64+
mut c := new_cipher(key, nonce)!
65+
mut out := []u8{len: plaintext.len}
66+
67+
c.encrypt(mut out, plaintext)
68+
unsafe { c.reset() }
69+
return out
6570
}
6671

6772
// decrypt does reverse of encrypt operation by decrypting ciphertext with ChaCha20 cipher
6873
// instance with provided key and nonce.
6974
pub fn decrypt(key []u8, nonce []u8, ciphertext []u8) ![]u8 {
70-
return encrypt_with_counter(key, nonce, u32(0), ciphertext)
75+
mut c := new_cipher(key, nonce)!
76+
mut out := []u8{len: ciphertext.len}
77+
78+
c.encrypt(mut out, ciphertext)
79+
unsafe { c.reset() }
80+
return out
7181
}
7282

7383
// xor_key_stream xors each byte in the given slice in the src with a byte from the
@@ -192,9 +202,19 @@ pub fn (mut c Cipher) encrypt(mut dst []u8, src []u8) {
192202
}
193203
}
194204

195-
// chacha20_block_generic generates ChaCha20 generic keystream
205+
// chacha20_block_generic generates a generic ChaCha20 keystream.
206+
// This is main building block for ChaCha20 keystream generator.
207+
// This routine was intended to work only for msg source with multiples of block_size in size.
196208
@[direct_array_access]
197209
fn (mut c Cipher) chacha20_block_generic(mut dst []u8, src []u8) {
210+
// ChaCha20 keystream generator was relatively easy to understand.
211+
// Its contains steps:
212+
// - Loads current ChaCha20 into temporary state, used for later.
213+
// - Performs quarter_round function on this state and returns some new state.
214+
// - Adds back the new state with the old state.
215+
// - Performs xor-ing between src bytes (loaded as little endian number) with result from previous step.
216+
// - Serializes, in little endian form, this xor-ed state into destination buffer.
217+
//
198218
// Makes sure its works for size of multiple of block_size
199219
if dst.len != src.len || dst.len % block_size != 0 {
200220
panic('chacha20: internal error: wrong dst and/or src length')
@@ -370,10 +390,6 @@ fn (mut c Cipher) do_rekey(key []u8, nonce []u8) ! {
370390
return error('chacha20: wrong nonce size')
371391
}
372392

373-
// bounds check elimination hint
374-
_ = keys[key_size - 1]
375-
_ = nonces[nonce_size - 1]
376-
377393
// setup ChaCha20 cipher key
378394
c.key[0] = binary.little_endian_u32(keys[0..4])
379395
c.key[1] = binary.little_endian_u32(keys[4..8])
@@ -426,33 +442,3 @@ fn quarter_round(a u32, b u32, c u32, d u32) (u32, u32, u32, u32) {
426442

427443
return ax, bx, cx, dx
428444
}
429-
430-
// encrypt_with_counter encrypts plaintext with internal counter set to ctr
431-
fn encrypt_with_counter(key []u8, nonce []u8, ctr u32, plaintext []u8) ![]u8 {
432-
if key.len != key_size {
433-
return error('bad key size')
434-
}
435-
if nonce.len == x_nonce_size {
436-
ciphertext := xchacha20_encrypt_with_counter(key, nonce, ctr, plaintext)!
437-
return ciphertext
438-
}
439-
if nonce.len == nonce_size {
440-
ciphertext := chacha20_encrypt_with_counter(key, nonce, ctr, plaintext)!
441-
return ciphertext
442-
}
443-
return error('Wrong nonce size')
444-
}
445-
446-
fn chacha20_encrypt(key []u8, nonce []u8, plaintext []u8) ![]u8 {
447-
return chacha20_encrypt_with_counter(key, nonce, u32(0), plaintext)
448-
}
449-
450-
fn chacha20_encrypt_with_counter(key []u8, nonce []u8, ctr u32, plaintext []u8) ![]u8 {
451-
mut c := new_cipher(key, nonce)!
452-
c.set_counter(ctr)
453-
mut out := []u8{len: plaintext.len}
454-
455-
c.encrypt(mut out, plaintext)
456-
457-
return out
458-
}

vlib/x/crypto/chacha20/chacha_test.v

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
module chacha20
22

3-
import crypto.cipher
43
import rand
54
import encoding.hex
65

@@ -33,24 +32,6 @@ fn test_xor_key_stream_consecutive() {
3332
assert dst2 == [u8(40), 17, 78, 116, 255, 224, 2, 52, 92, 151, 103, 107, 138]
3433
}
3534

36-
struct StreamCipher {
37-
mut:
38-
cipher &cipher.Stream
39-
}
40-
41-
// Verify chahca20.Cipher implements chiper.Stream correctly.
42-
fn test_chacha20_stream_cipher() ! {
43-
mut key := []u8{len: 32}
44-
mut nonce := []u8{len: 12}
45-
rand.read(mut key)
46-
rand.read(mut nonce)
47-
48-
mut c := new_cipher(key, nonce)!
49-
s := StreamCipher{
50-
cipher: c
51-
}
52-
}
53-
5435
fn test_chacha20_cipher_reset() ! {
5536
mut key := []u8{len: 32}
5637
mut nonce := []u8{len: 12}

vlib/x/crypto/chacha20/xchacha.v

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,3 @@ fn xchacha20(key []u8, nonce []u8) ![]u8 {
7474

7575
return out
7676
}
77-
78-
// eXtended ChaCha20 (XChaCha20) encrypt function
79-
// see https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03#section-2.3.1
80-
fn xchacha20_encrypt(key []u8, nonce []u8, plaintext []u8) ![]u8 {
81-
return xchacha20_encrypt_with_counter(key, nonce, u32(0), plaintext)
82-
}
83-
84-
fn xchacha20_encrypt_with_counter(key []u8, nonce []u8, ctr u32, plaintext []u8) ![]u8 {
85-
// bound check elimination
86-
_ = nonce[x_nonce_size - 1]
87-
subkey := xchacha20(key, nonce[0..16])!
88-
mut cnonce := nonce[16..24].clone()
89-
90-
cnonce.prepend([u8(0x00), 0x00, 0x00, 0x00])
91-
92-
ciphertext := chacha20_encrypt_with_counter(subkey, cnonce, ctr, plaintext)!
93-
94-
return ciphertext
95-
}

vlib/x/crypto/chacha20/xchacha_test.v

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ fn test_xchacha20_encrypt_vector_test_a321() ! {
3838
nonce_bytes := hex.decode(nonce)!
3939
ciphertext_bytes := hex.decode(ciphertext)!
4040

41-
encrypted_message := xchacha20_encrypt_with_counter(key_bytes, nonce_bytes, counter,
42-
plaintext_bytes) or { return }
41+
encrypted_message := encrypt(key_bytes, nonce_bytes, plaintext_bytes) or { return }
4342

4443
assert encrypted_message == ciphertext_bytes
4544
}
@@ -60,8 +59,11 @@ fn test_xchach20_encrypt_vector_test_a322() ! {
6059
nonce_bytes := hex.decode(nonce)!
6160
ciphertext_bytes := hex.decode(ciphertext)!
6261

63-
encrypted_message := xchacha20_encrypt_with_counter(key_bytes, nonce_bytes, counter,
64-
plaintext_bytes) or { return }
62+
mut c := new_cipher(key_bytes, nonce_bytes)!
63+
c.set_counter(counter)
64+
65+
mut encrypted_message := []u8{len: plaintext_bytes.len}
66+
c.encrypt(mut encrypted_message, plaintext_bytes)
6567

6668
assert encrypted_message == ciphertext_bytes
6769
}

0 commit comments

Comments
 (0)