Repo of no-std crates for my personal embedded projects

Sachy's crypto scheme lmao #13

merged opened by sachy.dev targeting main from sachy-crypto
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:usjm3ynnir6y4inkcdovrfei/sh.tangled.repo.pull/3mhklndgukc22
+83 -104
Interdiff #0 #1
+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
··· 27 27 embassy-sync = { version = "0.7" } 28 28 embassy-net = { version = "0.7" } 29 29 defmt = { version = "1" } 30 + 31 + [patch.crates-io] 32 + dhkem = { git = "https://github.com/RustCrypto/KEMs", rev = "2d277162e0c5ed1c53bb315d0c0dace394cba70a" }
+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
··· 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
sign up or login to add to the discussion
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
pull request successfully merged
1 commit
expand
Sachy's crypto scheme lmao
1/2 failed, 1/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
1/2 failed, 1/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 failed
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
sachy.dev submitted #1
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments
sachy.dev submitted #0
1 commit
expand
Sachy's crypto scheme lmao
2/2 success
expand
expand 0 comments