A whimsical STROBE based encryption protocol
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

NEKO v2 spec, improvements #3

open opened by sachy.dev targeting main from nekov0.2
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:usjm3ynnir6y4inkcdovrfei/sh.tangled.repo.pull/3mkv4dmpfw322
+459 -247
Diff #1
+1 -1
src/lib.rs
··· 10 10 extern crate alloc; 11 11 12 12 /// Version of WHARRGARBL that this crate implements. 13 - pub static WHARRGHARBL_PROTO: &str = "WGBLv0.1"; 13 + pub static WHARRGHARBL_PROTO: &str = "WGBLv0.2"; 14 14 15 15 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 16 16 #[repr(u8)]
+7 -3
src/transport.rs
··· 1 1 use core::marker::PhantomData; 2 2 3 - use aead::{AeadInOut, Buffer, Key, KeySizeUser, TagPosition}; 3 + use aead::{AeadInOut, Buffer, Key, KeySizeUser, TagPosition, common::IvSizeUser}; 4 4 use ctutils::{CtEq, CtSelect}; 5 5 use hybrid_array::AssocArraySize; 6 - use wharrgarbl_neko::{NekoNonce, NekoSec, NekoState, NekoTag}; 6 + use wharrgarbl_neko::{NekoSec, NekoState, NekoTag}; 7 7 8 8 use crate::{Role, WHARRGHARBL_PROTO}; 9 9 ··· 16 16 type KeySize = <NekoState<S> as KeySizeUser>::KeySize; 17 17 } 18 18 19 + impl<S: NekoSec> IvSizeUser for AeadNeko<S> { 20 + type IvSize = <NekoState<S> as IvSizeUser>::IvSize; 21 + } 22 + 19 23 impl<S: NekoSec> aead::AeadCore for AeadNeko<S> { 20 - type NonceSize = <NekoNonce as AssocArraySize>::Size; 24 + type NonceSize = <AeadNeko<S> as IvSizeUser>::IvSize; 21 25 type TagSize = <NekoTag as AssocArraySize>::Size; 22 26 const TAG_POSITION: TagPosition = TagPosition::Postfix; 23 27 }
-3
wharrgarbl-neko/Cargo.toml
··· 14 14 keccak = { version = "0.2" } 15 15 hybrid-array.workspace = true 16 16 zerocopy = "0.8.48" 17 - 18 - # [dev-dependencies] 19 - # zerocopy = "0.8.48"
+2 -2
wharrgarbl-neko/README.md
··· 2 2 3 3 A 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. 4 4 5 - ## SPEC 5 + ## Specification 6 6 7 - WIP 7 + Read about the NEKO encryption [specification here](./SPEC.md). 8 8 9 9 ☢️ **WARNING: DO NOT USE IN PRODUCTION. THIS CRATE IS NOT AUDITED AND IS WIP, MAYBE EVEN NOT AS SECURE AS THOUGHT. AAAAAAAAAA** ☢️ 10 10
+116
wharrgarbl-neko/SPEC.md
··· 1 + # NEKO Specification 2 + 3 + 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. 4 + 5 + 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. 6 + 7 + 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: 8 + 9 + #### Non-Permuting 10 + 11 + - Key (256-bit value) 12 + - Nonce (128-bit value) 13 + - Ad (Associated Data) 14 + - Cleartext 15 + 16 + #### Permuting 17 + 18 + - Encrypt 19 + - Decrypt 20 + - Create MAC 21 + - Verify MAC 22 + - Ratchet 23 + - Prf (Pseudo-Random Function) 24 + 25 + 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. 26 + 27 + 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. 28 + 29 + ## Construction 30 + 31 + 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. 32 + 33 + 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. 34 + 35 + 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. 36 + 37 + 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. 38 + 39 + 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. 40 + 41 + Internally, each operation *operates* on the data with one of 7 different actions: 42 + 43 + - **ABSORB**: This XOR's the input directly into the state: `State ^ Input`. This does not mutate the input. 44 + - **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. 45 + - **COPY STATE**: This copies out the state directly into the input: `Input = State`. This mutates the input. 46 + - **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. 47 + - **OVERWRITE**: This sets the state with the input bytes directly: `State = Input`. This does not mutate the input. 48 + - **SQUEEZE**: This sets the input with the state, then zeroes out the state. `Input = State, State = 0`. This mutates the input. 49 + - **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`. 50 + 51 + ## Flag Definitions 52 + 53 + 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: 54 + 55 + | FLAG | Bits | Value | 56 + | --------- | ------ | ----- | 57 + | TRANSPORT | 0b0001 | 1 | 58 + | APP | 0b0010 | 2 | 59 + | CIPHER | 0b0100 | 4 | 60 + | META | 0b1000 | 8 | 61 + 62 + 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: 63 + 64 + | OP | FLAGS | VALUE | 65 + | ----- | --------- | ------------- | 66 + | RESET | EMPTY | 0b0000, 0 | 67 + | CONT | TRANSPORT | 0b0001, 1 | 68 + 69 + 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. 70 + 71 + The rest of the valid ops are available to be invoked by user operations. They are defined as follows: 72 + 73 + | OP | FLAGS | VALUE | 74 + | ------- | --------------------------- | ---------- | 75 + | CLR | APP \| TRANSPORT | 0b0011, 3 | 76 + | MAC | CIPHER \| TRANSPORT | 0b0101, 5 | 77 + | ENC | CIPHER \| APP \| TRANSPORT | 0b0111, 7 | 78 + | NONCE | META \| TRANSPORT | 0b1001, 9 | 79 + | INIT | META \| APP | 0b1010, 10 | 80 + | AD | META \| APP \| TRANSPORT | 0b1011, 11 | 81 + | KEY | META \| CIPHER | 0b1100, 12 | 82 + | RATCHET | META \| CIPHER \| TRANSPORT | 0b1101, 13 | 83 + | PRF | META \| CIPHER \| APP | 0b1110, 14 | 84 + 85 + 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. 86 + 87 + ## Initialisation 88 + 89 + 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]`; 90 + 91 + 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. 92 + 93 + 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. 94 + 95 + Additionally, a protocol byte string can be written to the state, following after the preamble+version with its own `OVERWRITE` action. 96 + 97 + Once done, the state is finalised and ready to be used. 98 + 99 + ## User operations 100 + 101 + With an initialised Neko state, the following operations are available to the user (with the internal action & OP flag the state takes being described): 102 + 103 + - `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). 104 + - `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). 105 + - `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). 106 + - `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. 107 + - `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. 108 + - `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). 109 + - `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. 110 + - `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. 111 + - `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. 112 + - `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. 113 + 114 + 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. 115 + 116 + 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
··· 13 13 let second = neko.state[1].to_le_bytes(); 14 14 let third = neko.state[2].to_le_bytes(); 15 15 16 - assert_eq!(&first, b"\x01\x16\x07\x60NEKO"); 16 + assert_eq!(&first, b"\x01\x14\x07\x60NEKO"); 17 17 // Values that don't fill the block entirely leave padding 18 - assert_eq!(&second, b"v0.1.0\0\0"); 18 + assert_eq!(&second, b"v0.2.0\0\0"); 19 19 assert_eq!(&third[..4], b"test"); 20 20 // The rest of the state is zeroed 21 21 assert_eq!(&neko.state[3..], &[0; 22]); ··· 29 29 let second = neko.state[1].to_le_bytes(); 30 30 let third = neko.state[2].to_le_bytes(); 31 31 32 - assert_eq!(&first, b"\x01\x14\x07\x60NEKO"); 32 + assert_eq!(&first, b"\x01\x10\x07\x60NEKO"); 33 33 // Values that don't fill the block entirely leave padding 34 - assert_eq!(&second, b"v0.1.0\0\0"); 34 + assert_eq!(&second, b"v0.2.0\0\0"); 35 35 assert_eq!(&third[..4], b"test"); 36 36 // The rest of the state is zeroed 37 37 assert_eq!(&neko.state[3..], &[0; 22]); ··· 88 88 neko.nonce(&nonce); 89 89 neko.ad(ad); 90 90 91 - let op = neko.state[3].as_bytes(); 92 - let state = neko.state[4..8].as_bytes(); 91 + let state = neko.state[3..7].as_bytes(); 93 92 94 - // OPs are XOR'd onto the state, but when the state is zeroed, it 95 - // is the same as overwriting, so the ops are visible. 96 - assert_eq!(op, b"\x00\x08nyaan~"); 97 93 assert_eq!(state, key.as_slice()); 98 94 99 - let op = neko.state[8].as_bytes(); 100 - let state = neko.state[9..11].as_bytes(); 95 + let state = neko.state[7..9].as_bytes(); 101 96 102 - assert_eq!(op, b"\x04\x09nyaan~"); 103 97 assert_eq!(state, nonce.as_slice()); 104 98 105 - let op = neko.state[11].as_bytes(); 106 - let state = neko.state[12].as_bytes(); 99 + let state = neko.state[9].as_bytes(); 107 100 108 - assert_eq!(op, b"\x09\x0Anyaan~"); 109 101 assert_eq!(state, b"addz\0\0\0\0"); 110 102 111 - assert_eq!(&neko.state[13..], &[0; 12]); 103 + let ops = &neko.ops_stack; 104 + // OPs are XOR'd onto the state, but when the state is zeroed, it 105 + // is the same as overwriting, so the ops are visible. 106 + assert_eq!(ops, &[0x0A, 0x0C, 0x09, 0x0B]); 107 + // The rest of the state is zeroed 108 + assert_eq!(&neko.state[13..22], &[0; 9]); 109 + assert_eq!(&neko.state[23..], &[0; 2]); 110 + 111 + // We must permute at this point 112 + neko.ratchet(); 113 + 114 + let expected_state = [ 115 + 0x0000000000000000, 116 + 0x0000000000000000, 117 + 0x14fd15236a301dbc, 118 + 0x3d7a0f031c2332c7, 119 + 0x13d95db32a39a74c, 120 + 0xbfce1f9678690375, 121 + 0xc444bd0f9bb70133, 122 + 0x59600201db93b1de, 123 + 0x6bc376b646e898ea, 124 + 0x2e8a6c345fd3dca3, 125 + 0x9df94788a5fc9f4d, 126 + 0x2541272cca7a631c, 127 + 0xabc8b248a4e0eee3, 128 + 0x2a6befaf570b0120, 129 + 0x5e296ccc9b587798, 130 + 0x9b9d5caef6fc7d3c, 131 + 0x371099e20d7965db, 132 + 0x52b7fecf8d06aed7, 133 + 0xae285d1c6cada2c7, 134 + 0x12d17a37884449ea, 135 + 0x85846b16d640a55b, 136 + 0x0e7d16c0c2bf5a3e, 137 + 0xce32132c0b110014, 138 + 0x6620fda4f7642f84, 139 + 0xd1ea97aadb49a663, 140 + ]; 112 141 113 - // Cleartext will permute the state 142 + assert_eq!(&neko.state, &expected_state); 143 + 144 + let orig_state = neko.state[2]; 145 + 146 + // Cleartext will not permute the state unless the text is larger 147 + // than the available permuted buffer. 114 148 neko.cleartext(cleartext); 115 149 116 - for block in neko.state[0..22].iter().map(IntoBytes::as_bytes) { 150 + assert_eq!(&neko.ops_stack, &[0x0D, 0x03, 0, 0]); 151 + 152 + for block in neko.state[2..20].iter().map(IntoBytes::as_bytes) { 117 153 // The permuted state means the message is mixed, so it can no longer be "read" 118 154 // directly. 119 - assert_ne!(block, b"\x0c\x03nyaan~"); 120 155 assert_ne!(block, cleartext); 121 156 } 122 157 let orig_text = u64::from_le_bytes(*cleartext); 123 - let orig_state = neko.state[0] ^ orig_text; 124 - let recreated = neko.state[0] ^ orig_state; 158 + // RECREATE 159 + let recreated = neko.state[2] ^ orig_state; 125 160 126 - // After XOR'ing from the state, we can reconstruct the cleartext, so we can confirm 127 - // it is there. 161 + // After XOR'ing the original state from the modified state, we can reconstruct the cleartext 162 + // so we can confirm it is there. 128 163 assert_eq!(orig_text, recreated); 129 164 } 130 165 ··· 152 187 assert_eq!(neko.state[0..2].as_bytes(), &message); 153 188 154 189 let expected_state = [ 155 - 0x14bf7a55ecbfef9f, 156 - 0xbf881650acfa3419, 157 - 0x19e0cf77f80c9b9c, 158 - 0x926cb071dcc542bf, 159 - 0xb0c2ff19b2a1088a, 160 - 0x28e1786b57d258b5, 161 - 0xa10f75b4a49c5830, 162 - 0xe0d2a4c88992ddab, 163 - 0xf26af8755e5d7ce8, 164 - 0xa130fa26d8697da0, 165 - 0x2d6266942d30bb73, 166 - 0x0c47cacae106048e, 167 - 0x1f78293be88890d7, 168 - 0xcf24691274e96761, 169 - 0x0501d35fa5a7db34, 170 - 0xd242e3a40bf885f8, 171 - 0x717c3e620ea21bdf, 172 - 0x12af362391a15895, 173 - 0x8df18b22be842ea5, 174 - 0xa32fd58b7771026c, 175 - 0x4a61af391e658ec5, 176 - 0x189f225280d6d986, 177 - 0x2718bc59d75e8d60, 178 - 0x0be0f806c2086f5a, 179 - 0xd5f2731cc9c0fed0, 190 + 0x33e5964098e69bb2, 191 + 0xe8aae76360864f1d, 192 + 0x6c10b3c273ae582c, 193 + 0xd9584d46c8025d46, 194 + 0x8eeace52fffacd4c, 195 + 0x2346ce9726155884, 196 + 0x0f3427af0a3c77f1, 197 + 0xe2706ecbbd9596b4, 198 + 0x840b7500b73e537c, 199 + 0x0015960758c2e30e, 200 + 0xf2cd5efad521e8e2, 201 + 0xca7199cf34822634, 202 + 0xe21f1c3744135b1a, 203 + 0xe91599f57a74f2c9, 204 + 0x1395bb13bd8eec8d, 205 + 0x8417dd11dfee0671, 206 + 0x95d9c20086520a10, 207 + 0x90cfb46fc5a4963d, 208 + 0x2aaf5cd4d234a06d, 209 + 0x5e4372caf96bd84a, 210 + 0x6a858536f819bb62, 211 + 0xf7f33ca323c59700, 212 + 0x38b5d9a41b4d08e1, 213 + 0x39c33af857ae9a82, 214 + 0x39ed20d798ecd321, 180 215 ]; 181 216 182 217 assert_eq!(&neko.state, &expected_state); ··· 188 223 let ratched_expected_state = [ 189 224 0x0000000000000000, 190 225 0x0000000000000000, 191 - 0xaeaae19feedba131, 192 - 0x142c658c8c5d4d41, 193 - 0x5b15a77c99a73daa, 194 - 0xe8060d814eb54fe2, 195 - 0xaf03a57bc4107f06, 196 - 0x20a98fb94a745e23, 197 - 0x311dd2be1529baad, 198 - 0x4d50f23f34057523, 199 - 0xea89ea72449476d5, 200 - 0x82457373a57d4062, 201 - 0x0aad58af56986ea2, 202 - 0x8548e7cb9b7907c3, 203 - 0xca95c689bed5fbbb, 204 - 0x426b6a66b930e5ad, 205 - 0x514c8865b88f89ea, 206 - 0x361e3d8a73cd12f1, 207 - 0xbcd3e3c6949cec98, 208 - 0x6b210034af33aeac, 209 - 0x2d06ca5415ba4459, 210 - 0x57dfabd826619f1e, 211 - 0x69f4dd80c2791cc4, 212 - 0xd9086f9fde008f52, 213 - 0xd71233e8001d2c80, 226 + 0xe72010d5d3b254c0, 227 + 0x34007830a1c7585d, 228 + 0xbcd95dec900847a5, 229 + 0xfe1be2676e130078, 230 + 0xe6c29c4c48f292e6, 231 + 0x3d711aed9763e259, 232 + 0xa9b1329692f19ebd, 233 + 0xf1378893d98ad184, 234 + 0xe6f31bb95f5c361f, 235 + 0x549decbceeac0f78, 236 + 0x81b85f3f3d3a687d, 237 + 0x7dac55db9b73bf34, 238 + 0x6f07454e89ec5950, 239 + 0x9abd19c6e33eec92, 240 + 0x7358043c28de6955, 241 + 0x625421243d6b4bd5, 242 + 0xc986349494886128, 243 + 0xc00e8e52d7734dff, 244 + 0xbafa4f2b57d93144, 245 + 0x022f1aa724503cd5, 246 + 0x4b3633798cc9ae5e, 247 + 0x532b723068ab8c72, 248 + 0xa48327750108017c, 214 249 ]; 215 250 216 251 assert_eq!(&neko.state, &ratched_expected_state);
+131 -107
wharrgarbl-neko/src/lib.rs
··· 12 12 13 13 use aead::{ 14 14 KeySizeUser, 15 + common::IvSizeUser, 15 16 consts::{U4, U10, U16, U25, U32, U128, U256}, 16 17 }; 17 18 use ctutils::CtEq; 18 19 use hybrid_array::Array; 19 20 use wharrgarbl_utils::OpFlags; 21 + use zerocopy::IntoBytes; 20 22 21 23 use crate::operators::{NekoOperate, NekoOperateMut}; 22 24 pub use crate::traits::NekoSec; 23 25 24 26 pub type Neko128 = U128; 25 27 pub type Neko256 = U256; 26 - pub type NekoNonce = Array<u8, U16>; 28 + pub type NekoNonce<S> = Array<u8, <NekoState<S> as IvSizeUser>::IvSize>; 27 29 pub type NekoKey<S> = Array<u8, <NekoState<S> as KeySizeUser>::KeySize>; 28 30 pub type NekoTag = Array<u8, U16>; 29 31 30 - pub static NEKO_VERSION: &str = "NEKOv0.1.0"; 32 + pub static NEKO_VERSION: &str = "NEKOv0.2.0"; 33 + const U64_CHUNK: usize = core::mem::size_of::<u64>(); 34 + const MAX_OPS: usize = core::mem::size_of::<u32>(); 31 35 32 36 impl<S: NekoSec> KeySizeUser for NekoState<S> { 33 37 type KeySize = U32; 34 38 } 35 39 40 + impl<S: NekoSec> IvSizeUser for NekoState<S> { 41 + type IvSize = U16; 42 + } 43 + 36 44 #[derive(Clone)] 37 45 pub struct NekoState<S: NekoSec> { 38 46 pub(crate) state: Array<u64, U25>, 39 47 position: usize, 40 - start: usize, 48 + ops_count: usize, 49 + pub(crate) ops_stack: Array<u8, U4>, 41 50 sec: PhantomData<S>, 42 51 } 43 52 ··· 45 54 fn zeroize(&mut self) { 46 55 self.state.zeroize(); 47 56 self.position.zeroize(); 48 - self.position.zeroize(); 57 + self.ops_count.zeroize(); 58 + self.ops_stack.zeroize(); 49 59 } 50 60 } 51 61 ··· 58 68 } 59 69 60 70 impl<Sec: NekoSec> NekoState<Sec> { 71 + /// Create a new [`NekoState`] instance. It takes a protocol bytestring and encodes that 72 + /// into the state, returning an initialised instance. 73 + /// 74 + /// ``` 75 + /// use wharrgarbl_neko::{NekoState, Neko128}; 76 + /// 77 + /// let neko = NekoState::<Neko128>::new(b"whimsical"); 78 + /// 79 + /// assert_eq!(format!("{neko}"), "NEKOv0.2.0/1600-128"); 80 + /// ``` 61 81 pub fn new(protocol: &[u8]) -> Self { 62 - let mut strobe = Self { 82 + // OPS stack MUST be initialised with the INIT flag as the first op. 83 + let ops_stack = [ops::INIT.bits(), 0, 0, 0]; 84 + 85 + let mut neko = Self { 86 + // The buffer state is the zeroed buffer layout for KeccakF1600: [u64; 25] 63 87 state: Array([0u64; keccak::PLEN]), 88 + // No bytes have been loaded into the buffer yet, therefore set to zero 64 89 position: 0, 65 - start: 0, 90 + // INIT op has been loaded into the stack, therefore op count must be 1. 91 + ops_count: 1, 92 + ops_stack: Array(ops_stack), 66 93 sec: PhantomData, 67 94 }; 68 95 96 + // Preamble is defined with 0x01 as the first byte, the RATE as the second byte 97 + // and then 0x07 & 0x60 as the third & fourth byte. 69 98 let preamble: Array<u8, U4> = Array::from([0x01, Sec::rate() as u8, 0x07, 0x60]); 70 99 71 - // This is safe because the static string is always 10 bytes long. 100 + // This is safe because the specification version string is always 10 bytes long. 72 101 let version: Array<u8, U10> = Array::try_from(NEKO_VERSION.as_bytes()).unwrap(); 73 102 103 + // The NEKO specification version is concatenated to the end of the preamble 74 104 let combined = preamble.concat(version); 75 105 76 - strobe.overwrite(&combined); 77 - strobe.overwrite(protocol); 106 + // These two operations below DO NOT increment the ops counter, because it is defined 107 + // as the INIT operation. 108 + // Write the combined preamble+version to the zeroed buffer 109 + NekoOperate::overwrite(&mut neko, &combined); 110 + // Write the protocol byte string to the zeroed buffer 111 + NekoOperate::overwrite(&mut neko, protocol); 78 112 79 - strobe 113 + // INIT operation has concluded, return the finalised NEKO state 114 + neko 80 115 } 81 116 82 117 #[inline] 118 + #[track_caller] 83 119 fn begin_op(&mut self, red_flags: OpFlags) { 84 - let old_start = self.start; 85 - self.start = self.position + 1; 86 - 87 - self.absorb(&[ 88 - old_start as u8, 89 - red_flags.bits(), 90 - b'n', 91 - b'y', 92 - b'a', 93 - b'a', 94 - b'n', 95 - b'~', 96 - ]); 97 - } 98 - 99 - fn permutation_f(&mut self) { 100 - let permuter = [ 101 - self.start as u8, 102 - self.position as u8, 103 - 0x04, 104 - 0x80, 105 - b'M', 106 - b'E', 107 - b'O', 108 - b'W', 109 - ]; 110 - 111 - // XOR the permuter into first entropy block 112 - self.state[Sec::rate()] ^= u64::from_le_bytes(permuter); 113 - 114 - keccak::Keccak::new().with_f1600(|permute| permute(&mut self.state.0)); 115 - 116 - self.position = 0; 117 - self.start = 0; 120 + // We can chain upto 4 OPs before needing to permute. 121 + // So we can stack KEY+NONCE+AD ops before the next one 122 + // MUST be a permuting op, like ENC/MAC/RATCHET/PRF. 123 + // If we go over this, we've hit a failure state and MUST 124 + // terminate. 125 + assert!(self.ops_count < MAX_OPS); 126 + 127 + // Post-increment the ops counter. 128 + let op_index = self.ops_count; 129 + self.ops_count += 1; 130 + 131 + // Encode the opflags to the available stack slot. 132 + self.ops_stack[op_index] = red_flags.bits(); 118 133 } 119 134 120 - #[inline] 121 - fn absorb(&mut self, data: &[u8]) { 122 - NekoOperate::new(self, data).operate(|(state, byte)| *state ^= *byte); 123 - } 124 - 125 - #[inline] 126 - fn absorb_and_set(&mut self, data: &mut [u8]) { 127 - NekoOperateMut::new(self, data).operate_mut(|(state, byte)| { 128 - *state ^= *byte; 129 - *byte = *state; 130 - }); 131 - } 135 + fn permutation_f(&mut self, continuation: OpFlags) { 136 + // Last byte is zeroed in case the terminator overlaps 137 + let permuter: Array<u8, U4> = 138 + Array([self.ops_count as u8, self.position as u8, 0x80u8.to_le(), 0]); 132 139 133 - #[inline] 134 - fn copy_state(&mut self, data: &mut [u8]) { 135 - NekoOperateMut::new(self, data).operate_mut(|(state, byte)| *byte = *state); 136 - } 140 + let permuter_block = u64::from_ne_bytes(self.ops_stack.concat(permuter).0); 137 141 138 - #[inline] 139 - fn exchange(&mut self, data: &mut [u8]) { 140 - NekoOperateMut::new(self, data).operate_mut(|(state, byte)| { 141 - *byte ^= *state; 142 - *state ^= *byte; 143 - }); 144 - } 142 + // XOR the permuter block into the next block, spilling into the first entropy block 143 + // if required 144 + self.state[self.position.div_ceil(U64_CHUNK) + 1] ^= permuter_block; 145 + // Flip a bit in the last byte of the first entropy block with a 1 to act as the padding terminator. 146 + // The bit is selected via rotating right the value 0x80 (0b1000_0000) by the position counter. 147 + self.state[Sec::rate()].as_mut_bytes()[7] ^= 148 + 0x80u8.to_le().rotate_right(self.position as u32); 145 149 146 - #[inline] 147 - fn overwrite(&mut self, data: &[u8]) { 148 - NekoOperate::new(self, data).operate(|(state, byte)| *state = *byte); 149 - } 150 + // The state has been fully prepared, and now can be permuted by the F1600 function. 151 + keccak::Keccak::new().with_f1600(|permute| permute(&mut self.state.0)); 150 152 151 - #[inline] 152 - fn squeeze(&mut self, data: &mut [u8]) { 153 - NekoOperateMut::new(self, data).operate_mut(|(state, byte)| { 154 - *byte = *state; 155 - *state = 0; 156 - }); 153 + // Reset the state, zeroing all counters/stack unless a CONTINUATION, in which case 154 + // ops count is set to 1 and the first op slot is encoded with 0x01. 155 + self.position = 0; 156 + self.ops_count = continuation.bits() as usize; 157 + self.ops_stack = Array([continuation.bits(), 0, 0, 0]); 157 158 } 158 159 159 160 #[inline] 160 161 fn zero_state(&mut self) { 162 + // Select the amount of bytes to zero, according to Security level 163 + // 128 bits = 16 bytes to zero out to achieve forward secrecy 164 + // 256 bits = 32 bytes to zero out to achieve forward secrecy 161 165 let ratchet_bytes = Sec::ratchet_bytes(); 162 166 163 167 self.state[0..ratchet_bytes].iter_mut().for_each(|block| { 164 168 *block = 0; 165 169 }); 166 170 167 - self.position += ratchet_bytes; 171 + self.position += ratchet_bytes * U64_CHUNK; 168 172 } 169 173 170 174 /// Sets a provided key into the cipher state. 175 + /// 176 + /// **This is a NON-PERMUTING operation**. 171 177 pub fn key(&mut self, data: &NekoKey<Sec>) { 172 178 self.begin_op(ops::KEY); 173 179 174 - self.overwrite(data); 180 + NekoOperate::overwrite(self, data); 175 181 } 176 182 177 183 /// Absorbs a nonce value into the cipher state 178 - pub fn nonce(&mut self, data: &NekoNonce) { 184 + /// 185 + /// **This is a NON-PERMUTING operation**. 186 + pub fn nonce(&mut self, data: &NekoNonce<Sec>) { 179 187 self.begin_op(ops::NONCE); 180 188 181 - self.absorb(data); 189 + NekoOperate::absorb(self, data); 182 190 } 183 191 184 192 /// Absorb associated data into the cipher. 193 + /// 194 + /// **This is a NON-PERMUTING operation**. 185 195 pub fn ad(&mut self, data: &[u8]) { 186 196 self.begin_op(ops::AD); 187 197 188 - self.absorb(data); 198 + NekoOperate::absorb(self, data); 189 199 } 190 200 191 201 /// Pseudo-random Function. Used to generate new keys/nonces from the 192 202 /// cipher state. 203 + /// 204 + /// **This is a PERMUTING operation** 193 205 pub fn prf(&mut self, data: &mut [u8]) { 194 - self.begin_op(ops::PRF); 206 + self.permutation_f(ops::RST); 195 207 196 - self.permutation_f(); 208 + self.begin_op(ops::PRF); 197 209 198 - self.squeeze(data); 210 + NekoOperateMut::squeeze(self, data); 199 211 } 200 212 201 213 /// Create a message authentication code (MAC). This is used to validate the resulting 202 214 /// state after ingesting/encrypting data. 215 + /// 216 + /// **This is a PERMUTING operation** 203 217 pub fn create_mac(&mut self) -> NekoTag { 204 - self.begin_op(ops::MAC); 218 + self.permutation_f(ops::RST); 205 219 206 - self.permutation_f(); 220 + self.begin_op(ops::MAC); 207 221 208 222 let mut tag: NekoTag = Default::default(); 209 223 210 - self.copy_state(&mut tag); 224 + NekoOperateMut::copy_state(self, &mut tag); 211 225 212 226 tag 213 227 } ··· 215 229 /// Validates a provided MAC. After ingesting/decrypting data, the resulting state 216 230 /// of the cipher should be the same as the sender's. The MAC validates that this 217 231 /// is correct. Any differences in length/bits/etc, should result in an invalid MAC. 232 + /// 233 + /// **This is a PERMUTING operation** 218 234 pub fn verify_mac(&mut self, data: &NekoTag) -> aead::Result<()> { 219 - self.begin_op(ops::MAC); 235 + self.permutation_f(ops::RST); 220 236 221 - self.permutation_f(); 237 + self.begin_op(ops::MAC); 222 238 239 + // Copy the original MAC, because we are going to mutate it as part of validation. 240 + // If the MAC becomes all zeros, then it is valid. Any 1 bits in the 241 + // mutated copy indicates an invalid MAC. 223 242 let mut mac_copy = *data; 224 243 225 - self.exchange(&mut mac_copy); 244 + NekoOperateMut::exchange(self, &mut mac_copy); 226 245 227 246 let all_zero: NekoTag = Default::default(); 228 247 ··· 234 253 } 235 254 236 255 /// Takes a cleartext buffer, and encrypts it in place. 256 + /// 257 + /// **This is a PERMUTING operation** 237 258 pub fn encrypt(&mut self, data: &mut [u8]) { 238 - self.begin_op(ops::ENC); 259 + self.permutation_f(ops::RST); 239 260 240 - self.permutation_f(); 261 + self.begin_op(ops::ENC); 241 262 242 - self.absorb_and_set(data); 263 + NekoOperateMut::absorb_and_set(self, data); 243 264 } 244 265 245 266 /// Takes a ciphertext buffer, and decrypts it in place. 267 + /// 268 + /// **This is a PERMUTING operation** 246 269 pub fn decrypt(&mut self, data: &mut [u8]) { 247 - self.begin_op(ops::ENC); 270 + self.permutation_f(ops::RST); 248 271 249 - self.permutation_f(); 272 + self.begin_op(ops::ENC); 250 273 251 - self.exchange(data); 274 + NekoOperateMut::exchange(self, data); 252 275 } 253 276 254 277 /// This is for mixing a cleartext message into the state. This is meant to be used 255 278 /// both for when you send the cleartext message on one side, then when you receive the 256 279 /// message on the other side. 280 + /// 281 + /// **This is a NON-PERMUTING operation**. 257 282 pub fn cleartext(&mut self, data: &[u8]) { 258 283 self.begin_op(ops::CLR); 259 284 260 - self.permutation_f(); 261 - 262 - self.absorb(data); 285 + NekoOperate::absorb(self, data); 263 286 } 264 287 265 288 /// Permutes and zeros a portion of the cipher state in order to provide forward secrecy. 266 289 /// The amount of bits zeroed is dependent on the cipher strength (128/256). 290 + /// 291 + /// **This is a PERMUTING operation** 267 292 pub fn ratchet(&mut self) { 268 - self.begin_op(ops::RATCHET); 293 + self.permutation_f(ops::RST); 269 294 270 - self.permutation_f(); 295 + self.begin_op(ops::RATCHET); 271 296 272 297 self.zero_state(); 273 298 } ··· 275 300 276 301 impl<S: NekoSec> core::fmt::Display for NekoState<S> { 277 302 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 278 - f.write_str(NEKO_VERSION)?; 279 - write!(f, "/1600-{}", S::to_usize()) 303 + write!(f, "{}/1600-{}", NEKO_VERSION, S::to_usize()) 280 304 } 281 305 } 282 306 283 307 impl<S: NekoSec> core::fmt::Debug for NekoState<S> { 284 308 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 285 - // Do not reveal internal state of StrobeState, other than its security level 309 + // Do not reveal internal state of NekoState, other than its security level 286 310 f.debug_struct("NekoState") 287 311 .field("sec", &S::to_usize()) 288 312 .field("version", &NEKO_VERSION) ··· 303 327 let display = std::format!("{s}"); 304 328 let debug = std::format!("{s:?}"); 305 329 306 - assert_eq!(&display, "NEKOv0.1.0/1600-128"); 330 + assert_eq!(&display, "NEKOv0.2.0/1600-128"); 307 331 assert_eq!( 308 332 &debug, 309 - "NekoState { sec: 128, version: \"NEKOv0.1.0\", .. }" 333 + "NekoState { sec: 128, version: \"NEKOv0.2.0\", .. }" 310 334 ); 311 335 } 312 336 }
+72 -53
wharrgarbl-neko/src/operators.rs
··· 1 1 use zerocopy::IntoBytes; 2 2 3 - use crate::{NekoSec, NekoState}; 4 - 5 - const U64_CHUNK: usize = core::mem::size_of::<u64>(); 3 + use crate::{NekoSec, NekoState, U64_CHUNK, ops}; 6 4 7 5 pub(crate) struct NekoOperateMut<'s, S: NekoSec> { 8 6 neko: &'s mut NekoState<S>, ··· 10 8 } 11 9 12 10 impl<'s, S: NekoSec> NekoOperateMut<'s, S> { 13 - #[inline] 14 - pub(crate) const fn new(neko: &'s mut NekoState<S>, data: &'s mut [u8]) -> Self { 11 + #[inline(always)] 12 + const fn new(neko: &'s mut NekoState<S>, data: &'s mut [u8]) -> Self { 15 13 Self { neko, data } 16 14 } 17 15 18 - #[inline] 19 - fn next_block(&self) -> Option<usize> { 20 - match self 21 - .data 22 - .len() 23 - .min((S::rate() - self.neko.position) * U64_CHUNK) 24 - { 25 - 0 => None, 26 - take => Some(take), 27 - } 28 - } 29 - 30 - #[inline] 31 - pub(crate) fn operate_mut(&'s mut self, operation: fn((&mut u8, &mut u8))) { 32 - while let Some(take) = self.next_block() { 33 - let (block, rest) = self.data.split_at_mut(take); 34 - 35 - self.data = rest; 36 - 16 + #[inline(always)] 17 + fn operate_mut(&'s mut self, operation: fn((&mut u8, &mut u8))) { 18 + loop { 37 19 // Trans the neko 38 - let transed_bytes = self.neko.state[self.neko.position..].as_mut_bytes(); 20 + let transed_bytes = 21 + self.neko.state[self.neko.position.div_ceil(U64_CHUNK)..S::rate()].as_mut_bytes(); 22 + 23 + let take = transed_bytes 24 + .iter_mut() 25 + .zip(self.data.iter_mut()) 26 + .map(operation) 27 + .count(); 39 28 40 - transed_bytes.iter_mut().zip(block).for_each(operation); 29 + self.data = &mut self.data[take..]; 41 30 42 31 if !self.data.is_empty() { 43 - self.neko.permutation_f(); 32 + self.neko.permutation_f(ops::CONT); 44 33 } else { 45 - self.neko.position += take.div_ceil(U64_CHUNK); 34 + self.neko.position += take; 46 35 break; 47 36 } 48 37 } 49 38 } 50 - } 51 39 52 - pub(crate) struct NekoOperate<'s, S: NekoSec> { 53 - neko: &'s mut NekoState<S>, 54 - data: &'s [u8], 55 - } 40 + #[inline] 41 + pub(crate) fn absorb_and_set(neko: &'s mut NekoState<S>, data: &'s mut [u8]) { 42 + NekoOperateMut::new(neko, data).operate_mut(|(state, byte)| { 43 + *state ^= *byte; 44 + *byte = *state; 45 + }); 46 + } 56 47 57 - impl<'s, S: NekoSec> NekoOperate<'s, S> { 58 48 #[inline] 59 - pub(crate) const fn new(neko: &'s mut NekoState<S>, data: &'s [u8]) -> Self { 60 - Self { neko, data } 49 + pub(crate) fn copy_state(neko: &'s mut NekoState<S>, data: &'s mut [u8]) { 50 + NekoOperateMut::new(neko, data).operate_mut(|(state, byte)| *byte = *state); 61 51 } 62 52 63 53 #[inline] 64 - fn next_block(&self) -> Option<usize> { 65 - match self 66 - .data 67 - .len() 68 - .min((S::rate() - self.neko.position) * U64_CHUNK) 69 - { 70 - 0 => None, 71 - take => Some(take), 72 - } 54 + pub(crate) fn exchange(neko: &'s mut NekoState<S>, data: &'s mut [u8]) { 55 + NekoOperateMut::new(neko, data).operate_mut(|(state, byte)| { 56 + *byte ^= *state; 57 + *state ^= *byte; 58 + }); 73 59 } 74 60 75 61 #[inline] 76 - pub(crate) fn operate(&'s mut self, operation: fn((&mut u8, &u8))) { 77 - while let Some(take) = self.next_block() { 78 - let (block, rest) = self.data.split_at(take); 62 + pub(crate) fn squeeze(neko: &'s mut NekoState<S>, data: &'s mut [u8]) { 63 + NekoOperateMut::new(neko, data).operate_mut(|(state, byte)| { 64 + *byte = *state; 65 + *state = 0; 66 + }); 67 + } 68 + } 79 69 80 - self.data = rest; 70 + pub(crate) struct NekoOperate<'s, S: NekoSec> { 71 + neko: &'s mut NekoState<S>, 72 + data: &'s [u8], 73 + } 74 + 75 + impl<'s, S: NekoSec> NekoOperate<'s, S> { 76 + #[inline(always)] 77 + const fn new(neko: &'s mut NekoState<S>, data: &'s [u8]) -> Self { 78 + Self { neko, data } 79 + } 81 80 81 + #[inline(always)] 82 + fn operate(&'s mut self, operation: fn((&mut u8, &u8))) { 83 + loop { 82 84 // Trans the neko 83 - let transed_bytes = self.neko.state[self.neko.position..].as_mut_bytes(); 85 + let transed_bytes = 86 + self.neko.state[self.neko.position.div_ceil(U64_CHUNK)..S::rate()].as_mut_bytes(); 87 + 88 + let take = transed_bytes 89 + .iter_mut() 90 + .zip(self.data) 91 + .map(operation) 92 + .count(); 84 93 85 - transed_bytes.iter_mut().zip(block).for_each(operation); 94 + self.data = &self.data[take..]; 86 95 87 96 if !self.data.is_empty() { 88 - self.neko.permutation_f(); 97 + self.neko.permutation_f(ops::CONT); 89 98 } else { 90 - self.neko.position += take.div_ceil(U64_CHUNK); 99 + self.neko.position += take; 91 100 break; 92 101 } 93 102 } 94 103 } 104 + 105 + #[inline] 106 + pub(crate) fn absorb(neko: &'s mut NekoState<S>, data: &'s [u8]) { 107 + NekoOperate::new(neko, data).operate(|(state, byte)| *state ^= *byte); 108 + } 109 + 110 + #[inline] 111 + pub(crate) fn overwrite(neko: &'s mut NekoState<S>, data: &'s [u8]) { 112 + NekoOperate::new(neko, data).operate(|(state, byte)| *state = *byte); 113 + } 95 114 }
+19 -3
wharrgarbl-neko/src/ops.rs
··· 2 2 3 3 use crate::flags::*; 4 4 5 + // Internal State machine flags. These only get used during internal ops, and are 6 + // not invoked directly via user ops. 7 + pub const RST: OpFlags = OpFlags::EMPTY; 8 + pub const CONT: OpFlags = OpFlags::new(TRANSPORT.bits()); 9 + 10 + // All User operations are defined below. All valid combinations must have either 11 + // two flags or three flags. 0, 1 or 4 flags toggled are INVALID for User operations 12 + // and are reserved for internal usage only. 13 + // All higher bits on OpFlags are also reserved, but for v0.2 Spec, are ALWAYS invalid 14 + // and should NEVER be used. If these are added, it will be for a new spec revision. 15 + 16 + // Message related ops, either for encrypting, MAC, or ingesting cleartext 5 17 pub const CLR: OpFlags = OpFlags::new(APP.bits() | TRANSPORT.bits()); 6 18 pub const MAC: OpFlags = OpFlags::new(CIPHER.bits() | TRANSPORT.bits()); 7 19 pub const ENC: OpFlags = OpFlags::new(APP.bits() | CIPHER.bits() | TRANSPORT.bits()); 8 20 9 - pub const KEY: OpFlags = OpFlags::new(META.bits()); 21 + // Cipher related ops, either for init/loading keys/nonces/associated data, or for 22 + // cryptographic operations like ratchet for forward secrecy, or prf for expanding 23 + // new keys/nonces/other randomised data. 24 + pub const INIT: OpFlags = OpFlags::new(META.bits() | APP.bits()); 10 25 pub const NONCE: OpFlags = OpFlags::new(META.bits() | TRANSPORT.bits()); 11 - pub const AD: OpFlags = OpFlags::new(META.bits() | APP.bits()); 12 - pub const RATCHET: OpFlags = OpFlags::new(META.bits() | CIPHER.bits()); 26 + pub const AD: OpFlags = OpFlags::new(META.bits() | APP.bits() | TRANSPORT.bits()); 27 + pub const KEY: OpFlags = OpFlags::new(META.bits() | CIPHER.bits()); 28 + pub const RATCHET: OpFlags = OpFlags::new(META.bits() | TRANSPORT.bits() | CIPHER.bits()); 13 29 pub const PRF: OpFlags = OpFlags::new(META.bits() | APP.bits() | CIPHER.bits());
+4 -4
wharrgarbl-neko/src/traits.rs
··· 1 - use aead::consts::{U128, U256}; 1 + use aead::consts::{U128, U200, U256}; 2 2 use hybrid_array::typenum::Unsigned; 3 3 4 4 pub trait NekoSec: Unsigned { 5 5 fn to_bytes() -> [u8; 2] { 6 - Self::to_u16().to_le_bytes() 6 + Self::U16.to_le_bytes() 7 7 } 8 8 9 9 fn ratchet_bytes() -> usize { 10 - Self::to_usize().wrapping_shr(6) 10 + Self::USIZE.wrapping_shr(6) 11 11 } 12 12 13 13 fn rate() -> usize { 14 - keccak::PLEN - Self::ratchet_bytes() - 1 14 + (U200::USIZE - Self::USIZE / 4) / 8 - 1 15 15 } 16 16 } 17 17
+1
wharrgarbl-utils/src/opflags.rs
··· 9 9 impl OpFlags { 10 10 pub const EMPTY: OpFlags = OpFlags(0); 11 11 12 + #[inline(always)] 12 13 pub const fn new(val: u8) -> Self { 13 14 Self(val) 14 15 }

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
NEKO v2 spec, improvements
merge conflicts detected
expand
  • src/lib.rs:10
  • src/transport.rs:1
  • wharrgarbl-neko/Cargo.toml:14
  • wharrgarbl-neko/README.md:2
  • wharrgarbl-neko/src/kats.rs:13
  • wharrgarbl-neko/src/lib.rs:12
  • wharrgarbl-neko/src/operators.rs:1
  • wharrgarbl-neko/src/ops.rs:2
  • wharrgarbl-neko/src/traits.rs:1
  • wharrgarbl-utils/src/opflags.rs:9
expand 0 comments
1 commit
expand
NEKO v2 spec, improvements
expand 0 comments