qrc_opensource_rs/cipher/
csx.rs

1/* The AGPL version 3 License (AGPLv3)
2* 
3* Copyright (c) 2021 Digital Freedom Defence Inc.
4* This file is part of the QSC Cryptographic library
5* 
6* This program is free software : you can redistribute it and / or modify
7* it under the terms of the GNU Affero General Public License as published by
8* the Free Software Foundation, either version 3 of the License, or
9* (at your option) any later version.
10* 
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14* See the GNU Affero General Public License for more details.
15* 
16* You should have received a copy of the GNU Affero General Public License
17* along with this program. If not, see <http://www.gnu.org/licenses/>.
18*
19*
20*
21* Copyright (c) Original-2021 John G. Underhill <john.underhill@mailfence.com>
22* Copyright (c) 2022-Present QRC Eurosmart SA <opensource-support@qrcrypto.ch>
23*
24* The following code is a derivative work of the code from the QSC Cryptographic library in C, 
25* which is licensed AGPLv3. This code therefore is also licensed under the terms of 
26* the GNU Affero General Public License, version 3. The AGPL version 3 License (AGPLv3). */
27
28use crate::{common::common::QRC_SYSTEM_IS_LITTLE_ENDIAN, digest::sha3::{qrc_cshake_initialize, qrc_cshake_squeezeblocks, qrc_keccak_absorb_key_custom, qrc_keccak_dispose, qrc_keccak_finalize, qrc_keccak_initialize_state, qrc_keccak_update, qrc_kmac_finalize, qrc_kmac_initialize, qrc_kmac_update, QrcKeccakRate, QrcKeccakState, QRC_KECCAK_512_RATE, QRC_KECCAK_KMAC_DOMAIN_ID, QRC_KECCAK_PERMUTATION_MIN_ROUNDS, QRC_KECCAK_STATE_SIZE}, tools::intutils::{qrc_intutils_clear64, qrc_intutils_copy64, qrc_intutils_copy8, qrc_intutils_le32to8, qrc_intutils_le64to8, qrc_intutils_le8to64, qrc_intutils_min, qrc_intutils_rotl64, qrc_intutils_transform_8to64, qrc_intutils_verify, qrc_intutils_xor}};
29
30use core::{mem::size_of, default::Default};
31
32#[cfg(feature = "no_std")]
33use alloc::vec::Vec;
34
35/*
36\def QRC_CSX_AUTHENTICATED
37* \brief Enables KMAC authentication mode
38*/
39pub const QRC_CSX_AUTHENTICATED: bool = true;
40
41/*
42* \def QRC_CSX_AUTH_KMAC
43* \brief Sets the authentication mode to standard KMAC-R24.
44* Remove this definition to enable the reduced rounds version using KMAC-R12.
45*/
46pub const QRC_CSX_AUTH_KMAC: bool = false;
47pub const QRC_CSX_KPA_AUTHENTICATION: bool = false;
48
49/*
50\def QRC_CSX_KMAC_R12
51* \brief Enables the reduced rounds KMAC-R12 implementation.
52* Unrem this flag to enable the reduced rounds KMAC implementation.
53*/
54pub const QRC_CSX_AUTH_KMACR12: bool = true;
55
56/*
57\def QRC_CSX_BLOCK_SIZE
58* \brief The internal block size in bytes, required by the encryption and decryption functions
59*/
60pub const QRC_CSX_BLOCK_SIZE: usize = 128;
61
62/*
63\def QRC_CSX_INFO_SIZE
64* \brief The maximum byte length of the info string
65*/
66pub const QRC_CSX_INFO_SIZE: usize = 48;
67
68/*
69\def QRC_CSX_KEY_SIZE
70* \brief The size in bytes of the CSX-512 input cipher-key
71*/
72pub const QRC_CSX_KEY_SIZE: usize = 64;
73
74/*
75\def QRC_CSX_MAC_SIZE
76* \brief The CSX-512 MAC code array length in bytes
77*/
78pub const QRC_CSX_MAC_SIZE: usize = 64;
79
80/*
81\def QRC_CSX_NONCE_SIZE
82* \brief The byte size of the nonce array
83*/
84pub const QRC_CSX_NONCE_SIZE: usize = 16;
85
86/*
87\def QRC_CSX_STATE_SIZE
88* \brief The uint64 size of the internal state array
89*/
90pub const QRC_CSX_STATE_SIZE: usize = 16;
91
92/* 
93* \struct qrc_csx_keyparams
94* \brief The key parameters structure containing key, nonce, and info arrays and lengths.
95* Use this structure to load an input cipher-key and optional info tweak, using the qrc_csx_initialize function.
96* Keys must be random and secret, and align to the corresponding key size of the cipher implemented.
97* The info parameter is optional, and can be a salt or cryptographic key.
98* The nonce is always QRC_CSX_BLOCK_SIZE in length.
99*/
100#[derive(Clone)]
101pub struct QrcCsxKeyparams {
102	pub key: Vec<u8>,			/*< The input cipher key */
103	pub keylen: usize,			/*< The length in bytes of the cipher key */
104	pub nonce: Vec<u8>,			/*< The nonce or initialization vector */
105	pub info: Vec<u8>,			/*< The information tweak */
106	pub infolen: usize,			/*< The length in bytes of the information tweak */
107}
108impl Default for QrcCsxKeyparams{
109    fn default() -> Self {
110        Self {
111            key: Default::default(),
112			keylen: Default::default(),
113			nonce: Default::default(),
114            info: Default::default(),
115            infolen: Default::default(),
116        }
117    }
118}
119
120/* 
121* \struct qrc_csx_state
122* \brief The internal state structure containing the round-key array.
123*/
124#[derive(Clone)]
125pub struct QrcCsxState {
126	pub state: [u64; QRC_CSX_STATE_SIZE],		/*< the primary state array */
127    //pub kstate: qrc_kpa_state, IF QRC_CSX_KPA_AUTHENTICATION		/*< the KPA state structure */
128	pub kstate: QrcKeccakState,					/*< the KMAC state structure */
129	pub counter: u64,       					/*< the processed bytes counter */
130	pub encrypt: bool,							/*< the transformation mode; true for encryption */
131}
132impl Default for QrcCsxState{
133    fn default() -> Self {
134        Self {
135            state: Default::default(),
136			kstate: Default::default(),
137			counter: Default::default(),
138            encrypt: Default::default(),
139        }
140    }
141}
142
143/* public functions */
144
145/*
146* \brief Dispose of the CSX cipher state
147*
148* \warning The dispose function must be called when disposing of the cipher.
149* This function destroys the internal state of the cipher.
150*
151* \param ctx: [struct] The cipher state structure
152*/
153pub fn qrc_csx_dispose(ctx: &mut QrcCsxState) {
154
155	/* clear state */
156	if QRC_CSX_AUTHENTICATED {
157		qrc_keccak_dispose(&mut ctx.kstate);
158	}
159
160	qrc_intutils_clear64(&mut ctx.state, QRC_CSX_STATE_SIZE);
161	ctx.counter = 0;
162	ctx.encrypt = false;
163}
164
165/*
166* \brief Initialize the state with the input cipher-key and optional info tweak.
167*
168* \param ctx: [struct] The cipher state structure
169* \param keyparams: [const][struct] The secret input cipher-key and nonce structure
170* \param encryption: Initialize the cipher for encryption, or false for decryption mode
171*/
172pub fn qrc_csx_initialize(ctx: &mut QrcCsxState, keyparams: QrcCsxKeyparams, encryption: bool) {
173	ctx.counter = 0;
174	ctx.encrypt = encryption;
175
176	if QRC_CSX_AUTHENTICATED {
177
178		let kstate = &mut QrcKeccakState::default();
179		let buf = &mut [0u8; QRC_KECCAK_512_RATE];
180		let cpk = &mut [0u8; QRC_CSX_KEY_SIZE];
181		let mck = &mut [0u8; QRC_CSX_KEY_SIZE];
182		let nme = &mut [0u8; CSX_NAME_LENGTH];
183
184		/* load the information string */
185		if keyparams.infolen == 0 {
186			qrc_intutils_copy8(nme, &CSX_NAME, CSX_NAME_LENGTH);
187		} else {
188			let inflen = qrc_intutils_min(keyparams.infolen, CSX_NAME_LENGTH);
189			qrc_intutils_copy8(nme, &keyparams.info, inflen);
190		}
191
192		/* initialize the cSHAKE generator */
193		let rate = QrcKeccakRate::QrcKeccakRate512 as usize;
194		qrc_cshake_initialize(kstate, rate, &keyparams.key, keyparams.keylen, nme, CSX_NAME_LENGTH, &[], 0);
195
196		/* extract the cipher key */
197		qrc_cshake_squeezeblocks(kstate, rate, buf, 1);
198		qrc_intutils_copy8(cpk, buf, QRC_CSX_KEY_SIZE);
199		csx_load_key(ctx, cpk, &keyparams.nonce, &CSX_INFO);
200
201		/* extract the mac key */
202		qrc_cshake_squeezeblocks(kstate, rate, buf, 1);
203		qrc_intutils_copy8(mck, buf, QRC_CSX_KEY_SIZE);
204
205		/* initialize the mac generator */
206		qrc_intutils_clear64(&mut ctx.kstate.state, QRC_KECCAK_STATE_SIZE);
207
208		if QRC_CSX_AUTH_KMACR12 {
209			qrc_keccak_initialize_state(&mut ctx.kstate);
210			qrc_keccak_absorb_key_custom(&mut ctx.kstate, rate, mck, QRC_CSX_KEY_SIZE, &[], 0, &CSX_KMACR12_NAME, CSX_NAME_LENGTH, QRC_KECCAK_PERMUTATION_MIN_ROUNDS);
211		} else {
212			qrc_kmac_initialize(&mut ctx.kstate, rate, mck, QRC_CSX_KEY_SIZE, &mut [], 0);
213		}
214
215	} else {
216
217		let inf = &mut [0u8; QRC_CSX_INFO_SIZE];
218
219		/* load the information string */
220		if keyparams.infolen == 0 {
221			qrc_intutils_copy8(inf, &CSX_INFO, QRC_CSX_INFO_SIZE);
222		} else {
223			let inflen = qrc_intutils_min(keyparams.infolen, QRC_CSX_INFO_SIZE);
224			qrc_intutils_copy8(inf, &keyparams.info, inflen);
225		}
226
227		qrc_intutils_clear64(&mut ctx.state, QRC_CSX_STATE_SIZE);
228		csx_load_key(ctx, &keyparams.key, &keyparams.nonce, inf);
229	}
230}
231
232/*
233* \brief Set the associated data string used in authenticating the message.
234* The associated data may be packet header information, domain specific data, or a secret shared by a group.
235* The associated data must be set after initialization, and before each transformation call.
236* The data is erased after each call to the transform.
237*
238* \warning The cipher must be initialized before this function can be called
239*
240* \param ctx: [struct] The cipher state structure
241* \param data: [const] The associated data array
242* \param length: The associated data array length
243*/
244pub fn qrc_csx_set_associated(ctx: &mut QrcCsxState, data: &[u8], length: usize) {
245	if length != 0 {
246		let code = &mut [0u8; size_of::<u32>()];
247
248		/* add the ad data to the hash */
249		csx_mac_update(ctx, data, length);
250		/* add the length of the ad */
251		qrc_intutils_le32to8(code, length as u32);
252		csx_mac_update(ctx, code, size_of::<u32>());
253	}
254}
255
256/*
257* \brief Transform an array of bytes.
258* In encryption mode, the input plain-text is encrypted and then an authentication MAC code is appended to the cipher-text.
259* In decryption mode, the input cipher-text is authenticated internally and compared to the MAC code appended to the cipher-text,
260* if the codes to not match, the cipher-text is not decrypted and the call fails.
261*
262* \warning The cipher must be initialized before this function can be called
263*
264* \param ctx: [struct] The cipher state structure
265* \param output: A pointer to the output array
266* \param input: [const] A pointer to the input array
267* \param length: The number of bytes to transform
268*
269* \return: Returns true if the cipher has been transformed the data successfully, false on failure
270*/
271pub fn qrc_csx_transform(ctx: &mut QrcCsxState, output: &mut [u8], input: &[u8], length: usize) -> bool {
272	let mut res = true;
273
274	if QRC_CSX_AUTHENTICATED {
275
276		let ncopy = &mut [0u8; QRC_CSX_NONCE_SIZE];
277		res = false;
278
279		/* store the nonce */
280		qrc_intutils_le64to8(ncopy, ctx.state[12]);
281		qrc_intutils_le64to8(&mut ncopy[size_of::<u64>()..], ctx.state[13]);
282
283		/* update the processed bytes counter */
284		ctx.counter += length as u64;
285
286		/* update the mac with the nonce */
287		csx_mac_update(ctx, ncopy, QRC_CSX_NONCE_SIZE);
288
289		if ctx.encrypt {
290			/* use the transform to generate the key-stream and encrypt the data  */
291			csx_transform(ctx, output, input, length);
292
293			/* update the mac with the cipher-text */
294			csx_mac_update(ctx, output, length);
295
296			/* mac the cipher-text appending the code to the end of the array */
297			csx_finalize(ctx, &mut output[length..]);
298			res = true;
299		} else {
300			let code= &mut [0u8; QRC_CSX_MAC_SIZE];
301
302			/* update the mac with the cipher-text */
303			csx_mac_update(ctx, input, length);
304
305			/* generate the internal mac code */
306			csx_finalize(ctx, code);
307
308			/* compare the mac code with the one embedded in the cipher-text, bypassing the transform if the mac check fails */
309			if qrc_intutils_verify(code, &input[length..], QRC_CSX_MAC_SIZE) == 0 {
310				/* generate the key-stream and decrypt the array */
311				csx_transform(ctx, output, input, length);
312				res = true;
313			}
314		}
315	} else {
316		csx_transform(ctx, output, input, length);	
317	}
318
319	return res;
320}
321
322/*
323* \brief A multi-call transform for a large array of bytes, such as required by file encryption.
324* This call can be used to transform and authenticate a very large array of bytes (+1GB).
325* On the last call in the sequence, set the finalize parameter to true to complete authentication,
326* and write the MAC code to the end of the output array in encryption mode, 
327* or compare to the embedded MAC code and authenticate in decryption mode.
328* In encryption mode, the input plain-text is encrypted, then authenticated, and the MAC code is appended to the cipher-text.
329* In decryption mode, the input cipher-text is authenticated internally and compared to the MAC code appended to the cipher-text,
330* if the codes to not match, the cipher-text is not decrypted and the call fails.
331*
332* \warning The cipher must be initialized before this function can be called
333*
334* \param ctx: [struct] The cipher state structure
335* \param output: A pointer to the output array
336* \param input: [const] A pointer to the input array
337* \param length: The number of bytes to transform
338* \param finalize: Complete authentication on a stream if set to true
339*
340* \return: Returns true if the cipher has been transformed the data successfully, false on failure
341*/
342pub fn qrc_csx_extended_transform(ctx: &mut QrcCsxState, output: &mut [u8], input: &[u8], length: usize, finalize: bool) -> bool {
343	let mut res = true;
344
345	if QRC_CSX_AUTHENTICATED {
346
347		let ncopy = &mut [0u8; QRC_CSX_NONCE_SIZE];
348		res = false;
349
350		/* store the nonce */
351		qrc_intutils_le64to8(ncopy, ctx.state[12]);
352		qrc_intutils_le64to8(&mut ncopy[size_of::<u64>()..], ctx.state[13]);
353
354		/* update the processed bytes counter */
355		ctx.counter += length as u64;
356
357		/* update the mac with the nonce */
358		csx_mac_update(ctx, ncopy, QRC_CSX_NONCE_SIZE);
359
360		if ctx.encrypt {
361			/* use the transform to generate the key-stream and encrypt the data  */
362			csx_transform(ctx, output, input, length);
363
364			/* update the mac with the cipher-text */
365			csx_mac_update(ctx, output, length);
366
367			if finalize	{
368				/* mac the cipher-text appending the code to the end of the array */
369				csx_finalize(ctx, &mut output[length..]);
370			}
371
372			res = true;
373		} else {
374			let code = &mut [0u8; QRC_CSX_MAC_SIZE];
375
376			/* update the mac with the cipher-text */
377			csx_mac_update(ctx, input, length);
378
379			if finalize	{
380				/* generate the internal mac code */
381				csx_finalize(ctx, code);
382
383				/* compare the mac code with the one embedded in the cipher-text, bypassing the transform if the mac check fails */
384				if qrc_intutils_verify(code, &input[length..], QRC_CSX_MAC_SIZE) == 0 {
385					/* generate the key-stream and decrypt the array */
386					csx_transform(ctx, output, input, length);
387					res = true;
388				}
389			} else {
390				/* generate the key-stream and decrypt the array */
391				csx_transform(ctx, output, input, length);
392				res = true;
393			}
394		}
395
396	} else {
397		csx_transform(ctx, output, input, length);
398	}
399
400	return res;
401}
402
403/*
404\def CSX_ROUND_COUNT
405* \brief The number of mixing rounds used by CSX-512
406*/
407const CSX_ROUND_COUNT: usize = 40;
408/*
409\def CSX_NAME_LENGTH
410* \brief The byte size of the name array
411*/
412const CSX_NAME_LENGTH: usize = 14;
413
414const CSX_INFO: [u8; QRC_CSX_INFO_SIZE] = [
415	0x43, 0x53, 0x58, 0x35, 0x31, 0x32, 0x20, 0x4B, 0x4D, 0x41, 0x43, 0x20, 0x61, 0x75, 0x74, 0x68,
416	0x65, 0x6E, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x76, 0x65, 0x72, 0x2E, 0x20,
417	0x31, 0x63, 0x20, 0x43, 0x45, 0x58, 0x2B, 0x2B, 0x20, 0x6C, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79
418];
419
420const CSX_NAME: [u8; CSX_NAME_LENGTH] = [
421	0x43, 0x53, 0x58, 0x35, 0x31, 0x32, 0x2D, 0x4B, 0x4D, 0x41, 0x43, 0x35, 0x31, 0x32
422];
423
424const CSX_KMACR12_NAME: [u8; CSX_NAME_LENGTH] = [
425	0x43, 0x53, 0x58, 0x35, 0x31, 0x32, 0x2D, 0x4B, 0x4D, 0x41, 0x43, 0x52, 0x31, 0x32
426];
427
428fn csx_increment(ctx: &mut QrcCsxState) {
429	ctx.state[12] = ctx.state[12].wrapping_add(1);
430
431	if ctx.state[12] == 0 {
432		ctx.state[13] =  ctx.state[13].wrapping_add(1);
433	}
434}
435
436fn csx_permute_p1024c(ctx: QrcCsxState, output: &mut [u8]) {
437	let mut x0 = ctx.state[0];
438	let mut x1 = ctx.state[1];
439	let mut x2 = ctx.state[2];
440	let mut x3 = ctx.state[3];
441	let mut x4 = ctx.state[4];
442	let mut x5 = ctx.state[5];
443	let mut x6 = ctx.state[6];
444	let mut x7 = ctx.state[7];
445	let mut x8 = ctx.state[8];
446	let mut x9 = ctx.state[9];
447	let mut x10 = ctx.state[10];
448	let mut x11 = ctx.state[11];
449	let mut x12 = ctx.state[12];
450	let mut x13 = ctx.state[13];
451	let mut x14 = ctx.state[14];
452	let mut x15 = ctx.state[15];
453	let mut ctr = CSX_ROUND_COUNT;
454
455	/* new rotational constants=
456	38,19,10,55
457	33,4,51,13
458	16,34,56,51
459	4,53,42,41
460	34,41,59,17
461	23,31,37,20
462	31,44,47,46
463	12,47,44,30 */
464
465	while ctr != 0 {
466		/* round n */
467		x0 = x0.wrapping_add(x4);
468		x12 = qrc_intutils_rotl64(x12 ^ x0, 38);
469		x8 = x8.wrapping_add(x12);
470		x4 = qrc_intutils_rotl64(x4 ^ x8, 19);
471		x0 = x0.wrapping_add(x4);
472		x12 = qrc_intutils_rotl64(x12 ^ x0, 10);
473		x8 = x8.wrapping_add(x12);
474		x4 = qrc_intutils_rotl64(x4 ^ x8, 55);
475		x1 = x1.wrapping_add(x5);
476		x13 = qrc_intutils_rotl64(x13 ^ x1, 33);
477		x9 = x9.wrapping_add(x13);
478		x5 = qrc_intutils_rotl64(x5 ^ x9, 4);
479		x1 = x1.wrapping_add(x5);
480		x13 = qrc_intutils_rotl64(x13 ^ x1, 51);
481		x9 = x9.wrapping_add(x13);
482		x5 = qrc_intutils_rotl64(x5 ^ x9, 13);
483		x2 = x2.wrapping_add(x6);
484		x14 = qrc_intutils_rotl64(x14 ^ x2, 16);
485		x10 = x10.wrapping_add(x14);
486		x6 = qrc_intutils_rotl64(x6 ^ x10, 34);
487		x2 = x2.wrapping_add(x6);
488		x14 = qrc_intutils_rotl64(x14 ^ x2, 56);
489		x10 = x10.wrapping_add(x14);
490		x6 = qrc_intutils_rotl64(x6 ^ x10, 51);
491		x3 = x3.wrapping_add(x7);
492		x15 = qrc_intutils_rotl64(x15 ^ x3, 4);
493		x11 = x11.wrapping_add(x15);
494		x7 = qrc_intutils_rotl64(x7 ^ x11, 53);
495		x3 = x3.wrapping_add(x7);
496		x15 = qrc_intutils_rotl64(x15 ^ x3, 42);
497		x11 = x11.wrapping_add(x15);
498		x7 = qrc_intutils_rotl64(x7 ^ x11, 41);
499		/* round n+1 */
500		x0 = x0.wrapping_add(x5);
501		x15 = qrc_intutils_rotl64(x15 ^ x0, 34);
502		x10 = x10.wrapping_add(x15);
503		x5 = qrc_intutils_rotl64(x5 ^ x10, 41);
504		x0 = x0.wrapping_add(x5);
505		x15 = qrc_intutils_rotl64(x15 ^ x0, 59);
506		x10 = x10.wrapping_add(x15);
507		x5 = qrc_intutils_rotl64(x5 ^ x10, 17);
508		x1 = x1.wrapping_add(x6);
509		x12 = qrc_intutils_rotl64(x12 ^ x1, 23);
510		x11 = x11.wrapping_add(x12);
511		x6 = qrc_intutils_rotl64(x6 ^ x11, 31);
512		x1 = x1.wrapping_add(x6);
513		x12 = qrc_intutils_rotl64(x12 ^ x1, 37);
514		x11 = x11.wrapping_add(x12);
515		x6 = qrc_intutils_rotl64(x6 ^ x11, 20);
516		x2 = x2.wrapping_add(x7);
517		x13 = qrc_intutils_rotl64(x13 ^ x2, 31);
518		x8 = x8.wrapping_add(x13);
519		x7 = qrc_intutils_rotl64(x7 ^ x8, 44);
520		x2 = x2.wrapping_add(x7);
521		x13 = qrc_intutils_rotl64(x13 ^ x2, 47);
522		x8 = x8.wrapping_add(x13);
523		x7 = qrc_intutils_rotl64(x7 ^ x8, 46);
524		x3 = x3.wrapping_add(x4);
525		x14 = qrc_intutils_rotl64(x14 ^ x3, 12);
526		x9 = x9.wrapping_add(x14);
527		x4 = qrc_intutils_rotl64(x4 ^ x9, 47);
528		x3 = x3.wrapping_add(x4);
529		x14 = qrc_intutils_rotl64(x14 ^ x3, 44);
530		x9 = x9.wrapping_add(x14);
531		x4 = qrc_intutils_rotl64(x4 ^ x9, 30);
532		ctr -= 2;
533	}
534
535	qrc_intutils_le64to8(output, x0.wrapping_add(ctx.state[0]));
536	qrc_intutils_le64to8(&mut output[8..], x1.wrapping_add(ctx.state[1]));
537	qrc_intutils_le64to8(&mut output[16..], x2.wrapping_add(ctx.state[2]));
538	qrc_intutils_le64to8(&mut output[24..], x3.wrapping_add(ctx.state[3]));
539	qrc_intutils_le64to8(&mut output[32..], x4.wrapping_add(ctx.state[4]));
540	qrc_intutils_le64to8(&mut output[40..], x5.wrapping_add(ctx.state[5]));
541	qrc_intutils_le64to8(&mut output[48..], x6.wrapping_add(ctx.state[6]));
542	qrc_intutils_le64to8(&mut output[56..], x7.wrapping_add(ctx.state[7]));
543	qrc_intutils_le64to8(&mut output[64..], x8.wrapping_add(ctx.state[8]));
544	qrc_intutils_le64to8(&mut output[72..], x9.wrapping_add(ctx.state[9]));
545	qrc_intutils_le64to8(&mut output[80..], x10.wrapping_add(ctx.state[10]));
546	qrc_intutils_le64to8(&mut output[88..], x11.wrapping_add(ctx.state[11]));
547	qrc_intutils_le64to8(&mut output[96..], x12.wrapping_add(ctx.state[12]));
548	qrc_intutils_le64to8(&mut output[104..], x13.wrapping_add(ctx.state[13]));
549	qrc_intutils_le64to8(&mut output[112..], x14.wrapping_add(ctx.state[14]));
550	qrc_intutils_le64to8(&mut output[120..], x15.wrapping_add(ctx.state[15]));
551}
552
553fn csx_mac_update(ctx: &mut QrcCsxState, input: &[u8], length: usize) {
554	let rate = QrcKeccakRate::QrcKeccakRate512 as usize;
555	if QRC_CSX_AUTH_KMACR12 {
556		qrc_keccak_update(&mut ctx.kstate, rate, input, length, QRC_KECCAK_PERMUTATION_MIN_ROUNDS);
557	} else {
558		qrc_kmac_update(&mut ctx.kstate, rate, input, length);
559	}
560}
561
562fn csx_transform(ctx: &mut QrcCsxState, output: &mut [u8], input: &[u8], mut length: usize) {
563	let mut oft = 0;
564
565	/* generate remaining blocks */
566	while length >= QRC_CSX_BLOCK_SIZE {
567		csx_permute_p1024c(ctx.clone(), &mut output[oft..]);
568		qrc_intutils_xor(&mut output[oft..], &input[oft..], QRC_CSX_BLOCK_SIZE);
569		csx_increment(ctx);
570		oft += QRC_CSX_BLOCK_SIZE;
571		length -= QRC_CSX_BLOCK_SIZE;
572	}
573
574	/* generate unaligned key-stream */
575	if length != 0 {
576		let tmp = &mut [0u8; QRC_CSX_BLOCK_SIZE];
577		csx_permute_p1024c(ctx.clone(), tmp);
578		csx_increment(ctx);
579		qrc_intutils_copy8(&mut output[oft..], tmp, length);
580		qrc_intutils_xor(&mut output[oft..], &input[oft..], length);
581	}
582}
583
584fn csx_load_key(ctx: &mut QrcCsxState, key: &[u8], nonce: &[u8], code: &[u8]) {
585	if QRC_SYSTEM_IS_LITTLE_ENDIAN {
586		qrc_intutils_copy64(&mut ctx.state, &qrc_intutils_transform_8to64(key), 8);
587		qrc_intutils_copy64(&mut ctx.state[8..], &qrc_intutils_transform_8to64(code), 4);
588		qrc_intutils_copy64(&mut ctx.state[12..], &qrc_intutils_transform_8to64(nonce), 2);
589		qrc_intutils_copy64(&mut ctx.state[14..], &qrc_intutils_transform_8to64(&code[32..]), 2);
590	} else {
591		ctx.state[0] = qrc_intutils_le8to64(key);
592		ctx.state[1] = qrc_intutils_le8to64(&key[8..]);
593		ctx.state[2] = qrc_intutils_le8to64(&key[16..]);
594		ctx.state[3] = qrc_intutils_le8to64(&key[24..]);
595		ctx.state[4] = qrc_intutils_le8to64(&key[32..]);
596		ctx.state[5] = qrc_intutils_le8to64(&key[40..]);
597		ctx.state[6] = qrc_intutils_le8to64(&key[48..]);
598		ctx.state[7] = qrc_intutils_le8to64(&key[56..]);
599		ctx.state[8] = qrc_intutils_le8to64(code);
600		ctx.state[9] = qrc_intutils_le8to64(&code[8..]);
601		ctx.state[10] = qrc_intutils_le8to64(&code[16..]);
602		ctx.state[11] = qrc_intutils_le8to64(&code[24..]);
603		ctx.state[12] = qrc_intutils_le8to64(nonce);
604		ctx.state[13] = qrc_intutils_le8to64(&nonce[8..]);
605		ctx.state[14] = qrc_intutils_le8to64(&code[32..]);
606		ctx.state[15] = qrc_intutils_le8to64(&code[40..]);
607	}
608}
609
610fn csx_finalize(ctx: &mut QrcCsxState, output: &mut [u8]) {
611	let ctr = &mut  [0u8; size_of::<u64>()];
612
613	qrc_intutils_le64to8(ctr, ctx.counter);
614	csx_mac_update(ctx, ctr, size_of::<u64>());
615
616	let rate = QrcKeccakRate::QrcKeccakRate512 as usize;
617	if QRC_CSX_AUTH_KMACR12 {
618		/* update the counter */
619		qrc_keccak_update(&mut ctx.kstate, rate, ctr, size_of::<u64>(), QRC_KECCAK_PERMUTATION_MIN_ROUNDS);
620		/* finalize the mac and append code to output */
621		qrc_keccak_finalize(&mut ctx.kstate, rate, output, QRC_CSX_MAC_SIZE, QRC_KECCAK_KMAC_DOMAIN_ID as usize, QRC_KECCAK_PERMUTATION_MIN_ROUNDS);
622	} else {
623		/* finalize the mac and append code to output */
624		qrc_kmac_finalize(&mut ctx.kstate, rate, output, QRC_CSX_MAC_SIZE);
625	}
626}