Repo of no-std crates for my personal embedded projects
0
fork

Configure Feed

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

Clean up crypto code with better naming & some comments

+37 -75
+37 -75
sachy-crypto/src/lib.rs
··· 2 2 3 3 use core::ops::{AddAssign, BitXor}; 4 4 5 - use chacha20poly1305::{ 6 - AeadInOut, ChaCha20Poly1305, KeyInit, 7 - aead::{self, Buffer}, 8 - }; 5 + use chacha20poly1305::{AeadInOut, ChaCha20Poly1305, KeyInit, aead}; 9 6 use dhkem::{ 10 7 Encapsulate, Kem, Secp256k1DecapsulationKey, Secp256k1EncapsulationKey, Secp256k1Kem, 11 8 TryDecapsulate, ··· 41 38 42 39 pub struct EncapsulatedPublicKey(Secp256k1EncapsulationKey); 43 40 41 + /// The role of the participant, whether sending/receiving during handshake, 42 + /// and then whether sending/receiving during communication. 44 43 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 45 44 pub enum Role { 46 - Client, 47 - Server, 45 + /// Client SENDS data 46 + Sender, 47 + /// Server RECEIVES data 48 + Receiver, 48 49 } 49 50 50 51 impl From<Role> for u8 { 51 52 fn from(value: Role) -> Self { 52 53 match value { 53 - Role::Client => 0, 54 - Role::Server => 1, 54 + Role::Sender => 0, 55 + Role::Receiver => 1, 55 56 } 56 57 } 57 58 } ··· 93 94 .try_decapsulate_slice(ciphertext) 94 95 .map_err(|_| ProtoError)?; 95 96 96 - TransportState::init(psk, shared, Role::Client) 97 + TransportState::init(psk, shared, Role::Sender) 97 98 } 98 99 } 99 100 ··· 109 110 } 110 111 111 112 pub fn finish(self, psk: &[u8; 32]) -> Result<TransportState, ProtoError> { 112 - TransportState::init(psk, self.0, Role::Server) 113 + TransportState::init(psk, self.0, Role::Receiver) 113 114 } 114 115 } 115 116 ··· 117 118 /// 118 119 /// This trait provides a particular "flavor" of transport, as there are 119 120 /// different ways the specifics of the construction can be implemented. 120 - pub trait TransportPrimitive<A> 121 + trait TransportPrimitive<A> 121 122 where 122 123 A: AeadInOut, 123 124 { ··· 129 130 130 131 /// Maximum number of messages allowed to be sent via Transport 131 132 const COUNTER_MAX: Self::Counter; 132 - 133 - /// Encrypt an AEAD message in-place at the given position in the Transport. 134 - fn encrypt_in_place( 135 - &self, 136 - nonce: &aead::Nonce<A>, 137 - associated_data: &[u8], 138 - buffer: &mut dyn Buffer, 139 - ) -> Result<(), ProtoError>; 140 - 141 - /// Decrypt an AEAD message in-place at the given position in the Transport. 142 - fn decrypt_in_place( 143 - &self, 144 - nonce: &aead::Nonce<A>, 145 - associated_data: &[u8], 146 - buffer: &mut dyn Buffer, 147 - ) -> Result<(), ProtoError>; 148 133 } 149 134 150 135 pub struct SendingState<'a> { ··· 160 145 ) -> Result<(), ProtoError> { 161 146 let counter = self.counter.to_be_bytes(); 162 147 163 - self.transport.encrypt_in_place( 164 - &self.transport.mix_nonce(&counter, Role::Client), 148 + self.transport.aead.encrypt_in_place( 149 + &self.transport.mix_nonce(&counter, Role::Sender), 165 150 associated_data, 166 151 msg, 167 152 )?; 168 153 169 154 self.counter = self.counter.wrapping_add(TransportState::COUNTER_INCR); 170 155 171 - // If we wrapped around and equal the finish value, we have maxed out the amount of 172 - // messages we can send. 173 156 if self.counter.ct_eq(&TransportState::COUNTER_MAX).into() { 174 157 Err(ProtoError) 175 158 } else { ··· 191 174 ) -> Result<(), ProtoError> { 192 175 let counter = self.counter.to_be_bytes(); 193 176 194 - self.transport.decrypt_in_place( 195 - &self.transport.mix_nonce(&counter, Role::Server), 177 + self.transport.aead.decrypt_in_place( 178 + &self.transport.mix_nonce(&counter, Role::Receiver), 196 179 associated_data, 197 180 msg, 198 181 )?; 199 182 200 183 self.counter = self.counter.wrapping_add(TransportState::COUNTER_INCR); 201 184 202 - // If we wrapped around and equal the finish value, we have maxed out the amount of 203 - // messages we can send. 204 185 if self.counter.ct_eq(&TransportState::COUNTER_MAX).into() { 205 186 Err(ProtoError) 206 187 } else { ··· 215 196 const COUNTER_INCR: Self::Counter = 1; 216 197 217 198 const COUNTER_MAX: Self::Counter = u64::MAX; 218 - 219 - fn encrypt_in_place( 220 - &self, 221 - epstein: &aead::Nonce<ChaCha20Poly1305>, 222 - associated_data: &[u8], 223 - buffer: &mut dyn Buffer, 224 - ) -> Result<(), ProtoError> { 225 - self.aead 226 - .encrypt_in_place(epstein, associated_data, buffer)?; 227 - Ok(()) 228 - } 229 - 230 - fn decrypt_in_place( 231 - &self, 232 - epstein: &aead::Nonce<ChaCha20Poly1305>, 233 - associated_data: &[u8], 234 - buffer: &mut dyn Buffer, 235 - ) -> Result<(), ProtoError> { 236 - self.aead 237 - .decrypt_in_place(epstein, associated_data, buffer)?; 238 - Ok(()) 239 - } 240 199 } 241 200 242 201 #[repr(align(4))] ··· 290 249 ) 291 250 } 292 251 293 - fn mix_nonce(&self, position: &[u8; 8], send: Role) -> aead::Nonce<ChaCha20Poly1305> { 294 - let mut trump = aead::Nonce::<ChaCha20Poly1305>::default(); 295 - 252 + /// Selects which nonce to use for encrypting/decrypting, which matters for 253 + /// ensuring the same nonce is used only for one direction of communication. 254 + fn select_nonce_context(&self, send: Role) -> &aead::Nonce<ChaCha20Poly1305> { 296 255 let context_select = self.role ^ send; 297 256 298 - // Role switch allows toggling which nonce to use for encrypting/decrypting 299 - // Callee ROLE XOR Transport ROLE selects either one or other nonce context, 257 + // Handshake ROLE XOR Transport ROLE selects either one or other nonce context, 300 258 // (0) for first context, (1) for second context 301 - // Sending: Client ^ Client = 0 (select first) 302 - // Receiving: Server ^ Server = 0 (select first) 303 - // Sending: Server ^ Client = 1 (select second) 304 - // Receiving: Client ^ Server = 1 (select second) 305 - let epstein = if context_select.ct_eq(&0).into() { 259 + // Sending: Client ^ Sender = 0 (select first) 260 + // Receiving: Server ^ Receiver = 0 (select first) 261 + // Sending: Server ^ Sender = 1 (select second) 262 + // Receiving: Client ^ Receiver = 1 (select second) 263 + if context_select.ct_eq(&0).into() { 306 264 &self.first 307 265 } else { 308 266 &self.second 309 - }; 267 + } 268 + } 269 + 270 + fn mix_nonce(&self, position: &[u8; 8], send: Role) -> aead::Nonce<ChaCha20Poly1305> { 271 + let mut trump = aead::Nonce::<ChaCha20Poly1305>::default(); 272 + 273 + let epstein = self.select_nonce_context(send); 310 274 311 275 let (head, tail) = trump.split_at_mut(position.len()); 312 276 let (first, second) = epstein.split_at(position.len()); 313 277 314 - // XOR the base nonce with position bytes, copying them to the output nonce 278 + // XOR the base nonce with position bytes, copying them to the derived nonce 315 279 head.iter_mut() 316 280 .zip(first) 317 281 .zip(position) 318 282 .for_each(|((head, ep), pos)| *head = ep ^ pos); 319 283 320 - // Copy rest of base nonce into output nonce 321 - tail.iter_mut() 322 - .zip(second) 323 - .for_each(|(tail, ep)| *tail = *ep); 284 + // Copy rest of base nonce into derived nonce 285 + tail.copy_from_slice(second); 324 286 325 287 trump 326 288 } ··· 387 349 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, 388 350 ]; 389 351 390 - let alice = TransportState::init(&psk, Array(shared_secret), Role::Client)?; 391 - let bob = TransportState::init(&psk, Array(shared_secret), Role::Server)?; 352 + let alice = TransportState::init(&psk, Array(shared_secret), Role::Sender)?; 353 + let bob = TransportState::init(&psk, Array(shared_secret), Role::Receiver)?; 392 354 393 355 let (mut alice_send, mut alice_recv) = alice.split(); 394 356 let (mut bob_send, mut bob_recv) = bob.split();