Skip to content

Commit 6569e7f

Browse files
committed
Use a 64-bits counter for ChaCha
We already store the data in the matrix as 64 bits integers, so there's no point in restricting the block counter to 32 bits values. Allowing the full 64 bits makes the likelihood of running into an overflow practically impossible. Changelog: changed
1 parent 912eeca commit 6569e7f

File tree

2 files changed

+18
-22
lines changed

2 files changed

+18
-22
lines changed

std/src/std/crypto/chacha.inko

+3-22
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import std.crypto.cipher (Cipher)
99
import std.crypto.math (rotate_left_u32, to_u32)
1010
import std.endian.little
11+
import std.int (MAX as INT_MAX)
1112

1213
# The ChaCha key size in bytes.
1314
let KEY_SIZE = 256 / 8
@@ -24,9 +25,6 @@ let XCHACHA_NONCE_SIZE = 192 / 8
2425
# The size in bytes of a ChaCha block.
2526
let BLOCK_SIZE = 64
2627

27-
# The maximum value of the block counter.
28-
let MAX_COUNTER = 2 ** 32 - 1
29-
3028
# The number of values in a matrix.
3129
let MATRIX_SIZE = 16
3230

@@ -45,11 +43,7 @@ fn nonce_size_error(expected: Int, size: Int) -> Never {
4543
}
4644

4745
fn counter_size_error(value: Int) -> Never {
48-
panic('the block counter (${value}) must be between 0 and ${MAX_COUNTER}')
49-
}
50-
51-
fn counter_overflow_error(value: Int) -> Never {
52-
panic('the block counter (${value}) overflowed after ${MAX_COUNTER} blocks')
46+
panic('the block counter (${value}) must be between 0 and ${INT_MAX}')
5347
}
5448

5549
# Derives a sub-key from a secret key and nonce, using the HChaCha20 algorithm.
@@ -334,11 +328,7 @@ type pub inline ChaCha {
334328
# This method panics if the value doesn't fit in the range valid for an
335329
# unsigned 32-bits integer.
336330
fn pub mut counter=(value: Int) {
337-
if value < 0 or value > MAX_COUNTER {
338-
counter_size_error(value)
339-
} else {
340-
@matrix.set(12, value)
341-
}
331+
if value < 0 { counter_size_error(value) } else { @matrix.set(12, value) }
342332
}
343333

344334
fn mut apply(bytes: mut ByteArray) {
@@ -362,15 +352,6 @@ type pub inline ChaCha {
362352
# unsigned integer.
363353
let new_size = @matrix.get(12) + 1
364354

365-
# The original implementation makes no attempt at protecting the user from
366-
# overflowing the counter, as it's unlikely to happen in the first place.
367-
# Since we use a 32-bits counter it's still highly unlikely, but more
368-
# likely compared to using a 64-bits counter. Because it's so unlikely for
369-
# this to happen in practise we simply panic, instead of complicating the
370-
# API by forcing the user to handle errors that won't occur in 99.99999%
371-
# of all use cases.
372-
if new_size > MAX_COUNTER { counter_overflow_error(new_size) }
373-
374355
@matrix.set(12, new_size)
375356

376357
if len <= BLOCK_SIZE {

std/test/std/crypto/test_chacha.inko

+15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import std.crypto.chacha (ChaCha, XChaCha, hchacha20)
2+
import std.int (MAX as INT_MAX)
23
import std.test (Tests)
34

45
fn chacha_test_data -> Array[
@@ -361,6 +362,20 @@ fn pub tests(t: mut Tests) {
361362
t.equal(secret.into_string, 'hello')
362363
})
363364

365+
t.test('ChaCha.encrypt with a very large block counter', fn (t) {
366+
let key = ByteArray.filled(with: 1, times: 32)
367+
let nonce = ByteArray.filled(with: 1, times: 12)
368+
let cipher = ChaCha.new(key, nonce)
369+
let secret = 'hello'.to_byte_array
370+
371+
cipher.counter = INT_MAX - 10
372+
cipher.encrypt(secret)
373+
cipher.counter = INT_MAX - 10
374+
cipher.decrypt(secret)
375+
376+
t.equal(secret.to_string, 'hello')
377+
})
378+
364379
t.test('chacha.hchacha20', fn (t) {
365380
let key = ByteArray.from_array(
366381
[

0 commit comments

Comments
 (0)