+459
-247
Diff
round #1
+1
-1
src/lib.rs
+1
-1
src/lib.rs
+7
-3
src/transport.rs
+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
-3
wharrgarbl-neko/Cargo.toml
+2
-2
wharrgarbl-neko/README.md
+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
+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
+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
+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
+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
+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
+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
History
2 rounds
0 comments
1 commit
expand
collapse
NEKO v2 spec, improvements
merge conflicts detected
expand
collapse
expand
collapse
- 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