···2233A whimsical encryption crate that is inspired by the STROBE spec, though it takes diverging choices with regards to its internals and API surface for better performance while retaining security guarantees.
4455-## SPEC
55+## Specification
6677-WIP
77+Read about the NEKO encryption [specification here](./SPEC.md).
8899☢️ **WARNING: DO NOT USE IN PRODUCTION. THIS CRATE IS NOT AUDITED AND IS WIP, MAYBE EVEN NOT AS SECURE AS THOUGHT. AAAAAAAAAA** ☢️
1010
+116
wharrgarbl-neko/SPEC.md
···11+# NEKO Specification
22+33+NEKO 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+55+In lieu of streaming, NEKO uses op-stacking to reduce the amount of permutations needed. There are two kinds of operations, NON-PERMUTING and PERMUTING. A permutation is when the internals invoke the Keccak permutation function on the NEKO state, resulting in a new, pseudorandomised state. A permutation resets the buffer, and thus allows more data to be ingested into the cipher.
66+77+The pattern is a max of four chained ops, with the first being a permuting one or an INIT/CONT op. Any sequence of more than 3 non-permuting ops that don't incur a permutation during data ingestion, and NEKO will throw a panic. The available operations are as follows:
88+99+#### Non-Permuting
1010+1111+- Key (256-bit value)
1212+- Nonce (128-bit value)
1313+- Ad (Associated Data)
1414+- Cleartext
1515+1616+#### Permuting
1717+1818+- Encrypt
1919+- Decrypt
2020+- Create MAC
2121+- Verify MAC
2222+- Ratchet
2323+- Prf (Pseudo-Random Function)
2424+2525+Non-permuting operations do not perform a permutation UNLESS more data is ingested than can be fitted into the internal state buffer. Whereas Permuting operations ALWAYS permute before data is ingested, permuting further if need to be ingest all the input data.
2626+2727+A valid chain of operations can be \[`INIT`, `KEY`, `NONCE`, `AD`\]+\[`ENCRYPT`\]+\[`MAC`\], with the first three after `INIT` being non-permuting, and then followed by permuting operations that "reset" the chain. This example chain will permute AT LEAST two times in order to perform its operations. The operations performed are mixed into the internal state, as well as the state of the buffer when the permutation is performed. It encodes the bitflags of each of the operations, the final count of operations performed, the final position in the state, with some constants.
2828+2929+## Construction
3030+3131+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.
3232+3333+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.
3434+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 f1600 function, after which the block & ops counters are reset along with the ops stack.
3636+3737+After 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+3939+As such, how an operation works is as follows: User invokes op -> (Permute if required) -> Encode Op to free slot on stack -> Ingest Data -> (Permute if continuation) -> Finish.
4040+4141+Internally, each operation *operates* on the data with one of 7 different actions:
4242+4343+- **ABSORB**: This XOR's the input directly into the state: `State ^ Input`. This does not mutate the input.
4444+- **ABSORB & SET**: This XOR's the input into the state, then sets the input as the resulting state: `State ^ Input, Input = State`. This mutates the input.
4545+- **COPY STATE**: This copies out the state directly into the input: `Input = State`. This mutates the input.
4646+- **EXCHANGE**: This XOR's the input to the state, and then XOR's the resulting state to the input: `State ^ Input, Input ^ State`. This mutates the input.
4747+- **OVERWRITE**: This sets the state with the input bytes directly: `State = Input`. This does not mutate the input.
4848+- **SQUEEZE**: This sets the input with the state, then zeroes out the state. `Input = State, State = 0`. This mutates the input.
4949+- **ZERO STATE**: This op does not take input, but instead zeroes out a specific amount of state blocks according to security level: 128 bit zeros 2 blocks, 256 zeros 4 blocks. `State = 0`.
5050+5151+## Flag Definitions
5252+5353+NEKO's operation flags are defined with a 4 bit bitflag in a `u8`, with the remaining high bits reserved. The flags are defined as such:
5454+5555+| FLAG | Bits | Value |
5656+| --------- | ------ | ----- |
5757+| TRANSPORT | 0b0001 | 1 |
5858+| APP | 0b0010 | 2 |
5959+| CIPHER | 0b0100 | 4 |
6060+| META | 0b1000 | 8 |
6161+6262+There's a special case where a flag with all bits zero represents a `RESET` op. This is an internal op, with the only other internal op being `CONT` which is just `TRANSPORT` flag toggled on its own. These form the two ops that are not meant to be made available to users:
6363+6464+| OP | FLAGS | VALUE |
6565+| ----- | --------- | ------------- |
6666+| RESET | EMPTY | 0b0000, 0 |
6767+| CONT | TRANSPORT | 0b0001, 1 |
6868+6969+The internal flags are used to define when permutations occur either from a new OP or from a continuation of an OP to ingest more data.
7070+7171+The rest of the valid ops are available to be invoked by user operations. They are defined as follows:
7272+7373+| OP | FLAGS | VALUE |
7474+| ------- | --------------------------- | ---------- |
7575+| CLR | APP \| TRANSPORT | 0b0011, 3 |
7676+| MAC | CIPHER \| TRANSPORT | 0b0101, 5 |
7777+| ENC | CIPHER \| APP \| TRANSPORT | 0b0111, 7 |
7878+| NONCE | META \| TRANSPORT | 0b1001, 9 |
7979+| INIT | META \| APP | 0b1010, 10 |
8080+| AD | META \| APP \| TRANSPORT | 0b1011, 11 |
8181+| KEY | META \| CIPHER | 0b1100, 12 |
8282+| RATCHET | META \| CIPHER \| TRANSPORT | 0b1101, 13 |
8383+| PRF | META \| CIPHER \| APP | 0b1110, 14 |
8484+8585+ALL valid user ops are defined with a combination of two or three flags. No user operation is defined with a single op flag or all four toggled.
8686+8787+## Initialisation
8888+8989+Creating a new Neko State instance is qualified as an INIT operation. This means that it must be encoded as an operation on the op stack. As such the initial op stack is [INIT, 0, 0, 0] and the op counter must be set to one. The buffer position counter is then set to 0 and the Keccak state buffer is initialised and zeroed: `[0u64; 25]`;
9090+9191+Then, 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.
9292+9393+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.
9494+9595+Additionally, a protocol byte string can be written to the state, following after the preamble+version with its own `OVERWRITE` action.
9696+9797+Once done, the state is finalised and ready to be used.
9898+9999+## User operations
100100+101101+With an initialised Neko state, the following operations are available to the user (with the internal action & OP flag the state takes being described):
102102+103103+- `key`: Applies `KEY` opflag.Takes a 32 byte key reference `&Array<u8, U32>`, and `OVERWRITE`s it to the state. This is a non-permuting operation (it does not modify the input).
104104+- `nonce`: Applies `NONCE` opflag.Takes a 16 byte reference `&Array<u8, U16>`, and `ABSORB`s it to the state. This is a non-permuting operation (it does not modify the input).
105105+- `ad`: Applies `AD` opflag.Takes a byte slice `&[u8]` and `ABSORB`s it to the state. This is a non-permuting operation (it does not modify the input).
106106+- `ratchet`: Applies `RATCHET` opflag. Permutes the internal state and then zeroes out a set amount of bytes in the buffer. Either 16 bytes for `Neko128` or 32 bytes for `Neko256`. This is a permuting operation, but it doesn't mutate any input because it doesn't take any input.
107107+- `prf`: Applies `PRF` opflag. Takes a mutable byte slice `&mut [u8]` and then `SQUEEZE`s the internal state to the slice. Used for expanding keys/nonces/etc. This is a permuting operation, as it modifies the input slice.
108108+- `cleartext`: Applies `CLR` opflag. Takes a byte slice reference of cleartext `&[u8]`, and `ABSORB`s it to the state. While similar to `AD`, it is encoded as a separate operation for semantics, as `AD` is for data that doesn't get transmitted over-the-wire, but `CLR` is for data that is not encrypted but still transmitted. This is a non-permuting operation (it does not modify the input).
109109+- `encrypt`: Applies `ENC` opflag. Takes a mutable byte slice of cleartext `&mut [u8]`, and `ABSORB & SET`s it with the internal state to encrypt the text in place to form the ciphertext. This is a permuting operation, as it modifies the input slice.
110110+- `decrypt`: Applies `ENC` opflag. Takes a mutable byte slice of ciphertext `&mut [u8]`, and `EXCHANGE`s it with the internal state to decrypt the text in place to form the cleartext. This is a permuting operation, as it modifies the input slice.
111111+- `create_mac`: Applies `MAC` opflag. This doesn't take any input, but creates a tag/MAC via `COPY STATE` action, creating a `Array<u8, U16>` that represents a MAC of the processed state. This is a permuting operation, as it requires to process all previous operations into the internal state.
112112+- `verify_mac`: Applies `MAC` opflag. This takes a tag reference `&Array<u8, U16>` and verifies it by `EXCHANGE` with the internal state. If the tag/MAC resolves to all zeroes, then it is a valid tag, and the actions returns a success result. If not, it returns a failure result. This is a permuting operation, as it requires to process all previous operations into the internal state.
113113+114114+All 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.
115115+116116+A 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.
+106-71
wharrgarbl-neko/src/kats.rs
···1313 let second = neko.state[1].to_le_bytes();
1414 let third = neko.state[2].to_le_bytes();
15151616- assert_eq!(&first, b"\x01\x16\x07\x60NEKO");
1616+ 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.1.0\0\0");
1818+ assert_eq!(&second, b"v0.2.0\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]);
···2929 let second = neko.state[1].to_le_bytes();
3030 let third = neko.state[2].to_le_bytes();
31313232- assert_eq!(&first, b"\x01\x14\x07\x60NEKO");
3232+ assert_eq!(&first, b"\x01\x10\x07\x60NEKO");
3333 // Values that don't fill the block entirely leave padding
3434- assert_eq!(&second, b"v0.1.0\0\0");
3434+ assert_eq!(&second, b"v0.2.0\0\0");
3535 assert_eq!(&third[..4], b"test");
3636 // The rest of the state is zeroed
3737 assert_eq!(&neko.state[3..], &[0; 22]);
···8888 neko.nonce(&nonce);
8989 neko.ad(ad);
90909191- let op = neko.state[3].as_bytes();
9292- let state = neko.state[4..8].as_bytes();
9191+ let state = neko.state[3..7].as_bytes();
93929494- // OPs are XOR'd onto the state, but when the state is zeroed, it
9595- // is the same as overwriting, so the ops are visible.
9696- assert_eq!(op, b"\x00\x08nyaan~");
9793 assert_eq!(state, key.as_slice());
98949999- let op = neko.state[8].as_bytes();
100100- let state = neko.state[9..11].as_bytes();
9595+ let state = neko.state[7..9].as_bytes();
10196102102- assert_eq!(op, b"\x04\x09nyaan~");
10397 assert_eq!(state, nonce.as_slice());
10498105105- let op = neko.state[11].as_bytes();
106106- let state = neko.state[12].as_bytes();
9999+ let state = neko.state[9].as_bytes();
107100108108- assert_eq!(op, b"\x09\x0Anyaan~");
109101 assert_eq!(state, b"addz\0\0\0\0");
110102111111- assert_eq!(&neko.state[13..], &[0; 12]);
103103+ let ops = &neko.ops_stack;
104104+ // OPs are XOR'd onto the state, but when the state is zeroed, it
105105+ // is the same as overwriting, so the ops are visible.
106106+ assert_eq!(ops, &[0x0A, 0x0C, 0x09, 0x0B]);
107107+ // The rest of the state is zeroed
108108+ assert_eq!(&neko.state[13..22], &[0; 9]);
109109+ assert_eq!(&neko.state[23..], &[0; 2]);
110110+111111+ // We must permute at this point
112112+ neko.ratchet();
113113+114114+ let expected_state = [
115115+ 0x0000000000000000,
116116+ 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,
140140+ ];
112141113113- // Cleartext will permute the state
142142+ assert_eq!(&neko.state, &expected_state);
143143+144144+ let orig_state = neko.state[2];
145145+146146+ // Cleartext will not permute the state unless the text is larger
147147+ // than the available permuted buffer.
114148 neko.cleartext(cleartext);
115149116116- for block in neko.state[0..22].iter().map(IntoBytes::as_bytes) {
150150+ assert_eq!(&neko.ops_stack, &[0x0D, 0x03, 0, 0]);
151151+152152+ for block in neko.state[2..20].iter().map(IntoBytes::as_bytes) {
117153 // The permuted state means the message is mixed, so it can no longer be "read"
118154 // directly.
119119- assert_ne!(block, b"\x0c\x03nyaan~");
120155 assert_ne!(block, cleartext);
121156 }
122157 let orig_text = u64::from_le_bytes(*cleartext);
123123- let orig_state = neko.state[0] ^ orig_text;
124124- let recreated = neko.state[0] ^ orig_state;
158158+ // RECREATE
159159+ let recreated = neko.state[2] ^ orig_state;
125160126126- // After XOR'ing from the state, we can reconstruct the cleartext, so we can confirm
127127- // it is there.
161161+ // After XOR'ing the original state from the modified state, we can reconstruct the cleartext
162162+ // so we can confirm it is there.
128163 assert_eq!(orig_text, recreated);
129164}
130165···152187 assert_eq!(neko.state[0..2].as_bytes(), &message);
153188154189 let expected_state = [
155155- 0x14bf7a55ecbfef9f,
156156- 0xbf881650acfa3419,
157157- 0x19e0cf77f80c9b9c,
158158- 0x926cb071dcc542bf,
159159- 0xb0c2ff19b2a1088a,
160160- 0x28e1786b57d258b5,
161161- 0xa10f75b4a49c5830,
162162- 0xe0d2a4c88992ddab,
163163- 0xf26af8755e5d7ce8,
164164- 0xa130fa26d8697da0,
165165- 0x2d6266942d30bb73,
166166- 0x0c47cacae106048e,
167167- 0x1f78293be88890d7,
168168- 0xcf24691274e96761,
169169- 0x0501d35fa5a7db34,
170170- 0xd242e3a40bf885f8,
171171- 0x717c3e620ea21bdf,
172172- 0x12af362391a15895,
173173- 0x8df18b22be842ea5,
174174- 0xa32fd58b7771026c,
175175- 0x4a61af391e658ec5,
176176- 0x189f225280d6d986,
177177- 0x2718bc59d75e8d60,
178178- 0x0be0f806c2086f5a,
179179- 0xd5f2731cc9c0fed0,
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,
180215 ];
181216182217 assert_eq!(&neko.state, &expected_state);
···188223 let ratched_expected_state = [
189224 0x0000000000000000,
190225 0x0000000000000000,
191191- 0xaeaae19feedba131,
192192- 0x142c658c8c5d4d41,
193193- 0x5b15a77c99a73daa,
194194- 0xe8060d814eb54fe2,
195195- 0xaf03a57bc4107f06,
196196- 0x20a98fb94a745e23,
197197- 0x311dd2be1529baad,
198198- 0x4d50f23f34057523,
199199- 0xea89ea72449476d5,
200200- 0x82457373a57d4062,
201201- 0x0aad58af56986ea2,
202202- 0x8548e7cb9b7907c3,
203203- 0xca95c689bed5fbbb,
204204- 0x426b6a66b930e5ad,
205205- 0x514c8865b88f89ea,
206206- 0x361e3d8a73cd12f1,
207207- 0xbcd3e3c6949cec98,
208208- 0x6b210034af33aeac,
209209- 0x2d06ca5415ba4459,
210210- 0x57dfabd826619f1e,
211211- 0x69f4dd80c2791cc4,
212212- 0xd9086f9fde008f52,
213213- 0xd71233e8001d2c80,
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,
214249 ];
215250216251 assert_eq!(&neko.state, &ratched_expected_state);
+129-105
wharrgarbl-neko/src/lib.rs
···12121313use aead::{
1414 KeySizeUser,
1515+ common::IvSizeUser,
1516 consts::{U4, U10, U16, U25, U32, U128, U256},
1617};
1718use ctutils::CtEq;
1819use hybrid_array::Array;
1920use wharrgarbl_utils::OpFlags;
2121+use zerocopy::IntoBytes;
20222123use crate::operators::{NekoOperate, NekoOperateMut};
2224pub use crate::traits::NekoSec;
23252426pub type Neko128 = U128;
2527pub type Neko256 = U256;
2626-pub type NekoNonce = Array<u8, U16>;
2828+pub type NekoNonce<S> = Array<u8, <NekoState<S> as IvSizeUser>::IvSize>;
2729pub type NekoKey<S> = Array<u8, <NekoState<S> as KeySizeUser>::KeySize>;
2830pub type NekoTag = Array<u8, U16>;
29313030-pub static NEKO_VERSION: &str = "NEKOv0.1.0";
3232+pub static NEKO_VERSION: &str = "NEKOv0.2.0";
3333+const U64_CHUNK: usize = core::mem::size_of::<u64>();
3434+const MAX_OPS: usize = core::mem::size_of::<u32>();
31353236impl<S: NekoSec> KeySizeUser for NekoState<S> {
3337 type KeySize = U32;
3438}
35394040+impl<S: NekoSec> IvSizeUser for NekoState<S> {
4141+ type IvSize = U16;
4242+}
4343+3644#[derive(Clone)]
3745pub struct NekoState<S: NekoSec> {
3846 pub(crate) state: Array<u64, U25>,
3947 position: usize,
4040- start: usize,
4848+ ops_count: usize,
4949+ pub(crate) ops_stack: Array<u8, U4>,
4150 sec: PhantomData<S>,
4251}
4352···4554 fn zeroize(&mut self) {
4655 self.state.zeroize();
4756 self.position.zeroize();
4848- self.position.zeroize();
5757+ self.ops_count.zeroize();
5858+ self.ops_stack.zeroize();
4959 }
5060}
5161···5868}
59696070impl<Sec: NekoSec> NekoState<Sec> {
7171+ /// Create a new [`NekoState`] instance. It takes a protocol bytestring and encodes that
7272+ /// into the state, returning an initialised instance.
7373+ ///
7474+ /// ```
7575+ /// use wharrgarbl_neko::{NekoState, Neko128};
7676+ ///
7777+ /// let neko = NekoState::<Neko128>::new(b"whimsical");
7878+ ///
7979+ /// assert_eq!(format!("{neko}"), "NEKOv0.2.0/1600-128");
8080+ /// ```
6181 pub fn new(protocol: &[u8]) -> Self {
6262- let mut strobe = Self {
8282+ // OPS stack MUST be initialised with the INIT flag as the first op.
8383+ let ops_stack = [ops::INIT.bits(), 0, 0, 0];
8484+8585+ let mut neko = Self {
8686+ // The buffer state is the zeroed buffer layout for KeccakF1600: [u64; 25]
6387 state: Array([0u64; keccak::PLEN]),
8888+ // No bytes have been loaded into the buffer yet, therefore set to zero
6489 position: 0,
6565- start: 0,
9090+ // INIT op has been loaded into the stack, therefore op count must be 1.
9191+ ops_count: 1,
9292+ ops_stack: Array(ops_stack),
6693 sec: PhantomData,
6794 };
68959696+ // Preamble is defined with 0x01 as the first byte, the RATE as the second byte
9797+ // and then 0x07 & 0x60 as the third & fourth byte.
6998 let preamble: Array<u8, U4> = Array::from([0x01, Sec::rate() as u8, 0x07, 0x60]);
70997171- // This is safe because the static string is always 10 bytes long.
100100+ // This is safe because the specification version string is always 10 bytes long.
72101 let version: Array<u8, U10> = Array::try_from(NEKO_VERSION.as_bytes()).unwrap();
73102103103+ // The NEKO specification version is concatenated to the end of the preamble
74104 let combined = preamble.concat(version);
751057676- strobe.overwrite(&combined);
7777- strobe.overwrite(protocol);
106106+ // These two operations below DO NOT increment the ops counter, because it is defined
107107+ // as the INIT operation.
108108+ // Write the combined preamble+version to the zeroed buffer
109109+ NekoOperate::overwrite(&mut neko, &combined);
110110+ // Write the protocol byte string to the zeroed buffer
111111+ NekoOperate::overwrite(&mut neko, protocol);
781127979- strobe
113113+ // INIT operation has concluded, return the finalised NEKO state
114114+ neko
80115 }
8111682117 #[inline]
118118+ #[track_caller]
83119 fn begin_op(&mut self, red_flags: OpFlags) {
8484- let old_start = self.start;
8585- self.start = self.position + 1;
8686-8787- self.absorb(&[
8888- old_start as u8,
8989- red_flags.bits(),
9090- b'n',
9191- b'y',
9292- b'a',
9393- b'a',
9494- b'n',
9595- b'~',
9696- ]);
9797- }
9898-9999- fn permutation_f(&mut self) {
100100- let permuter = [
101101- self.start as u8,
102102- self.position as u8,
103103- 0x04,
104104- 0x80,
105105- b'M',
106106- b'E',
107107- b'O',
108108- b'W',
109109- ];
110110-111111- // XOR the permuter into first entropy block
112112- self.state[Sec::rate()] ^= u64::from_le_bytes(permuter);
120120+ // We can chain upto 4 OPs before needing to permute.
121121+ // So we can stack KEY+NONCE+AD ops before the next one
122122+ // MUST be a permuting op, like ENC/MAC/RATCHET/PRF.
123123+ // If we go over this, we've hit a failure state and MUST
124124+ // terminate.
125125+ assert!(self.ops_count < MAX_OPS);
113126114114- keccak::Keccak::new().with_f1600(|permute| permute(&mut self.state.0));
127127+ // Post-increment the ops counter.
128128+ let op_index = self.ops_count;
129129+ self.ops_count += 1;
115130116116- self.position = 0;
117117- self.start = 0;
131131+ // Encode the opflags to the available stack slot.
132132+ self.ops_stack[op_index] = red_flags.bits();
118133 }
119134120120- #[inline]
121121- fn absorb(&mut self, data: &[u8]) {
122122- NekoOperate::new(self, data).operate(|(state, byte)| *state ^= *byte);
123123- }
135135+ fn permutation_f(&mut self, continuation: OpFlags) {
136136+ // Last byte is zeroed in case the terminator overlaps
137137+ let permuter: Array<u8, U4> =
138138+ Array([self.ops_count as u8, self.position as u8, 0x80u8.to_le(), 0]);
124139125125- #[inline]
126126- fn absorb_and_set(&mut self, data: &mut [u8]) {
127127- NekoOperateMut::new(self, data).operate_mut(|(state, byte)| {
128128- *state ^= *byte;
129129- *byte = *state;
130130- });
131131- }
132132-133133- #[inline]
134134- fn copy_state(&mut self, data: &mut [u8]) {
135135- NekoOperateMut::new(self, data).operate_mut(|(state, byte)| *byte = *state);
136136- }
140140+ let permuter_block = u64::from_ne_bytes(self.ops_stack.concat(permuter).0);
137141138138- #[inline]
139139- fn exchange(&mut self, data: &mut [u8]) {
140140- NekoOperateMut::new(self, data).operate_mut(|(state, byte)| {
141141- *byte ^= *state;
142142- *state ^= *byte;
143143- });
144144- }
142142+ // XOR the permuter block into the next block, spilling into the first entropy block
143143+ // if required
144144+ self.state[self.position.div_ceil(U64_CHUNK) + 1] ^= permuter_block;
145145+ // Flip a bit in the last byte of the first entropy block with a 1 to act as the padding terminator.
146146+ // 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] ^=
148148+ 0x80u8.to_le().rotate_right(self.position as u32);
145149146146- #[inline]
147147- fn overwrite(&mut self, data: &[u8]) {
148148- NekoOperate::new(self, data).operate(|(state, byte)| *state = *byte);
149149- }
150150+ // 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));
150152151151- #[inline]
152152- fn squeeze(&mut self, data: &mut [u8]) {
153153- NekoOperateMut::new(self, data).operate_mut(|(state, byte)| {
154154- *byte = *state;
155155- *state = 0;
156156- });
153153+ // Reset the state, zeroing all counters/stack unless a CONTINUATION, in which case
154154+ // ops count is set to 1 and the first op slot is encoded with 0x01.
155155+ self.position = 0;
156156+ self.ops_count = continuation.bits() as usize;
157157+ self.ops_stack = Array([continuation.bits(), 0, 0, 0]);
157158 }
158159159160 #[inline]
160161 fn zero_state(&mut self) {
162162+ // Select the amount of bytes to zero, according to Security level
163163+ // 128 bits = 16 bytes to zero out to achieve forward secrecy
164164+ // 256 bits = 32 bytes to zero out to achieve forward secrecy
161165 let ratchet_bytes = Sec::ratchet_bytes();
162166163167 self.state[0..ratchet_bytes].iter_mut().for_each(|block| {
164168 *block = 0;
165169 });
166170167167- self.position += ratchet_bytes;
171171+ self.position += ratchet_bytes * U64_CHUNK;
168172 }
169173170174 /// Sets a provided key into the cipher state.
175175+ ///
176176+ /// **This is a NON-PERMUTING operation**.
171177 pub fn key(&mut self, data: &NekoKey<Sec>) {
172178 self.begin_op(ops::KEY);
173179174174- self.overwrite(data);
180180+ NekoOperate::overwrite(self, data);
175181 }
176182177183 /// Absorbs a nonce value into the cipher state
178178- pub fn nonce(&mut self, data: &NekoNonce) {
184184+ ///
185185+ /// **This is a NON-PERMUTING operation**.
186186+ pub fn nonce(&mut self, data: &NekoNonce<Sec>) {
179187 self.begin_op(ops::NONCE);
180188181181- self.absorb(data);
189189+ NekoOperate::absorb(self, data);
182190 }
183191184192 /// Absorb associated data into the cipher.
193193+ ///
194194+ /// **This is a NON-PERMUTING operation**.
185195 pub fn ad(&mut self, data: &[u8]) {
186196 self.begin_op(ops::AD);
187197188188- self.absorb(data);
198198+ NekoOperate::absorb(self, data);
189199 }
190200191201 /// Pseudo-random Function. Used to generate new keys/nonces from the
192202 /// cipher state.
203203+ ///
204204+ /// **This is a PERMUTING operation**
193205 pub fn prf(&mut self, data: &mut [u8]) {
206206+ self.permutation_f(ops::RST);
207207+194208 self.begin_op(ops::PRF);
195209196196- self.permutation_f();
197197-198198- self.squeeze(data);
210210+ NekoOperateMut::squeeze(self, data);
199211 }
200212201213 /// Create a message authentication code (MAC). This is used to validate the resulting
202214 /// state after ingesting/encrypting data.
215215+ ///
216216+ /// **This is a PERMUTING operation**
203217 pub fn create_mac(&mut self) -> NekoTag {
204204- self.begin_op(ops::MAC);
218218+ self.permutation_f(ops::RST);
205219206206- self.permutation_f();
220220+ self.begin_op(ops::MAC);
207221208222 let mut tag: NekoTag = Default::default();
209223210210- self.copy_state(&mut tag);
224224+ NekoOperateMut::copy_state(self, &mut tag);
211225212226 tag
213227 }
···215229 /// Validates a provided MAC. After ingesting/decrypting data, the resulting state
216230 /// of the cipher should be the same as the sender's. The MAC validates that this
217231 /// is correct. Any differences in length/bits/etc, should result in an invalid MAC.
232232+ ///
233233+ /// **This is a PERMUTING operation**
218234 pub fn verify_mac(&mut self, data: &NekoTag) -> aead::Result<()> {
235235+ self.permutation_f(ops::RST);
236236+219237 self.begin_op(ops::MAC);
220238221221- self.permutation_f();
222222-239239+ // Copy the original MAC, because we are going to mutate it as part of validation.
240240+ // If the MAC becomes all zeros, then it is valid. Any 1 bits in the
241241+ // mutated copy indicates an invalid MAC.
223242 let mut mac_copy = *data;
224243225225- self.exchange(&mut mac_copy);
244244+ NekoOperateMut::exchange(self, &mut mac_copy);
226245227246 let all_zero: NekoTag = Default::default();
228247···234253 }
235254236255 /// Takes a cleartext buffer, and encrypts it in place.
256256+ ///
257257+ /// **This is a PERMUTING operation**
237258 pub fn encrypt(&mut self, data: &mut [u8]) {
259259+ self.permutation_f(ops::RST);
260260+238261 self.begin_op(ops::ENC);
239262240240- self.permutation_f();
241241-242242- self.absorb_and_set(data);
263263+ NekoOperateMut::absorb_and_set(self, data);
243264 }
244265245266 /// Takes a ciphertext buffer, and decrypts it in place.
267267+ ///
268268+ /// **This is a PERMUTING operation**
246269 pub fn decrypt(&mut self, data: &mut [u8]) {
247247- self.begin_op(ops::ENC);
270270+ self.permutation_f(ops::RST);
248271249249- self.permutation_f();
272272+ self.begin_op(ops::ENC);
250273251251- self.exchange(data);
274274+ NekoOperateMut::exchange(self, data);
252275 }
253276254277 /// This is for mixing a cleartext message into the state. This is meant to be used
255278 /// both for when you send the cleartext message on one side, then when you receive the
256279 /// message on the other side.
280280+ ///
281281+ /// **This is a NON-PERMUTING operation**.
257282 pub fn cleartext(&mut self, data: &[u8]) {
258283 self.begin_op(ops::CLR);
259284260260- self.permutation_f();
261261-262262- self.absorb(data);
285285+ NekoOperate::absorb(self, data);
263286 }
264287265288 /// Permutes and zeros a portion of the cipher state in order to provide forward secrecy.
266289 /// The amount of bits zeroed is dependent on the cipher strength (128/256).
290290+ ///
291291+ /// **This is a PERMUTING operation**
267292 pub fn ratchet(&mut self) {
293293+ self.permutation_f(ops::RST);
294294+268295 self.begin_op(ops::RATCHET);
269269-270270- self.permutation_f();
271296272297 self.zero_state();
273298 }
···275300276301impl<S: NekoSec> core::fmt::Display for NekoState<S> {
277302 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
278278- f.write_str(NEKO_VERSION)?;
279279- write!(f, "/1600-{}", S::to_usize())
303303+ write!(f, "{}/1600-{}", NEKO_VERSION, S::to_usize())
280304 }
281305}
282306283307impl<S: NekoSec> core::fmt::Debug for NekoState<S> {
284308 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285285- // Do not reveal internal state of StrobeState, other than its security level
309309+ // Do not reveal internal state of NekoState, other than its security level
286310 f.debug_struct("NekoState")
287311 .field("sec", &S::to_usize())
288312 .field("version", &NEKO_VERSION)
···303327 let display = std::format!("{s}");
304328 let debug = std::format!("{s:?}");
305329306306- assert_eq!(&display, "NEKOv0.1.0/1600-128");
330330+ assert_eq!(&display, "NEKOv0.2.0/1600-128");
307331 assert_eq!(
308332 &debug,
309309- "NekoState { sec: 128, version: \"NEKOv0.1.0\", .. }"
333333+ "NekoState { sec: 128, version: \"NEKOv0.2.0\", .. }"
310334 );
311335 }
312336}
···2233use crate::flags::*;
4455+// Internal State machine flags. These only get used during internal ops, and are
66+// not invoked directly via user ops.
77+pub const RST: OpFlags = OpFlags::EMPTY;
88+pub const CONT: OpFlags = OpFlags::new(TRANSPORT.bits());
99+1010+// All User operations are defined below. All valid combinations must have either
1111+// two flags or three flags. 0, 1 or 4 flags toggled are INVALID for User operations
1212+// and are reserved for internal usage only.
1313+// All higher bits on OpFlags are also reserved, but for v0.2 Spec, are ALWAYS invalid
1414+// and should NEVER be used. If these are added, it will be for a new spec revision.
1515+1616+// Message related ops, either for encrypting, MAC, or ingesting cleartext
517pub const CLR: OpFlags = OpFlags::new(APP.bits() | TRANSPORT.bits());
618pub const MAC: OpFlags = OpFlags::new(CIPHER.bits() | TRANSPORT.bits());
719pub const ENC: OpFlags = OpFlags::new(APP.bits() | CIPHER.bits() | TRANSPORT.bits());
82099-pub const KEY: OpFlags = OpFlags::new(META.bits());
2121+// Cipher related ops, either for init/loading keys/nonces/associated data, or for
2222+// cryptographic operations like ratchet for forward secrecy, or prf for expanding
2323+// new keys/nonces/other randomised data.
2424+pub const INIT: OpFlags = OpFlags::new(META.bits() | APP.bits());
1025pub const NONCE: OpFlags = OpFlags::new(META.bits() | TRANSPORT.bits());
1111-pub const AD: OpFlags = OpFlags::new(META.bits() | APP.bits());
1212-pub const RATCHET: OpFlags = OpFlags::new(META.bits() | CIPHER.bits());
2626+pub const AD: OpFlags = OpFlags::new(META.bits() | APP.bits() | TRANSPORT.bits());
2727+pub const KEY: OpFlags = OpFlags::new(META.bits() | CIPHER.bits());
2828+pub const RATCHET: OpFlags = OpFlags::new(META.bits() | TRANSPORT.bits() | CIPHER.bits());
1329pub const PRF: OpFlags = OpFlags::new(META.bits() | APP.bits() | CIPHER.bits());