Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,102 @@
|
||||
// Copyright © 2025 blackshirt.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// This file contains a building block for eXtended ChaCha20 stream cipher (XChaCha20) construction.
|
||||
// Its based on https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03
|
||||
// Note: so, its maybe outdated...
|
||||
// Beside above draft that defines XChaCha20 construction with 32-bit internal counter,
|
||||
// this XChaCha20 construction was expanded to support 64-bit counter.
|
||||
// There are nothing RFC draft or published standard that can be used as a reference.
|
||||
// Fortunatelly, this construct commonly implemented in popular chacha20 libraries.
|
||||
module chacha20
|
||||
|
||||
import encoding.binary
|
||||
|
||||
// HChaCha20 nonce size
|
||||
const h_nonce_size = 16
|
||||
|
||||
// hchacha20 are intermediary step to build XChaCha20 and initialized the same way as the ChaCha20 cipher,
|
||||
// except hchacha20 use a 128-bit (16 byte) nonce and has no counter to derive subkey.
|
||||
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03#section-2.2
|
||||
@[direct_array_access]
|
||||
fn hchacha20(key []u8, nonce []u8) ![]u8 {
|
||||
// early bound check
|
||||
if key.len != key_size {
|
||||
return error('xchacha: Bad key size')
|
||||
}
|
||||
if nonce.len != h_nonce_size {
|
||||
return error('xchacha: Bad nonce size')
|
||||
}
|
||||
// initializes ChaCha20 state
|
||||
mut x := State{}
|
||||
x[0] = cc0
|
||||
x[1] = cc1
|
||||
x[2] = cc2
|
||||
x[3] = cc3
|
||||
|
||||
x[4] = binary.little_endian_u32(key[0..4])
|
||||
x[5] = binary.little_endian_u32(key[4..8])
|
||||
x[6] = binary.little_endian_u32(key[8..12])
|
||||
x[7] = binary.little_endian_u32(key[12..16])
|
||||
|
||||
x[8] = binary.little_endian_u32(key[16..20])
|
||||
x[9] = binary.little_endian_u32(key[20..24])
|
||||
x[10] = binary.little_endian_u32(key[24..28])
|
||||
x[11] = binary.little_endian_u32(key[28..32])
|
||||
|
||||
// we have no counter
|
||||
x[12] = binary.little_endian_u32(nonce[0..4])
|
||||
x[13] = binary.little_endian_u32(nonce[4..8])
|
||||
x[14] = binary.little_endian_u32(nonce[8..12])
|
||||
x[15] = binary.little_endian_u32(nonce[12..16])
|
||||
|
||||
// After initialization, proceed through the ChaCha20 rounds as usual.
|
||||
x.qround(10)
|
||||
|
||||
// Once the 20 ChaCh20 rounds have been completed, the first 128 bits (16 bytes) and
|
||||
// last 128 bits (16 bytes) of the ChaCha state (both little-endian) are
|
||||
// concatenated, and this 256-bit (32 bytes) subkey is returned.
|
||||
mut out := []u8{len: 32}
|
||||
binary.little_endian_put_u32(mut out[0..4], x[0])
|
||||
binary.little_endian_put_u32(mut out[4..8], x[1])
|
||||
binary.little_endian_put_u32(mut out[8..12], x[2])
|
||||
binary.little_endian_put_u32(mut out[12..16], x[3])
|
||||
|
||||
binary.little_endian_put_u32(mut out[16..20], x[12])
|
||||
binary.little_endian_put_u32(mut out[20..24], x[13])
|
||||
binary.little_endian_put_u32(mut out[24..28], x[14])
|
||||
binary.little_endian_put_u32(mut out[28..32], x[15])
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// derive_xchacha20_key_nonce derives a new key and nonce for eXtended ChaCha20 construction.
|
||||
// It accepts boolean `flag64` flag as the last parameters.
|
||||
// When its set into true, it would be used as an indicator of a 64-bit counter construction.
|
||||
@[direct_array_access; inline]
|
||||
fn derive_xchacha20_key_nonce(key []u8, nonce []u8, flag64 bool) !([]u8, []u8) {
|
||||
// Its only for x_nonce_size
|
||||
if nonce.len != x_nonce_size {
|
||||
return error('Bad nonce size for derive_xchacha20_key_nonce')
|
||||
}
|
||||
// derives a new key based on XChaCha20 construction
|
||||
// first, use 16 bytes of nonce used to derive the key
|
||||
new_key := hchacha20(key, nonce[0..16])!
|
||||
remaining_nonce := nonce[16..24].clone()
|
||||
|
||||
// derive a new nonce based on the flag64 flag.
|
||||
// If flag64 was true, its intended to build XChaCha20 original variant with 64-bit counter.
|
||||
// Otherwise, its a XChaCha20 standard variant with 32-bit counter
|
||||
new_nonce := if flag64 {
|
||||
// use the remaining 8-bytes nonce
|
||||
remaining_nonce
|
||||
} else {
|
||||
// and the last of 8 bytes of nonce copied into to build nonce_size length of new nonce.
|
||||
mut nonce12 := []u8{len: nonce_size}
|
||||
_ := copy(mut nonce12[4..12], remaining_nonce)
|
||||
nonce12
|
||||
}
|
||||
|
||||
return new_key, new_nonce
|
||||
}
|
||||
Reference in new issue
Block a user