A whimsical STROBE based encryption protocol
2
fork

Configure Feed

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

Switch to ctutils, constant time more things

+76 -46
+16 -7
Cargo.lock
··· 9 9 checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 10 10 11 11 [[package]] 12 + name = "cmov" 13 + version = "0.5.3" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" 16 + 17 + [[package]] 12 18 name = "cpufeatures" 13 19 version = "0.3.0" 14 20 source = "registry+https://github.com/rust-lang/crates.io-index" 15 21 checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" 16 22 dependencies = [ 17 23 "libc", 24 + ] 25 + 26 + [[package]] 27 + name = "ctutils" 28 + version = "0.4.2" 29 + source = "registry+https://github.com/rust-lang/crates.io-index" 30 + checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" 31 + dependencies = [ 32 + "cmov", 18 33 ] 19 34 20 35 [[package]] ··· 122 137 ] 123 138 124 139 [[package]] 125 - name = "subtle" 126 - version = "2.6.1" 127 - source = "registry+https://github.com/rust-lang/crates.io-index" 128 - checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 129 - 130 - [[package]] 131 140 name = "syn" 132 141 version = "2.0.117" 133 142 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 148 157 name = "wharrgarbl" 149 158 version = "0.1.0" 150 159 dependencies = [ 160 + "ctutils", 151 161 "hex", 152 162 "keccak", 153 163 "serde", 154 164 "serde-big-array", 155 165 "serde_json", 156 - "subtle", 157 166 ] 158 167 159 168 [[package]]
+1 -1
Cargo.toml
··· 8 8 9 9 [dependencies] 10 10 keccak = "0.2" 11 - subtle = { version = "2.6", default-features = false } 11 + ctutils = { version = "0.4.2", default-features = false } 12 12 13 13 [dev-dependencies] 14 14 serde_json = "1"
+16 -4
src/opflags.rs
··· 1 1 use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; 2 2 3 - use subtle::{Choice, ConstantTimeEq}; 3 + use ctutils::{Choice, CtAssign, CtEq}; 4 4 5 5 use crate::strobe::Role; 6 6 ··· 43 43 } 44 44 } 45 45 46 + impl Default for OpFlags { 47 + fn default() -> Self { 48 + Self::EMPTY 49 + } 50 + } 51 + 46 52 impl core::fmt::Debug for OpFlags { 47 53 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 48 54 write!(f, "OpFlags({:#08b})", self.0) 49 55 } 50 56 } 51 57 52 - impl ConstantTimeEq for OpFlags { 53 - fn ct_eq(&self, other: &Self) -> subtle::Choice { 58 + impl CtEq for OpFlags { 59 + fn ct_eq(&self, other: &Self) -> Choice { 54 60 self.0.ct_eq(&other.0) 61 + } 62 + } 63 + 64 + impl CtAssign for OpFlags { 65 + fn ct_assign(&mut self, src: &Self, choice: Choice) { 66 + self.0.ct_assign(&src.0, choice); 55 67 } 56 68 } 57 69 ··· 106 118 // This is for a specific case to toggle INBOUND 107 119 impl BitXorAssign<Choice> for OpFlags { 108 120 fn bitxor_assign(&mut self, rhs: Choice) { 109 - self.0 ^= rhs.unwrap_u8(); 121 + self.0 ^= rhs.to_u8(); 110 122 } 111 123 } 112 124
+43 -34
src/strobe.rs
··· 1 - use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeLess}; 1 + use ctutils::{Choice, CtAssign, CtEq, CtLt, CtSelect}; 2 2 3 3 use crate::{ 4 4 GarbledError, STROBE_VERSION, ··· 14 14 Receiver, 15 15 } 16 16 17 - impl ConstantTimeEq for Role { 17 + impl CtEq for Role { 18 18 fn ct_eq(&self, other: &Self) -> Choice { 19 19 (*self as u8).ct_eq(&(*other as u8)) 20 20 } ··· 162 162 fn increment_position(&mut self, increment: usize) { 163 163 self.position += increment; 164 164 165 - if self.position.ct_eq(&self.rate).into() { 165 + if self.position.ct_eq(&self.rate).to_bool() { 166 166 self.permutation_f(); 167 167 } 168 168 } ··· 246 246 ); 247 247 248 248 // Do the zero-writing in chunks 249 - while bytes_to_zero.ct_ne(&0).into() { 249 + while bytes_to_zero > 0 { 250 250 let min_slice = (self.rate - self.position) as u32; 251 251 let to_zero = bytes_to_zero as u32; 252 252 253 - let slice_len = ConditionallySelectable::conditional_select( 254 - &min_slice, 255 - &to_zero, 256 - to_zero.ct_lt(&min_slice), 257 - ) as usize; 253 + let slice_len = min_slice.ct_select(&to_zero, to_zero.ct_lt(&min_slice)) as usize; 258 254 259 255 self.state.0[self.position..(self.position + slice_len)].fill(0); 260 256 ··· 267 263 /// Mixes the current state index and flags into the state, accounting for whether we are 268 264 /// sending or receiving 269 265 fn begin_op(&mut self, mut flags: OpFlags) { 270 - if flags.contains(OpFlags::TRANSPORT).into() { 271 - let op_role = if flags.contains(OpFlags::INBOUND).into() { 266 + if flags.contains(OpFlags::TRANSPORT).to_bool() { 267 + let op_role = if flags.contains(OpFlags::INBOUND).to_bool() { 272 268 Role::Receiver 273 269 } else { 274 270 Role::Sender ··· 287 283 let mut force_permutation = flags.intersects(OpFlags::CIPHER | OpFlags::KEYTREE); 288 284 force_permutation &= self.position.ct_ne(&0); 289 285 290 - if force_permutation.into() { 286 + if force_permutation.to_bool() { 291 287 self.permutation_f(); 292 288 } 293 289 } ··· 299 295 self.prev_flags = flags; 300 296 301 297 // If `more` isn't set, this is a new operation. Do the begin_op sequence 302 - if !bool::from(more) { 298 + if !more.to_bool() { 303 299 self.begin_op(flags); 304 300 } 305 301 ··· 311 307 // RATCHET is special-cased to never call operate directly 312 308 debug_assert!(flags != ops::KEY && bool::from(flags.contains(OpFlags::CIPHER))); 313 309 314 - match flags { 315 - ops::PRF => self.squeeze(data), 316 - ops::SEND_MAC => self.copy_state(data), 317 - ops::SEND_ENC => self.absorb_and_set(data), 318 - _ => self.exchange(data), 319 - } 310 + const SPECIAL_CASES: [OpFlags; 3] = [ops::PRF, ops::SEND_MAC, ops::SEND_ENC]; 311 + const OPS: [fn(&mut StrobeState, data: &mut [u8]); 4] = [ 312 + StrobeState::squeeze, 313 + StrobeState::copy_state, 314 + StrobeState::absorb_and_set, 315 + StrobeState::exchange, 316 + ]; 317 + 318 + // Constant time resolution of op index 319 + let index = SPECIAL_CASES 320 + .iter() 321 + .enumerate() 322 + .fold(3usize, |mut res, (index, op)| { 323 + res.ct_assign(&index, flags.ct_eq(op)); 324 + res 325 + }); 326 + 327 + OPS[index](self, data); 320 328 } 321 329 322 330 /// Performs the state transformation that corresponds to the given flags. If `more` is given, ··· 326 334 self.prev_flags = flags; 327 335 328 336 // If `more` isn't set, this is a new operation. Do the begin_op sequence 329 - if !bool::from(more) { 337 + if !more.to_bool() { 330 338 self.begin_op(flags); 331 339 } 332 340 ··· 337 345 // RATCHET is special cased to never call operate/operate_no_mutate directly 338 346 debug_assert!(flags == ops::KEY || !bool::from(flags.contains(OpFlags::CIPHER))); 339 347 340 - match flags { 341 - // This is equivalent to a non-mutating form of the `duplex` operation in the Python 342 - // implementation, with `cbefore = True` 343 - ops::KEY => self.overwrite(data), 344 - // This is equivalent to the `duplex` operation in the Python implementation, with 345 - // `cbefore = cafter = False` 346 - _ => self.absorb(data), 347 - } 348 + const OPS: [fn(&mut StrobeState, data: &[u8]); 2] = 349 + [StrobeState::absorb, StrobeState::overwrite]; 350 + 351 + let index = (flags.ct_eq(&ops::KEY).to_u8() & 1) as usize; 352 + 353 + OPS[index](self, data); 348 354 } 349 355 350 356 fn recv_mac_inner(&mut self, flags: OpFlags, mac_copy: &mut [u8]) -> Result<(), GarbledError> { 351 357 // recv_mac can never be streamed 352 - self.operate(flags, mac_copy, Choice::from(0u8)); 358 + self.operate(flags, mac_copy, Choice::FALSE); 353 359 354 360 // Constant-time MAC check. This accumulates the truth values of byte == 0 355 - let all_zero: bool = mac_copy 361 + let all_zero = mac_copy 356 362 .iter() 357 - .fold(Choice::from(1u8), |all_zero, b| all_zero & 0u8.ct_eq(b)) 358 - .into(); 363 + .fold(Choice::TRUE, |all_zero, b| all_zero & 0u8.ct_eq(b)); 359 364 360 - if all_zero { Ok(()) } else { Err(GarbledError) } 365 + if all_zero.to_bool() { 366 + Ok(()) 367 + } else { 368 + Err(GarbledError) 369 + } 361 370 } 362 371 363 372 pub fn recv_mac<const N: usize>(&mut self, mac: &[u8; N]) -> Result<(), GarbledError> { ··· 379 388 // to make the `begin_op` call manually. 380 389 self.prev_flags = flags; 381 390 382 - if !bool::from(more) { 391 + if !more.to_bool() { 383 392 self.begin_op(flags); 384 393 } 385 394