···22#![forbid(unsafe_code)]
3344use ml_kem::{MlKem512, MlKem768};
55-pub use wharrgarbl_neko::{Neko128, Neko256};
55+pub use wharrgarbl_neko::{Neko128, Neko192, Neko256};
6677pub mod handshake;
88pub mod transport;
···33333434pub type NekoClientHandshake128 = handshake::ClientHandshake<Neko128, MlKem512>;
3535pub type NekoServerHandshake128 = handshake::ServerHandshake<Neko128, MlKem512>;
3636+pub type NekoClientHandshake192 = handshake::ClientHandshake<Neko192, MlKem768>;
3737+pub type NekoServerHandshake192 = handshake::ServerHandshake<Neko192, MlKem768>;
3638pub type NekoClientHandshake256 = handshake::ClientHandshake<Neko256, MlKem768>;
3739pub type NekoServerHandshake256 = handshake::ServerHandshake<Neko256, MlKem768>;
+15-5
wharrgarbl-neko/SPEC.md
···11-# NEKO Specification
11+# NEKO Specification v0.2.1
2233NEKO is inspired by STROBE in that it uses Keccak in a duplex construction to perform encryption and message authentication. It has stripped down the amount of operation flags it uses and simplifies the internals, ridding the need to have "streaming" by instead opting for having different operating modes.
44···28282929## Construction
30303131-NEKO internally uses the Keccakf1600 state buffer directly, with its layout of `[u64; 25]`. It operates on blocks of `u64`, and mixes `[u8]` input into these blocks. If a block isn't "filled" completely, the remainder of that block is left as padding, with then the position incremented to point to the next block. It tracks a block counter and an op counter.
3131+NEKO internally uses the Keccakp1600 state buffer directly, with its layout of `[u64; 25]`. It operates on blocks of `u64`, and mixes `[u8]` input into these blocks. If a block isn't "filled" completely, the remainder of that block is left as padding, with then the position incremented to point to the next block. It tracks a block counter and an op counter.
32323333-NEKO has two security levels, 128-bit and 256-bit security. Each level determines the *rate*, which is how many blocks are available for input, so 128 gives 21 blocks for input with 4 blocks for entropy/*capacity*, while 256 gives 17 blocks and 8 blocks for entropy/*capacity*. The input blocks are separated for data input with the last block reserved for encoding ops. 256 mode will require permuting more often than 128 mode, as it won't have as much input capacity before its input buffer is exhausted.
3333+NEKO has three security levels, 128-bit, 192-bit and 256-bit security. Each level determines the *rate*, which is how many blocks are available for input, so 128 gives 21 blocks for input with 4 blocks for entropy/*capacity*, while 256 gives 17 blocks and 8 blocks for entropy/*capacity*. The input blocks are separated for data input with the last block reserved for encoding ops. 256 mode will require permuting more often than 128 mode, as it won't have as much input capacity before its input buffer is exhausted.
34343535-Ops are tracked with a stack, with a max of 4 ops being "stacked" before a permutation must occur. When a permutation starts, the ops encoding block is selected (position block + 1) and then it is XOR'd with the list of op bits on the \[0..4\] part of the block, going from first op to last, with the \[4..8\] part of the block XOR'd with the ops count, the block position on the first bytes, with then `0x80` & `0` on the last bytes. This creates the start of the padding structure. The padding terminator is then XOR'd on the last byte of the reserved block with a `0x80` value that is rotated by the position counter. If the input fills the buffer completely, the position+1 and reserved block are the same, so the combined XOR'd blocks will form a single block construction. The permutation is then executed with the f1600 function, after which the block & ops counters are reset along with the ops stack.
3535+Ops are tracked with a stack, with a max of 4 ops being "stacked" before a permutation must occur. When a permutation starts, the ops encoding block is selected (position block + 1) and then it is XOR'd with the list of op bits on the \[0..4\] part of the block, going from first op to last, with the \[4..8\] part of the block XOR'd with the ops count, the block position on the first bytes, with then `0x80` & `0` on the last bytes. This creates the start of the padding structure. The padding terminator is then XOR'd on the last byte of the reserved block with a `0x80` value that is rotated by the position counter. If the input fills the buffer completely, the position+1 and reserved block are the same, so the combined XOR'd blocks will form a single block construction. The permutation is then executed with the p1600 function with 12 rounds (KangarooTwelve), after which the block & ops counters are reset along with the ops stack.
36363737After the permutation concludes, the op which triggered the permutation is then encoded into the stack. There are two ways a permutation is invoked: via a user OP, or from a *continuation* for ingesting more data. In the case of a user op, the counter is set to zero, and the stack fully zeroed so the op can encode its flags onto the first slot when it begins. In the case of a *continuation*, the counter is set to `1`, and the first slot is encoded with the `CONT` flag bits with the rest of the slots zeroed. This ensures that in **every** case, there will always be 3 remaining free slots to form a 4 op chain.
3838···90909191Then, we must encode the preamble & NEKO version onto the state buffer. The preamble is defined as `[0x01, RATE, 0x07, 0x60]`. The `RATE` is calculated as `(200 - SecLevel / 4) / 8 - 1` and encoded as a `u8` value. So for `Neko128`, the `RATE` should resolve to `20`, and `Neko256` should resolve to `16`. These represent the max buffer position that input data can be absorbed into before needing to permute.
92929393-The NEKO version string is then concatenated to the preamble. For version v0.2 of this specification, the string is `NEKOv0.2.0`, and it should be concatenated as a byte string. Then the combined preamble+version bytes should be written to the buffer in an `OVERWRITE` action.
9393+The NEKO version string is then concatenated to the preamble. For version v0.2.1 of this specification, the string is `NEKOv0.2.1`, and it should be concatenated as a byte string. Then the combined preamble+version bytes should be written to the buffer in an `OVERWRITE` action.
94949595Additionally, a protocol byte string can be written to the state, following after the preamble+version with its own `OVERWRITE` action.
9696···114114All user operations follow a chain of 4 ops. Non-permuting ops can *stack*, so they add to the stack if their inputs don't cause the state to permute. Permuting ops always *reset* the stack, as they permute the state. When the stack resets due to an op, the stack is cleared and the op that triggered the reset is encoded into the first slot. `INIT` is always the very first op, so `KEY` + `NONCE` + `AD` can be added as ops without triggering a permutation. If another non-permuting op were to be chained at this point (like `CLR`), this would cause a panic. To resolve this, call a permuting op like `ENC` or `RATCHET`, and the stack is reset to be just the permuting op on the stack, with three more free slots.
115115116116A panic is a must, because any occasion that we are going over 4 chained ops is a misuse of the protocol and thus MUST fail quickly. Under normal usage, no sequence of ops should cause a chain of more than 4 to occur, and with large enough payloads, the state would be getting permuted enough to ensure this is not required.
117117+118118+## Changes
119119+120120+### v0.2
121121+122122+Initial publication and base specification.
123123+124124+### v0.2.1
125125+126126+Changed from f1600 to p1600 with 12 rounds for Keccak permutation function, and introduced Neko192 (192-bit) strength.
+145-74
wharrgarbl-neko/src/kats.rs
···11use hybrid_array::Array;
22use zerocopy::IntoBytes;
3344-use crate::{Neko128, Neko256, NekoState};
44+use crate::{Neko128, Neko192, Neko256, NekoState};
5566extern crate alloc;
77···15151616 assert_eq!(&first, b"\x01\x14\x07\x60NEKO");
1717 // Values that don't fill the block entirely leave padding
1818- assert_eq!(&second, b"v0.2.0\0\0");
1818+ assert_eq!(&second, b"v0.2.1\0\0");
1919+ assert_eq!(&third[..4], b"test");
2020+ // The rest of the state is zeroed
2121+ assert_eq!(&neko.state[3..], &[0; 22]);
2222+}
2323+2424+#[test]
2525+fn neko_192_init_state() {
2626+ let neko = NekoState::<Neko192>::new(b"test");
2727+2828+ let first = neko.state[0].to_le_bytes();
2929+ let second = neko.state[1].to_le_bytes();
3030+ let third = neko.state[2].to_le_bytes();
3131+3232+ assert_eq!(&first, b"\x01\x12\x07\x60NEKO");
3333+ // Values that don't fill the block entirely leave padding
3434+ assert_eq!(&second, b"v0.2.1\0\0");
1935 assert_eq!(&third[..4], b"test");
2036 // The rest of the state is zeroed
2137 assert_eq!(&neko.state[3..], &[0; 22]);
···31473248 assert_eq!(&first, b"\x01\x10\x07\x60NEKO");
3349 // Values that don't fill the block entirely leave padding
3434- assert_eq!(&second, b"v0.2.0\0\0");
5050+ assert_eq!(&second, b"v0.2.1\0\0");
3551 assert_eq!(&third[..4], b"test");
3652 // The rest of the state is zeroed
3753 assert_eq!(&neko.state[3..], &[0; 22]);
···114130 let expected_state = [
115131 0x0000000000000000,
116132 0x0000000000000000,
117117- 0x14fd15236a301dbc,
118118- 0x3d7a0f031c2332c7,
119119- 0x13d95db32a39a74c,
120120- 0xbfce1f9678690375,
121121- 0xc444bd0f9bb70133,
122122- 0x59600201db93b1de,
123123- 0x6bc376b646e898ea,
124124- 0x2e8a6c345fd3dca3,
125125- 0x9df94788a5fc9f4d,
126126- 0x2541272cca7a631c,
127127- 0xabc8b248a4e0eee3,
128128- 0x2a6befaf570b0120,
129129- 0x5e296ccc9b587798,
130130- 0x9b9d5caef6fc7d3c,
131131- 0x371099e20d7965db,
132132- 0x52b7fecf8d06aed7,
133133- 0xae285d1c6cada2c7,
134134- 0x12d17a37884449ea,
135135- 0x85846b16d640a55b,
136136- 0x0e7d16c0c2bf5a3e,
137137- 0xce32132c0b110014,
138138- 0x6620fda4f7642f84,
139139- 0xd1ea97aadb49a663,
133133+ 0xd3c9627ab3f60112,
134134+ 0xf9bc5a3ce86c0308,
135135+ 0x1b649c672f8291e7,
136136+ 0x348b76389636d1c5,
137137+ 0x7e7b242854a89160,
138138+ 0xc588e1bab9fff3f3,
139139+ 0x6869595e613dce2f,
140140+ 0x21aa5dd9c8ace1ed,
141141+ 0x976ac27c1542f2e3,
142142+ 0x271c24a505720c01,
143143+ 0x1cf4dbfd172716ec,
144144+ 0x5e338536048f69cd,
145145+ 0x198c6a958d8215a6,
146146+ 0x7e181ad612b4ec51,
147147+ 0xd3c7adc0e13f29fd,
148148+ 0x92adbe76b85f49a9,
149149+ 0xc05147b39c222c68,
150150+ 0xea1f4797623a1431,
151151+ 0x4d837d8b8ef79878,
152152+ 0x59b03ae6c5f3c16f,
153153+ 0x2b5da88884b6b1d9,
154154+ 0x04ecf618b84acb0a,
155155+ 0x58ae3cfc3b9574c2,
140156 ];
141157142158 assert_eq!(&neko.state, &expected_state);
···187203 assert_eq!(neko.state[0..2].as_bytes(), &message);
188204189205 let expected_state = [
190190- 0x33e5964098e69bb2,
191191- 0xe8aae76360864f1d,
192192- 0x6c10b3c273ae582c,
193193- 0xd9584d46c8025d46,
194194- 0x8eeace52fffacd4c,
195195- 0x2346ce9726155884,
196196- 0x0f3427af0a3c77f1,
197197- 0xe2706ecbbd9596b4,
198198- 0x840b7500b73e537c,
199199- 0x0015960758c2e30e,
200200- 0xf2cd5efad521e8e2,
201201- 0xca7199cf34822634,
202202- 0xe21f1c3744135b1a,
203203- 0xe91599f57a74f2c9,
204204- 0x1395bb13bd8eec8d,
205205- 0x8417dd11dfee0671,
206206- 0x95d9c20086520a10,
207207- 0x90cfb46fc5a4963d,
208208- 0x2aaf5cd4d234a06d,
209209- 0x5e4372caf96bd84a,
210210- 0x6a858536f819bb62,
211211- 0xf7f33ca323c59700,
212212- 0x38b5d9a41b4d08e1,
213213- 0x39c33af857ae9a82,
214214- 0x39ed20d798ecd321,
206206+ 0x376ff3f6beb5164d,
207207+ 0x6fce4cdd41481b09,
208208+ 0x62c9980b6ebd4375,
209209+ 0x35bebdd63cbae3f2,
210210+ 0xe82c6eed7a9482b1,
211211+ 0xe3948f44d30e1b63,
212212+ 0x64f3fbd74d1485a9,
213213+ 0xe3a5c716bab805e0,
214214+ 0xdb50cee154ae7634,
215215+ 0xc5f16a574d98aaa0,
216216+ 0xa701afa0ce5e483a,
217217+ 0xffdb8ceedc0e2137,
218218+ 0x1ced87c9b390ad78,
219219+ 0xc066d80fecc4e712,
220220+ 0x4d8d46e5f5972a6d,
221221+ 0xa948bf5cb8e4442f,
222222+ 0x1f08d0ca81e27400,
223223+ 0x2234e5f1e79cc913,
224224+ 0x4238eb75cde4fa79,
225225+ 0xe163927593ef0bfa,
226226+ 0x1665d19a25c4a420,
227227+ 0x3ec458a9b98e00cb,
228228+ 0xf19ee10721f39c29,
229229+ 0x96f715bbe8f84a73,
230230+ 0xaec74c90f467207c,
215231 ];
216232217233 assert_eq!(&neko.state, &expected_state);
···223239 let ratched_expected_state = [
224240 0x0000000000000000,
225241 0x0000000000000000,
226226- 0xe72010d5d3b254c0,
227227- 0x34007830a1c7585d,
228228- 0xbcd95dec900847a5,
229229- 0xfe1be2676e130078,
230230- 0xe6c29c4c48f292e6,
231231- 0x3d711aed9763e259,
232232- 0xa9b1329692f19ebd,
233233- 0xf1378893d98ad184,
234234- 0xe6f31bb95f5c361f,
235235- 0x549decbceeac0f78,
236236- 0x81b85f3f3d3a687d,
237237- 0x7dac55db9b73bf34,
238238- 0x6f07454e89ec5950,
239239- 0x9abd19c6e33eec92,
240240- 0x7358043c28de6955,
241241- 0x625421243d6b4bd5,
242242- 0xc986349494886128,
243243- 0xc00e8e52d7734dff,
244244- 0xbafa4f2b57d93144,
245245- 0x022f1aa724503cd5,
246246- 0x4b3633798cc9ae5e,
247247- 0x532b723068ab8c72,
248248- 0xa48327750108017c,
242242+ 0x01e23e724c12db57,
243243+ 0xe92a5c3fae8b582d,
244244+ 0x36454900c0add829,
245245+ 0x225646c8528ce4ff,
246246+ 0xe0cf79fd5e3e495a,
247247+ 0x77aed0a3813ef760,
248248+ 0xcbc0aa13baee85be,
249249+ 0x3fd706b857c5e671,
250250+ 0xfcbe3c0cefddfc6d,
251251+ 0xf8d1978f52c0d7e0,
252252+ 0x06c874e792fd7180,
253253+ 0xa6b978b38814727b,
254254+ 0x412d8e5666bde321,
255255+ 0x29e578d4ed104740,
256256+ 0x17b53fbaf1257b55,
257257+ 0x24cc8a75a308d641,
258258+ 0x3345c778c83c4882,
259259+ 0x1ea9692aff46faf8,
260260+ 0x68ce75a36d554dbc,
261261+ 0xe1faa52728b78408,
262262+ 0xdcabac6ec8e018e4,
263263+ 0xc631647d96ed3f06,
264264+ 0x8e29b59ab6a8956b,
249265 ];
250266251267 assert_eq!(&neko.state, &ratched_expected_state);
252268 // The rest of the non-zeroed state should not match the previous state
253269 assert_ne!(&expected_state[2..], &ratched_expected_state[2..]);
270270+271271+ let large_message = r#"Lorem ipsum dolor sit amet, consectetur adipiscing elit.
272272+Ut augue lectus, rhoncus a pulvinar ut, lacinia non est.
273273+Pellentesque imperdiet ornare blandit. Mauris dictum mollis blandit.
274274+Ut a arcu tincidunt, ultricies arcu sed, auctor erat.
275275+Nunc dignissim hendrerit porttitor. Nunc fermentum mi sed viverra euismod.
276276+Cras vel diam tortor. Morbi scelerisque augue eget elit ultrices suscipit.
277277+Nunc varius arcu sit amet neque faucibus, id iaculis ex porta.
278278+Nullam non neque nec neque faucibus sollicitudin.
279279+Phasellus eget ultrices purus. Aenean tincidunt nisl mi, vel tincidunt ante pretium eu.
280280+In scelerisque, ex et porta varius, orci risus malesuada nibh, convallis euismod nibh ipsum non turpis."#;
281281+282282+ let mut large_encrypted_message = large_message.as_bytes().to_vec();
283283+284284+ neko.encrypt(&mut large_encrypted_message);
285285+286286+ let expected_state = [
287287+ 0x13412b65f7e20af3,
288288+ 0x6d3424b086abd336,
289289+ 0xcb9fc2f09e70d9a0,
290290+ 0x88c998c81951d0c2,
291291+ 0x059e869c2f7a61ce,
292292+ 0xc3659823ee130ee2,
293293+ 0xaa59c8ff1d95842f,
294294+ 0x940740fc352b4fae,
295295+ 0xdbd1854b5f012452,
296296+ 0xe14d604523a7c4b1,
297297+ 0x4a1a2305128b2afa,
298298+ 0xec94592e37b13bd9,
299299+ 0x2acce9aec73ec364,
300300+ 0x73324f4034356ff2,
301301+ 0x563ae980c6a2e903,
302302+ 0xdf6491d84e9a29dc,
303303+ 0xd86c5e19f7f243ca,
304304+ 0x18e7a1523d271264,
305305+ 0x41f808f2bb8d7c2c,
306306+ 0xfeea3498ca40ba48,
307307+ 0xb304f29eb1df2f33,
308308+ 0xce55d8a83bba04a4,
309309+ 0x452613733fbc50df,
310310+ 0xe137b9f22560f2d0,
311311+ 0xb12bb41da4aa156e,
312312+ ];
313313+314314+ assert_eq!(&neko.state, &expected_state);
315315+ assert_ne!(large_message.as_bytes(), &large_encrypted_message);
316316+317317+ let tag = neko.create_mac();
318318+319319+ let expected_tag: [u8; 16] = [
320320+ 0x9b, 0x89, 0x06, 0xea, 0x70, 0xec, 0x90, 0xc1, 0x9e, 0x47, 0x62, 0xd7, 0x52, 0xcb, 0x0d,
321321+ 0xaa,
322322+ ];
323323+324324+ assert_eq!(&tag, &expected_tag);
254325}
255326256327#[test]
+73-19
wharrgarbl-neko/src/lib.rs
···11#![no_std]
22-#![forbid(unsafe_code)]
3243mod flags;
54#[cfg(test)]
···1312use aead::{
1413 KeySizeUser,
1514 common::IvSizeUser,
1616- consts::{U4, U10, U16, U25, U32, U128, U256},
1515+ consts::{U4, U10, U16, U25, U32, U128, U192, U256},
1716};
1817use ctutils::CtEq;
1918use hybrid_array::Array;
···2423pub use crate::traits::NekoSec;
25242625pub type Neko128 = U128;
2626+pub type Neko192 = U192;
2727pub type Neko256 = U256;
2828pub type NekoNonce<S> = Array<u8, <NekoState<S> as IvSizeUser>::IvSize>;
2929pub type NekoKey<S> = Array<u8, <NekoState<S> as KeySizeUser>::KeySize>;
3030pub type NekoTag = Array<u8, U16>;
31313232-pub static NEKO_VERSION: &str = "NEKOv0.2.0";
3232+pub static NEKO_VERSION: &str = "NEKOv0.2.1";
3333const U64_CHUNK: usize = core::mem::size_of::<u64>();
3434const MAX_OPS: usize = core::mem::size_of::<u32>();
3535···7676 ///
7777 /// let neko = NekoState::<Neko128>::new(b"whimsical");
7878 ///
7979- /// assert_eq!(format!("{neko}"), "NEKOv0.2.0/1600-128");
7979+ /// assert_eq!(format!("{neko}"), "NEKOv0.2.1/1600-128");
8080 /// ```
8181 pub fn new(protocol: &[u8]) -> Self {
8282+ const {
8383+ assert!(Sec::BLOCK_RATE < keccak::PLEN);
8484+ assert!(Sec::BLOCK_RATE < u8::MAX as usize);
8585+ };
8686+8287 // OPS stack MUST be initialised with the INIT flag as the first op.
8388 let ops_stack = [ops::INIT.bits(), 0, 0, 0];
8489···9510096101 // Preamble is defined with 0x01 as the first byte, the RATE as the second byte
97102 // and then 0x07 & 0x60 as the third & fourth byte.
9898- let preamble: Array<u8, U4> = Array::from([0x01, Sec::rate() as u8, 0x07, 0x60]);
103103+ let preamble: Array<u8, U4> = Array::from([0x01, Sec::BLOCK_RATE as u8, 0x07, 0x60]);
99104100105 // This is safe because the specification version string is always 10 bytes long.
101106 let version: Array<u8, U10> = Array::try_from(NEKO_VERSION.as_bytes()).unwrap();
···114119 neko
115120 }
116121122122+ #[inline(always)]
123123+ #[must_use]
124124+ fn block(&self) -> usize {
125125+ let block = usize::from(self.raw_position()).div_ceil(U64_CHUNK);
126126+ debug_assert!(block <= Sec::BLOCK_RATE);
127127+ if block <= Sec::BLOCK_RATE {
128128+ block
129129+ } else {
130130+ // SAFETY: the type enforces that `block` is always smaller than `RATE`
131131+ unsafe { core::hint::unreachable_unchecked() };
132132+ }
133133+ }
134134+135135+ #[inline(always)]
136136+ #[must_use]
137137+ fn raw_position(&self) -> u8 {
138138+ debug_assert!(self.position < u8::MAX as usize && self.position <= Sec::POS_RATE);
139139+ if self.position < u8::MAX as usize && self.position <= Sec::POS_RATE {
140140+ self.position as u8
141141+ } else {
142142+ // SAFETY: the type enforces that `position` is always smaller than
143143+ // `RATE * U64_CHUNK` & `u8::MAX`
144144+ unsafe { core::hint::unreachable_unchecked() };
145145+ }
146146+ }
147147+148148+ #[inline(always)]
149149+ fn advance_position(&mut self, advance: usize) {
150150+ let updated = self.position + advance;
151151+ assert!(updated <= Sec::POS_RATE);
152152+ self.position = updated;
153153+ }
154154+155155+ #[inline(always)]
156156+ #[must_use]
157157+ fn should_permute(&self) -> bool {
158158+ self.raw_position() == Sec::POS_RATE as u8
159159+ }
160160+117161 #[inline]
118162 #[track_caller]
119163 fn begin_op(&mut self, red_flags: OpFlags) {
···132176 self.ops_stack[op_index] = red_flags.bits();
133177 }
134178135135- fn permutation_f(&mut self, continuation: OpFlags) {
179179+ fn permutation_p12(&mut self, continuation: OpFlags) {
180180+ const {
181181+ assert!(Sec::BLOCK_RATE < keccak::PLEN);
182182+ assert!(Sec::BLOCK_RATE < u8::MAX as usize);
183183+ };
184184+136185 // Last byte is zeroed in case the terminator overlaps
137186 let permuter: Array<u8, U4> =
138138- Array([self.ops_count as u8, self.position as u8, 0x80u8.to_le(), 0]);
187187+ Array([self.ops_count as u8, self.raw_position(), 0x80u8.to_le(), 0]);
139188140189 let permuter_block = u64::from_ne_bytes(self.ops_stack.concat(permuter).0);
141190···144193 self.state[self.position.div_ceil(U64_CHUNK) + 1] ^= permuter_block;
145194 // Flip a bit in the last byte of the first entropy block with a 1 to act as the padding terminator.
146195 // The bit is selected via rotating right the value 0x80 (0b1000_0000) by the position counter.
147147- self.state[Sec::rate()].as_mut_bytes()[7] ^=
196196+ self.state[Sec::BLOCK_RATE].as_mut_bytes()[7] ^=
148197 0x80u8.to_le().rotate_right(self.position as u32);
149198150150- // The state has been fully prepared, and now can be permuted by the F1600 function.
151151- keccak::Keccak::new().with_f1600(|permute| permute(&mut self.state.0));
199199+ // The state has been fully prepared, and now can be permuted by the p1600(12) function.
200200+ keccak::Keccak::new().with_p1600::<12>(|permute| permute(&mut self.state.0));
152201153202 // Reset the state, zeroing all counters/stack unless a CONTINUATION, in which case
154203 // ops count is set to 1 and the first op slot is encoded with 0x01.
···159208160209 #[inline]
161210 fn zero_state(&mut self) {
211211+ const {
212212+ assert!(Sec::RATCHET < keccak::PLEN);
213213+ assert!(Sec::RATCHET < u8::MAX as usize);
214214+ };
162215 // Select the amount of bytes to zero, according to Security level
163216 // 128 bits = 16 bytes to zero out to achieve forward secrecy
217217+ // 192 bits = 24 bytes to zero out to achieve forward secrecy
164218 // 256 bits = 32 bytes to zero out to achieve forward secrecy
165165- let ratchet_bytes = Sec::ratchet_bytes();
219219+ let ratchet_bytes = Sec::RATCHET;
166220167221 self.state[0..ratchet_bytes].iter_mut().for_each(|block| {
168222 *block = 0;
···203257 ///
204258 /// **This is a PERMUTING operation**
205259 pub fn prf(&mut self, data: &mut [u8]) {
206206- self.permutation_f(ops::RST);
260260+ self.permutation_p12(ops::RST);
207261208262 self.begin_op(ops::PRF);
209263···215269 ///
216270 /// **This is a PERMUTING operation**
217271 pub fn create_mac(&mut self) -> NekoTag {
218218- self.permutation_f(ops::RST);
272272+ self.permutation_p12(ops::RST);
219273220274 self.begin_op(ops::MAC);
221275···232286 ///
233287 /// **This is a PERMUTING operation**
234288 pub fn verify_mac(&mut self, data: &NekoTag) -> aead::Result<()> {
235235- self.permutation_f(ops::RST);
289289+ self.permutation_p12(ops::RST);
236290237291 self.begin_op(ops::MAC);
238292···256310 ///
257311 /// **This is a PERMUTING operation**
258312 pub fn encrypt(&mut self, data: &mut [u8]) {
259259- self.permutation_f(ops::RST);
313313+ self.permutation_p12(ops::RST);
260314261315 self.begin_op(ops::ENC);
262316···267321 ///
268322 /// **This is a PERMUTING operation**
269323 pub fn decrypt(&mut self, data: &mut [u8]) {
270270- self.permutation_f(ops::RST);
324324+ self.permutation_p12(ops::RST);
271325272326 self.begin_op(ops::ENC);
273327···290344 ///
291345 /// **This is a PERMUTING operation**
292346 pub fn ratchet(&mut self) {
293293- self.permutation_f(ops::RST);
347347+ self.permutation_p12(ops::RST);
294348295349 self.begin_op(ops::RATCHET);
296350···327381 let display = std::format!("{s}");
328382 let debug = std::format!("{s:?}");
329383330330- assert_eq!(&display, "NEKOv0.2.0/1600-128");
384384+ assert_eq!(&display, "NEKOv0.2.1/1600-128");
331385 assert_eq!(
332386 &debug,
333333- "NekoState { sec: 128, version: \"NEKOv0.2.0\", .. }"
387387+ "NekoState { sec: 128, version: \"NEKOv0.2.1\", .. }"
334388 );
335389 }
336390}
+33-23
wharrgarbl-neko/src/operators.rs
···11use zerocopy::IntoBytes;
2233-use crate::{NekoSec, NekoState, U64_CHUNK, ops};
33+use crate::{NekoSec, NekoState, ops};
4455pub(crate) struct NekoOperateMut<'s, S: NekoSec> {
66 neko: &'s mut NekoState<S>,
···15151616 #[inline(always)]
1717 fn operate_mut(&'s mut self, operation: fn((&mut u8, &mut u8))) {
1818- loop {
1818+ const {
1919+ assert!(S::BLOCK_RATE < keccak::PLEN);
2020+ assert!(S::BLOCK_RATE < u8::MAX as usize);
2121+ };
2222+2323+ while !self.data.is_empty() {
2424+ if self.neko.should_permute() {
2525+ self.neko.permutation_p12(ops::CONT);
2626+ }
2727+2828+ let block = self.neko.block();
2929+1930 // Trans the neko
2020- let transed_bytes =
2121- self.neko.state[self.neko.position.div_ceil(U64_CHUNK)..S::rate()].as_mut_bytes();
3131+ let transed_bytes = self.neko.state[block..S::BLOCK_RATE].as_mut_bytes();
22322323- let take = transed_bytes
3333+ let advanced = transed_bytes
2434 .iter_mut()
2535 .zip(self.data.iter_mut())
2636 .map(operation)
2737 .count();
28382929- self.data = &mut self.data[take..];
3939+ self.data = &mut self.data[advanced..];
30403131- if !self.data.is_empty() {
3232- self.neko.permutation_f(ops::CONT);
3333- } else {
3434- self.neko.position += take;
3535- break;
3636- }
4141+ self.neko.advance_position(advanced);
3742 }
3843 }
3944···80858186 #[inline(always)]
8287 fn operate(&'s mut self, operation: fn((&mut u8, &u8))) {
8383- loop {
8888+ const {
8989+ assert!(S::BLOCK_RATE < keccak::PLEN);
9090+ assert!(S::BLOCK_RATE < u8::MAX as usize);
9191+ };
9292+9393+ while !self.data.is_empty() {
9494+ if self.neko.should_permute() {
9595+ self.neko.permutation_p12(ops::CONT);
9696+ }
9797+9898+ let block = self.neko.block();
9999+84100 // Trans the neko
8585- let transed_bytes =
8686- self.neko.state[self.neko.position.div_ceil(U64_CHUNK)..S::rate()].as_mut_bytes();
101101+ let transed_bytes = self.neko.state[block..S::BLOCK_RATE].as_mut_bytes();
871028888- let take = transed_bytes
103103+ let advanced = transed_bytes
89104 .iter_mut()
90105 .zip(self.data)
91106 .map(operation)
92107 .count();
931089494- self.data = &self.data[take..];
109109+ self.data = &self.data[advanced..];
951109696- if !self.data.is_empty() {
9797- self.neko.permutation_f(ops::CONT);
9898- } else {
9999- self.neko.position += take;
100100- break;
101101- }
111111+ self.neko.advance_position(advanced);
102112 }
103113 }
104114