Cargo.lock
Cargo.lock
This file has not been changed.
Cargo.toml
Cargo.toml
This file has not been changed.
sachy-crypto/Cargo.toml
sachy-crypto/Cargo.toml
This file has not been changed.
+45
-51
sachy-crypto/src/lib.rs
+45
-51
sachy-crypto/src/lib.rs
···
1
1
#![no_std]
2
2
3
-
use core::ops::{AddAssign, Sub};
3
+
use core::ops::AddAssign;
4
4
5
5
use chacha20poly1305::{
6
6
AeadCore, AeadInOut, ChaCha20Poly1305, KeyInit,
7
-
aead::{
8
-
self, Buffer,
9
-
array::{Array, ArraySize},
10
-
common::array::typenum::Unsigned,
11
-
},
12
-
consts::U8,
7
+
aead::{self, Buffer, array::Array, common::array::typenum::Unsigned},
13
8
};
14
9
use dhkem::{
15
10
Encapsulate, Generate, Kem, Secp256k1DecapsulationKey, Secp256k1EncapsulationKey, Secp256k1Kem,
···
95
90
}
96
91
}
97
92
98
-
/// Nonce as used by a given AEAD construction and STREAM primitive.
99
-
pub type Nonce<A, S> = chacha20poly1305::aead::array::Array<u8, NonceSize<A, S>>;
100
-
101
-
/// Size of a nonce as used by a STREAM construction, sans the overhead of
102
-
/// the STREAM protocol itself.
103
-
pub type NonceSize<A, S> =
104
-
<<A as AeadCore>::NonceSize as Sub<<S as TransportPrimitive<A>>::NonceOverhead>>::Output;
105
-
106
-
/// Low-level STREAM implementation.
93
+
/// Low-level Transport implementation.
107
94
///
108
-
/// This trait provides a particular "flavor" of STREAM, as there are
95
+
/// This trait provides a particular "flavor" of transport, as there are
109
96
/// different ways the specifics of the construction can be implemented.
110
-
///
111
-
/// Deliberately immutable and stateless to permit parallel operation.
112
97
pub trait TransportPrimitive<A>
113
98
where
114
99
A: AeadInOut,
115
-
A::NonceSize: Sub<Self::NonceOverhead>,
116
-
NonceSize<A, Self>: ArraySize,
117
100
{
118
-
/// Number of bytes this STREAM primitive requires from the nonce.
119
-
type NonceOverhead: ArraySize;
120
-
121
-
/// Type used as the STREAM counter.
101
+
/// Type used as the Trasnport counter.
122
102
type Counter: AddAssign + Copy + Default + Eq;
123
103
124
-
/// Value to use when incrementing the STREAM counter (i.e. one)
104
+
/// Value to use when incrementing the Transport counter (i.e. one)
125
105
const COUNTER_INCR: Self::Counter;
126
106
127
-
/// Maximum value of the STREAM counter.
128
-
const COUNTER_MAX: Self::Counter;
129
-
130
-
/// Encrypt an AEAD message in-place at the given position in the STREAM.
107
+
/// Encrypt an AEAD message in-place at the given position in the Transport.
131
108
fn encrypt_in_place(
132
109
&self,
133
110
nonce: &aead::Nonce<A>,
···
135
112
buffer: &mut dyn Buffer,
136
113
) -> Result<(), ProtoError>;
137
114
138
-
/// Decrypt an AEAD message in-place at the given position in the STREAM.
115
+
/// Decrypt an AEAD message in-place at the given position in the Transport.
139
116
fn decrypt_in_place(
140
117
&self,
141
118
nonce: &aead::Nonce<A>,
···
144
121
) -> Result<(), ProtoError>;
145
122
}
146
123
147
-
pub struct TransportState {
148
-
aead: ChaCha20Poly1305,
149
-
}
150
-
151
124
pub struct SendingState<'a> {
152
125
transport: &'a TransportState,
153
126
counter: u64,
···
157
130
pub fn encrypt(&mut self, msg: &mut alloc::vec::Vec<u8>) -> Result<(), ProtoError> {
158
131
let counter = self.counter.to_be_bytes();
159
132
133
+
// Nonce is randomised to act as a OTP when mixed with the counter state
160
134
let mut epstein = Array::generate();
161
135
162
136
self.transport.encrypt_in_place(&epstein, &counter, msg)?;
163
137
164
-
msg.extend_from_slice(Self::mix_nonce(&mut epstein, &counter));
138
+
msg.extend(Self::mix_nonce(&mut epstein, &counter));
165
139
166
-
self.counter += TransportState::COUNTER_INCR;
140
+
self.counter = self.counter.wrapping_add(TransportState::COUNTER_INCR);
167
141
168
-
if self.counter.ct_eq(&TransportState::COUNTER_MAX).into() {
142
+
// If we wrapped around and equal the finish value, we have maxed out the amount of
143
+
// messages we can send.
144
+
if self.counter.ct_eq(&self.transport.finish).into() {
169
145
Err(ProtoError)
170
146
} else {
171
147
Ok(())
···
173
149
}
174
150
175
151
fn mix_nonce<'a>(
176
-
nonce: &'a mut aead::Nonce<ChaCha20Poly1305>,
152
+
epstein: &'a mut aead::Nonce<ChaCha20Poly1305>,
177
153
position: &'a [u8; 8],
178
154
) -> &'a aead::Nonce<ChaCha20Poly1305> {
179
-
nonce[..position.len()]
155
+
epstein[..position.len()]
180
156
.iter_mut()
181
157
.zip(position)
182
158
.for_each(|(byte, count)| *byte ^= *count);
183
159
184
-
nonce
160
+
epstein
185
161
}
186
162
}
187
163
···
199
175
200
176
self.transport.decrypt_in_place(&epstein, &counter, msg)?;
201
177
202
-
self.counter += TransportState::COUNTER_INCR;
178
+
self.counter = self.counter.wrapping_add(TransportState::COUNTER_INCR);
203
179
204
-
if self.counter.ct_eq(&TransportState::COUNTER_MAX).into() {
180
+
// If we wrapped around and equal the finish value, we have maxed out the amount of
181
+
// messages we can send.
182
+
if self.counter.ct_eq(&self.transport.finish).into() {
205
183
Err(ProtoError)
206
184
} else {
207
185
Ok(())
···
233
211
}
234
212
235
213
impl TransportPrimitive<ChaCha20Poly1305> for TransportState {
236
-
type NonceOverhead = U8;
237
-
238
214
type Counter = u64;
239
215
240
216
const COUNTER_INCR: Self::Counter = 1;
241
217
242
-
const COUNTER_MAX: Self::Counter = u64::MAX;
243
-
244
218
fn encrypt_in_place(
245
219
&self,
246
220
epstein: &aead::Nonce<ChaCha20Poly1305>,
···
264
238
}
265
239
}
266
240
241
+
pub struct TransportState {
242
+
aead: ChaCha20Poly1305,
243
+
finish: u64,
244
+
}
245
+
267
246
impl TransportState {
268
247
pub fn init(psk: &[u8; 32], shared: impl Into<SharedSecret>) -> Result<Self, ProtoError> {
269
248
let noncer = shared.into();
···
271
250
272
251
let mut key = [0u8; 32];
273
252
274
-
kdf.expand(b"sachy-crypto", &mut key)
253
+
let mut finish = [0u8; 8];
254
+
255
+
kdf.expand(b"SachY-Crypt0", &mut key)
256
+
.map_err(|_| ProtoError)?;
257
+
258
+
kdf.expand(b"PickANumber", &mut finish)
275
259
.map_err(|_| ProtoError)?;
276
260
277
261
Ok(Self {
278
262
aead: ChaCha20Poly1305::new(&key.into()),
263
+
finish: u64::from_le_bytes(finish),
279
264
})
280
265
}
281
266
282
267
pub fn split(&self) -> (SendingState<'_>, ReceivingState<'_>) {
268
+
let counter = self.finish;
269
+
283
270
(
284
271
SendingState {
285
272
transport: self,
286
-
counter: 0,
273
+
counter,
287
274
},
288
275
ReceivingState {
289
276
transport: self,
290
-
counter: 0,
277
+
counter,
291
278
},
292
279
)
293
280
}
···
321
308
let mut buffer2 = vec![0u8; 64];
322
309
323
310
// Using the same nonce to check that the internal states match. Normally, client/server
324
-
// would work with different nonces, because nonce reuse is BAD
311
+
// would work with randomised nonces, because nonce reuse is BAD
325
312
client_transport
326
313
.aead
327
314
.encrypt_in_place(&nonce, &[], &mut buffer1)?;
···
355
342
let (mut alice_send, mut alice_recv) = alice.split();
356
343
let (mut bob_send, mut bob_recv) = bob.split();
357
344
345
+
// Have to be synchronised on both ends, so the counter state matches between the two
346
+
// and thus messages can be encrypted/decrypted statefully. But the actual number is
347
+
// "random", making it harder to guess the position state.
348
+
assert_eq!(alice.finish, bob.finish);
349
+
358
350
let orig = b"Test Message, Please ignore.".to_vec();
359
351
360
352
let mut msg = orig.clone();
···
388
380
assert_eq!(orig.as_slice(), msg.as_slice());
389
381
assert_eq!(alice_send.counter, bob_recv.counter);
390
382
assert_eq!(bob_send.counter, alice_recv.counter);
383
+
assert_ne!(alice_send.counter, alice_recv.counter);
384
+
assert_ne!(bob_send.counter, bob_recv.counter);
391
385
392
386
Ok(())
393
387
}
History
18 rounds
0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
pull request successfully merged
1 commit
expand
collapse
Sachy's crypto scheme lmao
1/2 failed, 1/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
1/2 failed, 1/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 failed
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao
2/2 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
Sachy's crypto scheme lmao