+24
Cargo.lock
+24
Cargo.lock
···
408
408
]
409
409
410
410
[[package]]
411
+
name = "dhkem"
412
+
version = "0.1.0-rc.0"
413
+
source = "git+https://github.com/RustCrypto/KEMs?rev=2d277162e0c5ed1c53bb315d0c0dace394cba70a#2d277162e0c5ed1c53bb315d0c0dace394cba70a"
414
+
dependencies = [
415
+
"elliptic-curve",
416
+
"hkdf",
417
+
"k256",
418
+
"kem",
419
+
"rand_core",
420
+
"zeroize",
421
+
]
422
+
423
+
[[package]]
411
424
name = "digest"
412
425
version = "0.10.7"
413
426
source = "registry+https://github.com/rust-lang/crates.io-index"
···
934
947
]
935
948
936
949
[[package]]
950
+
name = "kem"
951
+
version = "0.3.0-rc.6"
952
+
source = "registry+https://github.com/rust-lang/crates.io-index"
953
+
checksum = "e3ae2c3347ff4a7af4f679a9e397c2c7e6034a00b773dd2dd3c001d7f40897c9"
954
+
dependencies = [
955
+
"crypto-common 0.2.1",
956
+
"rand_core",
957
+
]
958
+
959
+
[[package]]
937
960
name = "leb128fmt"
938
961
version = "0.1.0"
939
962
···
1528
1551
version = "0.1.0"
1529
1552
dependencies = [
1530
1553
"chacha20poly1305",
1554
+
"dhkem",
1531
1555
"k256",
1532
1556
"sha2 0.11.0-rc.5",
1533
1557
]
+3
Cargo.toml
+3
Cargo.toml
+1
sachy-crypto/Cargo.toml
+1
sachy-crypto/Cargo.toml
···
11
11
chacha20poly1305 = { version = "=0.11.0-rc.3", default-features = false, features = ["getrandom", "alloc"] }
12
12
k256 = { version = "=0.14.0-rc.8", default-features = false, features = ["ecdh", "getrandom"] }
13
13
sha2 = { version = "=0.11.0-rc.5", default-features = false, features = [] }
14
+
dhkem = { version = "0.1.0-rc.0", features = ["getrandom", "k256"] }
+55
-104
sachy-crypto/src/lib.rs
+55
-104
sachy-crypto/src/lib.rs
···
11
11
},
12
12
consts::U8,
13
13
};
14
-
use k256::{
15
-
PublicKey, Sec1Point,
16
-
ecdh::{EphemeralSecret, SharedSecret},
17
-
elliptic_curve::{Generate, subtle::ConstantTimeEq},
14
+
use dhkem::{
15
+
Encapsulate, Kem, Secp256k1DecapsulationKey, Secp256k1EncapsulationKey, Secp256k1Kem,
16
+
TryDecapsulate,
17
+
kem::{Ciphertext, SharedKey, TryKeyInit},
18
18
};
19
+
use k256::{ecdh::SharedSecret, elliptic_curve::subtle::ConstantTimeEq};
19
20
20
21
extern crate alloc;
21
22
22
-
struct HandshakeTurn(bool);
23
-
24
-
impl HandshakeTurn {
25
-
fn next_turn(&mut self) {
26
-
self.0 = !self.0;
27
-
}
28
-
29
-
fn is_mine(&self) -> bool {
30
-
self.0
31
-
}
32
-
}
33
-
34
-
pub struct Handshake {
35
-
turn: HandshakeTurn,
36
-
ephemeral: EphemeralSecret,
37
-
shared: Option<SharedSecret>,
38
-
sent_public: bool,
39
-
}
40
-
41
23
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42
-
pub enum HandshakeError {
43
-
InvalidTurn,
44
-
InvalidSec1Bytes,
45
-
BufferTooSmall,
46
-
UnfinishedHandshake,
47
-
}
24
+
pub struct HandshakeError;
48
25
49
-
impl Handshake {
50
-
pub fn client() -> Self {
51
-
Self {
52
-
turn: HandshakeTurn(true),
53
-
ephemeral: EphemeralSecret::generate(),
54
-
shared: None,
55
-
sent_public: false,
56
-
}
57
-
}
26
+
pub struct ClientHandshake(Secp256k1DecapsulationKey);
58
27
59
-
pub fn server() -> Self {
60
-
Self {
61
-
turn: HandshakeTurn(false),
62
-
ephemeral: EphemeralSecret::generate(),
63
-
shared: None,
64
-
sent_public: false,
65
-
}
66
-
}
67
-
68
-
pub fn send<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a [u8], HandshakeError> {
69
-
if !self.turn.is_mine() {
70
-
return Err(HandshakeError::InvalidTurn);
71
-
}
72
-
73
-
let pub_key = self.ephemeral.public_key();
28
+
impl ClientHandshake {
29
+
pub fn send() -> (Secp256k1EncapsulationKey, Self) {
30
+
let (decap, encap) = Secp256k1Kem::generate_keypair();
74
31
75
-
let sec = Sec1Point::from(pub_key);
76
-
77
-
let len = sec.len();
78
-
79
-
if buf.len() < len {
80
-
return Err(HandshakeError::BufferTooSmall);
81
-
}
82
-
83
-
buf[..len].copy_from_slice(sec.as_bytes());
32
+
(encap, Self(decap))
33
+
}
84
34
85
-
self.turn.next_turn();
86
-
self.sent_public = true;
35
+
pub fn finish(
36
+
self,
37
+
ciphertext: &[u8],
38
+
psk: &[u8; 32],
39
+
) -> Result<TransportState, HandshakeError> {
40
+
let shared = self
41
+
.0
42
+
.try_decapsulate_slice(ciphertext)
43
+
.map_err(|_| HandshakeError)?;
87
44
88
-
Ok(&buf[..len])
45
+
TransportState::init(psk, shared)
89
46
}
47
+
}
90
48
91
-
pub fn receive(&mut self, buf: &[u8]) -> Result<(), HandshakeError> {
92
-
let public_key =
93
-
PublicKey::from_sec1_bytes(buf).map_err(|_| HandshakeError::InvalidSec1Bytes)?;
49
+
pub struct ServerHandshake(SharedKey<Secp256k1Kem>);
94
50
95
-
self.turn.next_turn();
51
+
impl ServerHandshake {
52
+
pub fn receive(buf: &[u8]) -> Result<(Ciphertext<Secp256k1Kem>, Self), HandshakeError> {
53
+
let encap = Secp256k1EncapsulationKey::new_from_slice(buf).map_err(|_| HandshakeError)?;
96
54
97
-
self.shared = Some(self.ephemeral.diffie_hellman(&public_key));
55
+
let (ciphertext, sk) = encap.encapsulate();
98
56
99
-
Ok(())
57
+
Ok((ciphertext, Self(sk)))
100
58
}
101
59
102
-
pub fn finish(self) -> Result<SharedSecret, HandshakeError> {
103
-
if !self.sent_public {
104
-
return Err(HandshakeError::UnfinishedHandshake);
105
-
}
106
-
107
-
if let Some(shared) = self.shared {
108
-
Ok(shared)
109
-
} else {
110
-
Err(HandshakeError::UnfinishedHandshake)
111
-
}
60
+
pub fn finish(self, psk: &[u8; 32]) -> Result<TransportState, HandshakeError> {
61
+
TransportState::init(psk, self.0)
112
62
}
113
63
}
114
64
···
235
185
result
236
186
}
237
187
238
-
pub fn init(psk: &[u8; 32], shared: SharedSecret) -> Result<Self, AeadError> {
239
-
let kdf = shared.extract::<sha2::Sha256>(Some(psk));
188
+
pub fn init(psk: &[u8; 32], shared: impl Into<SharedSecret>) -> Result<Self, HandshakeError> {
189
+
let kdf = shared.into().extract::<sha2::Sha256>(Some(psk));
240
190
241
191
let mut key = [0u8; 32];
242
192
243
193
kdf.expand(b"sachy-crypto", &mut key)
244
-
.map_err(|_| AeadError)?;
194
+
.map_err(|_| HandshakeError)?;
245
195
246
196
let mut epstein = Nonce::<ChaCha20Poly1305, Self>::default();
247
197
248
-
kdf.expand(b"noncer", &mut epstein).map_err(|_| AeadError)?;
198
+
kdf.expand(b"noncer", &mut epstein)
199
+
.map_err(|_| HandshakeError)?;
249
200
250
201
Ok(Self {
251
202
aead: ChaCha20Poly1305::new(&key.into()),
···
255
206
}
256
207
257
208
pub fn encrypt(&mut self, msg: &mut alloc::vec::Vec<u8>) -> Result<(), AeadError> {
258
-
self.counter += Self::COUNTER_INCR;
259
-
260
209
let counter_data = self.counter.to_be_bytes();
261
210
262
211
self.encrypt_in_place(self.counter, &counter_data, msg)?;
263
212
213
+
self.counter += Self::COUNTER_INCR;
214
+
264
215
if self.counter.ct_eq(&Self::COUNTER_MAX).into() {
265
216
Err(AeadError)
266
217
} else {
···
269
220
}
270
221
271
222
pub fn decrypt(&mut self, msg: &mut alloc::vec::Vec<u8>) -> Result<(), AeadError> {
272
-
self.counter += Self::COUNTER_INCR;
273
-
274
223
let counter_data = self.counter.to_be_bytes();
275
224
276
225
self.decrypt_in_place(self.counter, &counter_data, msg)?;
277
226
227
+
self.counter += Self::COUNTER_INCR;
228
+
278
229
if self.counter.ct_eq(&Self::COUNTER_MAX).into() {
279
230
Err(AeadError)
280
231
} else {
···
285
236
286
237
#[cfg(test)]
287
238
mod tests {
239
+
use dhkem::kem::KeyExport;
240
+
288
241
use super::*;
289
242
290
243
#[test]
291
244
fn handshake_protocol_works() -> Result<(), HandshakeError> {
292
-
let mut client = Handshake::client();
293
-
let mut server = Handshake::server();
294
-
295
-
let mut client_buf = alloc::vec![0u8; 2048];
296
-
let mut server_buf = alloc::vec![0u8; 2048];
245
+
let psk: [u8; 32] = [
246
+
31, 48, 29, 177, 88, 236, 186, 84, 65, 51, 214, 243, 174, 24, 45, 101, 229, 129, 62,
247
+
132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251,
248
+
];
297
249
298
-
let sent = client.send(&mut client_buf)?;
299
-
server.receive(sent)?;
250
+
let (ek, client) = ClientHandshake::send();
300
251
301
-
let sent = server.send(&mut server_buf)?;
302
-
client.receive(sent)?;
252
+
let (ciphertext, server) = ServerHandshake::receive(&ek.to_bytes())?;
303
253
304
-
let client_shared = client.finish()?;
305
-
let server_shared = server.finish()?;
254
+
let client_transport = client.finish(&ciphertext, &psk)?;
255
+
let server_transport = server.finish(&psk)?;
306
256
257
+
// If the nonces match, then we can assume the rest of the internal state is correct
307
258
assert_eq!(
308
-
client_shared.raw_secret_bytes(),
309
-
server_shared.raw_secret_bytes()
259
+
client_transport.epstein.as_slice(),
260
+
server_transport.epstein.as_slice()
310
261
);
311
262
312
263
Ok(())
···
325
276
132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251,
326
277
];
327
278
328
-
let mut alice = TransportState::init(&psk, Array(shared_secret).into())?;
329
-
let mut bob = TransportState::init(&psk, Array(shared_secret).into())?;
279
+
let mut alice = TransportState::init(&psk, Array(shared_secret)).map_err(|_| AeadError)?;
280
+
let mut bob = TransportState::init(&psk, Array(shared_secret)).map_err(|_| AeadError)?;
330
281
331
282
let orig = b"Test Message, Please ignore.".to_vec();
332
283
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