// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Generates one serialized ChaCha block with scalar rounds.
#cfg(not(any(target="native", target="wasm")))
fn chachaBlock(
key : FixedArray[UInt],
count : UInt,
nonce : FixedArray[UInt],
round : UInt,
output : FixedArray[Byte],
) -> Unit {
guard! key.length() == 8
guard! nonce.length() == 3
guard! output.length() == 64
let state = FixedArray::make(16, 0U)
state[0] = 0X61707865U
state[1] = 0X3320646eU
state[2] = 0X79622d32U
state[3] = 0X6b206574U
key.blit_to(state, len=8, dst_offset=4)
state[12] = count
nonce.blit_to(state, len=3, dst_offset=13)
state.chachaBlockLoop(round)
state[0] += 0X61707865U
state[1] += 0X3320646eU
state[2] += 0X79622d32U
state[3] += 0X6b206574U
for i in 0..<8 {
state[i + 4] += key[i]
}
state[12] += count
for i in 0..<3 {
state[i + 13] += nonce[i]
}
for i in 0..<16 {
let word = state[i]
output[i * 4] = word.to_byte()
output[i * 4 + 1] = (word >> 8).to_byte()
output[i * 4 + 2] = (word >> 16).to_byte()
output[i * 4 + 3] = (word >> 24).to_byte()
}
}
///|
/// Generates one ChaCha block with one state row per vector. Each lane holds
/// one consecutive word, so a row rotation aligns the lanes for the diagonal
/// rounds and its inverse restores the serialized state order.
#cfg(any(target="native", target="wasm"))
fn chachaBlock(
key : FixedArray[UInt],
count : UInt,
nonce : FixedArray[UInt],
round : UInt,
output : FixedArray[Byte],
) -> Unit {
guard! key.length() == 8
guard! nonce.length() == 3
let initial_a = @v128.i32x4_const(
0X61707865, 0X3320646e, 0X79622d32, 0X6b206574,
)
let initial_b = @v128.i32x4_const(key[0], key[1], key[2], key[3])
let initial_c = @v128.i32x4_const(key[4], key[5], key[6], key[7])
let initial_d = @v128.i32x4_const(count, nonce[0], nonce[1], nonce[2])
let mut a = initial_a
let mut b = initial_b
let mut c = initial_c
let mut d = initial_d
for _ in 0U.. Unit {
for i in 0.. Unit {
data.blit_to(
target,
len=length,
src_offset=input_offset,
dst_offset=target_offset,
)
let simd_end = length / 16 * 16
for relative_offset = 0
relative_offset < simd_end
relative_offset = relative_offset + 16 {
@v128.v128_store(
target,
target_offset + relative_offset,
@v128.v128_xor(
@v128.v128_load(target, target_offset + relative_offset),
@v128.v128_load(key_stream, key_stream_offset + relative_offset),
),
)
}
for relative_offset in simd_end..