A whimsical STROBE based encryption protocol
2
fork

Configure Feed

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

Refactor to constant-time opflags

authored by

Sachymetsu and committed by
Tangled
d169a418 e38ad4b5

+333 -154
-21
Cargo.lock
··· 18 18 ] 19 19 20 20 [[package]] 21 - name = "enumflags2" 22 - version = "0.7.12" 23 - source = "registry+https://github.com/rust-lang/crates.io-index" 24 - checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" 25 - dependencies = [ 26 - "enumflags2_derive", 27 - ] 28 - 29 - [[package]] 30 - name = "enumflags2_derive" 31 - version = "0.7.12" 32 - source = "registry+https://github.com/rust-lang/crates.io-index" 33 - checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" 34 - dependencies = [ 35 - "proc-macro2", 36 - "quote", 37 - "syn", 38 - ] 39 - 40 - [[package]] 41 21 name = "hex" 42 22 version = "0.4.3" 43 23 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 168 148 name = "wharrgarbl" 169 149 version = "0.1.0" 170 150 dependencies = [ 171 - "enumflags2", 172 151 "hex", 173 152 "keccak", 174 153 "serde",
-1
Cargo.toml
··· 8 8 9 9 [dependencies] 10 10 keccak = "0.2" 11 - enumflags2 = "0.7.12" 12 11 subtle = { version = "2.6", default-features = false } 13 12 14 13 [dev-dependencies]
+16 -12
src/basic_kats.rs
··· 6 6 7 7 use crate::{ 8 8 keccakf::KECCAK_BUFFER_SIZE, 9 - strobe::{SecurityParameter, StrobeState}, 9 + strobe::{Role, SecurityParameter, StrobeState}, 10 10 }; 11 11 12 12 extern crate std; 13 13 14 14 #[test] 15 15 fn test_init_128() { 16 - let s = StrobeState::new(b"", SecurityParameter::B128); 16 + let s = StrobeState::new(b"", SecurityParameter::B128, Role::Sender); 17 17 18 18 let expected_st: [u8; KECCAK_BUFFER_SIZE] = [ 19 19 0x9c, 0x7f, 0x16, 0x8f, 0xf8, 0xfd, 0x55, 0xda, 0x2a, 0xa7, 0x3c, 0x23, 0x55, 0x65, 0x35, ··· 37 37 38 38 #[test] 39 39 fn test_init_256() { 40 - let s = StrobeState::new(b"", SecurityParameter::B256); 40 + let s = StrobeState::new(b"", SecurityParameter::B256, Role::Sender); 41 41 42 42 let expected_st: [u8; KECCAK_BUFFER_SIZE] = [ 43 43 0x37, 0xc1, 0x15, 0x06, 0xed, 0x61, 0xe7, 0xda, 0x7c, 0x1a, 0x2f, 0x2c, 0x1f, 0x49, 0x74, ··· 62 62 #[test] 63 63 fn test_metadata() { 64 64 // We will accumulate output over 3 operations and 3 meta-operations 65 - let mut s = StrobeState::new(b"metadatatest", SecurityParameter::B256); 65 + let mut s = StrobeState::new(b"metadatatest", SecurityParameter::B256, Role::Sender); 66 66 let mut output = std::vec::Vec::new(); 67 67 68 68 let buf = b"meta1"; ··· 116 116 117 117 #[test] 118 118 fn test_seq() { 119 - let mut s = StrobeState::new(b"seqtest", SecurityParameter::B256); 119 + let mut s = StrobeState::new(b"seqtest", SecurityParameter::B256, Role::Sender); 120 120 121 121 let mut buf = [0u8; 10]; 122 122 s.prf(&mut buf[..]); ··· 172 172 #[test] 173 173 fn test_enc_correctness() { 174 174 let orig_msg = b"Hello there"; 175 - let mut tx = StrobeState::new(b"enccorrectnesstest", SecurityParameter::B256); 176 - let mut rx = StrobeState::new(b"enccorrectnesstest", SecurityParameter::B256); 175 + let mut tx = StrobeState::new(b"enccorrectnesstest", SecurityParameter::B256, Role::Sender); 176 + let mut rx = StrobeState::new( 177 + b"enccorrectnesstest", 178 + SecurityParameter::B256, 179 + Role::Receiver, 180 + ); 177 181 178 182 tx.key(b"the-combination-on-my-luggage"); 179 183 rx.key(b"the-combination-on-my-luggage"); ··· 188 192 189 193 #[test] 190 194 fn test_mac_correctness_and_soundness() { 191 - let mut tx = StrobeState::new(b"mactest", SecurityParameter::B256); 192 - let mut rx = StrobeState::new(b"mactest", SecurityParameter::B256); 195 + let mut tx = StrobeState::new(b"mactest", SecurityParameter::B256, Role::Sender); 196 + let mut rx = StrobeState::new(b"mactest", SecurityParameter::B256, Role::Receiver); 193 197 194 198 // Just do some stuff with the state 195 199 ··· 217 221 218 222 #[test] 219 223 fn test_long_inputs() { 220 - let mut s = StrobeState::new(b"bigtest", SecurityParameter::B256); 224 + let mut s = StrobeState::new(b"bigtest", SecurityParameter::B256, Role::Sender); 221 225 const BIG_N: usize = 9823; 222 226 const SMALL_N: usize = 65; 223 227 let big_data = [0x34u8; BIG_N]; ··· 274 278 fn test_streaming_correctness() { 275 279 // Compute a few things without breaking up their inputs 276 280 let one_shot_st: std::vec::Vec<u8> = { 277 - let mut s = StrobeState::new(b"streamingtest", SecurityParameter::B256); 281 + let mut s = StrobeState::new(b"streamingtest", SecurityParameter::B256, Role::Receiver); 278 282 279 283 s.ad(b"mynonce"); 280 284 ··· 290 294 }; 291 295 // Now do the same thing but stream the inputs 292 296 let streamed_st: std::vec::Vec<u8> = { 293 - let mut s = StrobeState::new(b"streamingtest", SecurityParameter::B256); 297 + let mut s = StrobeState::new(b"streamingtest", SecurityParameter::B256, Role::Receiver); 294 298 295 299 s.ad(b"my"); 296 300 s.ad(b"nonce");
+2 -2
src/herding_kats/harness.rs
··· 4 4 5 5 use serde::{Deserialize, Deserializer, de}; 6 6 7 - use crate::strobe::{SecurityParameter, StrobeState}; 7 + use crate::strobe::{Role, SecurityParameter, StrobeState}; 8 8 9 9 /// The harness we will put on our KATs so we can herd them and make them do tests. 10 10 /// (This is the top-level structure of the JSON we find in the test vectors) ··· 113 113 operations, 114 114 } = serde_json::from_reader(file).unwrap(); 115 115 116 - let mut strobe = StrobeState::new(proto_string.as_bytes(), security); 116 + let mut strobe = StrobeState::new(proto_string.as_bytes(), security, Role::Sender); 117 117 118 118 operations.into_iter().for_each( 119 119 |KatOperation {
+2
src/lib.rs
··· 6 6 #[cfg(test)] 7 7 mod herding_kats; 8 8 mod keccakf; 9 + mod opflags; 10 + mod ops; 9 11 pub mod strobe; 10 12 11 13 /// Version of Strobe that this crate implements.
+188
src/opflags.rs
··· 1 + use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; 2 + 3 + use subtle::{Choice, ConstantTimeEq}; 4 + 5 + use crate::strobe::Role; 6 + 7 + #[derive(Clone, Copy, PartialEq, Eq, Hash)] 8 + pub struct OpFlags(u8); 9 + 10 + impl OpFlags { 11 + pub const EMPTY: OpFlags = OpFlags(0); 12 + pub const INBOUND: OpFlags = OpFlags(1 << 0); 13 + pub const APP: OpFlags = OpFlags(1 << 1); 14 + pub const CIPHER: OpFlags = OpFlags(1 << 2); 15 + pub const TRANSPORT: OpFlags = OpFlags(1 << 3); 16 + pub const META: OpFlags = OpFlags(1 << 4); 17 + pub const KEYTREE: OpFlags = OpFlags(1 << 5); 18 + 19 + pub(crate) const fn new(val: u8) -> Self { 20 + Self(val) 21 + } 22 + 23 + pub fn contains(&self, other: OpFlags) -> Choice { 24 + (*self & other).ct_eq(&other) 25 + } 26 + 27 + pub fn intersects(&self, other: OpFlags) -> Choice { 28 + (*self & other).ct_ne(&OpFlags::EMPTY) 29 + } 30 + 31 + pub fn set(&mut self, flags: OpFlags, cond: Choice) { 32 + if cond.into() { 33 + *self |= flags; 34 + } else { 35 + *self &= !flags; 36 + } 37 + } 38 + 39 + #[inline(always)] 40 + pub const fn bits(&self) -> u8 { 41 + self.0 42 + } 43 + } 44 + 45 + impl core::fmt::Debug for OpFlags { 46 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 47 + write!(f, "OpFlags({:#08b})", self.0) 48 + } 49 + } 50 + 51 + impl ConstantTimeEq for OpFlags { 52 + fn ct_eq(&self, other: &Self) -> subtle::Choice { 53 + self.0.ct_eq(&other.0) 54 + } 55 + } 56 + 57 + impl BitAnd for OpFlags { 58 + type Output = OpFlags; 59 + 60 + fn bitand(self, rhs: Self) -> Self::Output { 61 + Self(self.0 & rhs.0) 62 + } 63 + } 64 + 65 + impl BitAndAssign for OpFlags { 66 + fn bitand_assign(&mut self, rhs: Self) { 67 + self.0 &= rhs.0; 68 + } 69 + } 70 + 71 + impl BitOr for OpFlags { 72 + type Output = OpFlags; 73 + 74 + fn bitor(self, rhs: Self) -> Self::Output { 75 + Self(self.0 | rhs.0) 76 + } 77 + } 78 + 79 + impl BitOrAssign for OpFlags { 80 + fn bitor_assign(&mut self, rhs: Self) { 81 + self.0 |= rhs.0; 82 + } 83 + } 84 + 85 + impl BitXor for OpFlags { 86 + type Output = Self; 87 + 88 + fn bitxor(self, rhs: Self) -> Self::Output { 89 + Self(self.0 ^ rhs.0) 90 + } 91 + } 92 + 93 + impl BitXorAssign for OpFlags { 94 + fn bitxor_assign(&mut self, rhs: Self) { 95 + self.0 ^= rhs.0; 96 + } 97 + } 98 + 99 + impl BitXorAssign<Role> for OpFlags { 100 + fn bitxor_assign(&mut self, rhs: Role) { 101 + self.0 ^= rhs as u8; 102 + } 103 + } 104 + 105 + // This is for a specific case to toggle INBOUND 106 + impl BitXorAssign<Choice> for OpFlags { 107 + fn bitxor_assign(&mut self, rhs: Choice) { 108 + self.0 ^= rhs.unwrap_u8(); 109 + } 110 + } 111 + 112 + impl Not for OpFlags { 113 + type Output = OpFlags; 114 + 115 + fn not(self) -> Self::Output { 116 + Self(!self.0) 117 + } 118 + } 119 + 120 + #[cfg(test)] 121 + mod flag_tests { 122 + use super::*; 123 + 124 + extern crate std; 125 + 126 + #[test] 127 + fn debug_shows_toggled_bits() { 128 + let test_case = OpFlags::INBOUND | OpFlags::APP | OpFlags::META; 129 + 130 + let debug = std::format!("{test_case:?}"); 131 + 132 + assert_eq!(&debug, "OpFlags(0b010011)"); 133 + } 134 + 135 + #[test] 136 + fn contains_works() { 137 + assert!(!bool::from(OpFlags::EMPTY.contains(OpFlags::INBOUND))); 138 + assert!(bool::from(OpFlags(0b110).contains(OpFlags::CIPHER))); 139 + assert!(bool::from(OpFlags(0b110).contains(OpFlags::APP))); 140 + assert!(bool::from( 141 + OpFlags(0b110).contains(OpFlags::CIPHER | OpFlags::APP) 142 + )); 143 + assert!(!bool::from( 144 + OpFlags(0b100).contains(OpFlags::CIPHER | OpFlags::APP) 145 + )); 146 + assert!(!bool::from(OpFlags(0b110).contains(OpFlags::INBOUND))); 147 + } 148 + 149 + #[test] 150 + fn intersects_works() { 151 + let intersection = OpFlags::CIPHER | OpFlags::KEYTREE; 152 + 153 + let test_1 = OpFlags::CIPHER | OpFlags::TRANSPORT; 154 + let test_2 = OpFlags::KEYTREE | OpFlags::META; 155 + let test_3 = OpFlags::APP | OpFlags::TRANSPORT; 156 + 157 + assert!(bool::from(test_1.intersects(intersection))); 158 + assert_eq!( 159 + bool::from(test_1.intersects(intersection)), 160 + bool::from(test_1.contains(OpFlags::KEYTREE)) 161 + || bool::from(test_1.contains(OpFlags::CIPHER)) 162 + ); 163 + 164 + assert!(bool::from(test_2.intersects(intersection))); 165 + 166 + assert!(!bool::from(test_3.intersects(intersection))); 167 + assert_eq!( 168 + bool::from(test_3.intersects(intersection)), 169 + bool::from(test_3.contains(OpFlags::KEYTREE)) 170 + || bool::from(test_3.contains(OpFlags::CIPHER)) 171 + ); 172 + } 173 + 174 + #[test] 175 + fn set_works() { 176 + let mut test_case = OpFlags::INBOUND | OpFlags::APP | OpFlags::META; 177 + 178 + test_case.set(OpFlags::META, Choice::from(0)); 179 + 180 + assert!(!bool::from(test_case.contains(OpFlags::META))); 181 + 182 + assert_eq!(test_case, OpFlags::INBOUND | OpFlags::APP); 183 + 184 + test_case.set(OpFlags::META, Choice::from(1)); 185 + 186 + assert!(bool::from(test_case.contains(OpFlags::META))); 187 + } 188 + }
+42
src/ops.rs
··· 1 + use crate::opflags::OpFlags; 2 + 3 + macro_rules! define_ops { 4 + { 5 + $( 6 + $name:ident($flags:expr); 7 + )+ 8 + } => { 9 + $( 10 + pub(crate) const $name: OpFlags = OpFlags::new($flags); 11 + )+ 12 + }; 13 + } 14 + 15 + define_ops! { 16 + SEND_ENC(OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits()); 17 + META_SEND_ENC(OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits() | OpFlags::META.bits()); 18 + RECV_ENC(OpFlags::INBOUND.bits() | OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits()); 19 + META_RECV_ENC(OpFlags::INBOUND.bits() | OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits() | OpFlags::META.bits()); 20 + 21 + SEND_MAC(OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits()); 22 + META_SEND_MAC(OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits() | OpFlags::META.bits()); 23 + RECV_MAC(OpFlags::INBOUND.bits() | OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits()); 24 + META_RECV_MAC(OpFlags::INBOUND.bits() | OpFlags::CIPHER.bits() | OpFlags::TRANSPORT.bits() | OpFlags::META.bits()); 25 + 26 + PRF(OpFlags::INBOUND.bits() | OpFlags::APP.bits() | OpFlags::CIPHER.bits()); 27 + META_PRF(OpFlags::INBOUND.bits() | OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::META.bits()); 28 + 29 + RATCHET(OpFlags::CIPHER.bits()); 30 + META_RATCHET(OpFlags::CIPHER.bits() | OpFlags::META.bits()); 31 + 32 + AD(OpFlags::APP.bits()); 33 + META_AD(OpFlags::APP.bits() | OpFlags::META.bits()); 34 + 35 + KEY(OpFlags::APP.bits() | OpFlags::CIPHER.bits()); 36 + META_KEY(OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::META.bits()); 37 + 38 + SEND_CLR(OpFlags::APP.bits() | OpFlags::TRANSPORT.bits()); 39 + META_SEND_CLR(OpFlags::APP.bits() | OpFlags::TRANSPORT.bits() | OpFlags::META.bits()); 40 + RECV_CLR(OpFlags::INBOUND.bits() | OpFlags::APP.bits() | OpFlags::TRANSPORT.bits()); 41 + META_RECV_CLR(OpFlags::INBOUND.bits() | OpFlags::APP.bits() | OpFlags::TRANSPORT.bits() | OpFlags::META.bits()); 42 + }
+83 -118
src/strobe.rs
··· 1 - use enumflags2::{BitFlag, BitFlags}; 2 - use subtle::ConstantTimeEq; 1 + use subtle::{Choice, ConstantTimeEq}; 3 2 4 3 use crate::{ 5 4 GarbledError, STROBE_VERSION, 6 5 keccakf::{KECCAK_BUFFER_SIZE, KeccakF1600}, 6 + opflags::OpFlags, 7 + ops, 7 8 }; 8 9 9 - #[enumflags2::bitflags] 10 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 10 11 #[repr(u8)] 11 - #[derive(Copy, Clone, Debug, PartialEq)] 12 - enum OpFlags { 13 - /// Is data being moved inbound 14 - Inbound = 0b000001, // 1<<0 15 - /// Is data being sent to the application 16 - App = 0b000010, // 1<<1 17 - /// Does this operation use cipher output 18 - Cipher = 0b000100, // 1<<2 19 - /// Is data being sent for transport 20 - Transport = 0b001000, // 1<<3 21 - /// Use exclusively for metadata operations 22 - Meta = 0b010000, // 1<<4 23 - /// Reserved and currently unimplemented. Using this will cause a panic. 24 - KeyTree = 0b100000, // 1<<5 12 + pub enum Role { 13 + Sender, 14 + Receiver, 25 15 } 26 16 27 - #[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 - enum Role { 29 - Sender, 30 - Receiver, 17 + impl ConstantTimeEq for Role { 18 + fn ct_eq(&self, other: &Self) -> Choice { 19 + (*self as u8).ct_eq(&(*other as u8)) 20 + } 31 21 } 32 22 33 23 #[derive(Debug, Clone, Copy)] ··· 49 39 position: usize, 50 40 /// Index into `state` 51 41 start: usize, 52 - /// Represents whether we're a sender or a receiver or uninitialized 53 - role: Option<Role>, 42 + /// Represents whether we're a sender or a receiver 43 + role: Role, 54 44 /// The last operation performed. This is to verify that the `more` flag is only used across 55 45 /// identical operations. 56 - prev_flags: BitFlags<OpFlags>, 46 + prev_flags: OpFlags, 57 47 } 58 48 59 49 macro_rules! define_mut_operations { ··· 64 54 #[$doc] 65 55 pub fn $name(&mut self, data: &mut [u8]) { 66 56 let flags = $flags; 67 - let prev_flags = self.prev_flags.bits(); 68 - let more = prev_flags.ct_eq(&flags.bits()); 69 - self.operate(flags, data, bool::from(more)); 57 + let prev_flags = self.prev_flags; 58 + let more = prev_flags.ct_eq(&flags); 59 + self.operate(flags, data, more); 70 60 } 71 61 )* 72 62 }; ··· 80 70 #[$doc] 81 71 pub fn $name(&mut self, data: &[u8]) { 82 72 let flags = $flags; 83 - let prev_flags = self.prev_flags.bits(); 84 - let more = prev_flags.ct_eq(&flags.bits()); 85 - self.operate_no_mutate(flags, data, bool::from(more)); 73 + let prev_flags = self.prev_flags; 74 + let more = prev_flags.ct_eq(&flags); 75 + self.operate_no_mutate(flags, data, more); 86 76 } 87 77 )* 88 78 }; ··· 112 102 113 103 impl StrobeState { 114 104 /// Makes a new `StrobeTransport` object with a given protocol byte string and security parameter. 115 - pub fn new(protocol: &[u8], sec: SecurityParameter) -> Self { 105 + pub fn new(protocol: &[u8], sec: SecurityParameter, role: Role) -> Self { 116 106 let rate = KECCAK_BUFFER_SIZE - (sec as usize) / 4 - 2; 117 107 assert!((1..254).contains(&rate)); 118 108 ··· 132 122 rate, 133 123 position: 0, 134 124 start: 0, 135 - role: None, 136 - prev_flags: OpFlags::empty(), 125 + role, 126 + prev_flags: OpFlags::EMPTY, 137 127 }; 138 128 139 129 // Mix the protocol into the state ··· 154 144 pub fn reset_ops(&mut self) { 155 145 // This prevents streaming so to always make the prev_flags == flags 156 146 // comparison always fail 157 - self.prev_flags = OpFlags::empty(); 147 + self.prev_flags = OpFlags::EMPTY; 158 148 } 159 149 160 150 // Runs the permutation function on the internal state ··· 263 253 264 254 /// Mixes the current state index and flags into the state, accounting for whether we are 265 255 /// sending or receiving 266 - fn begin_op(&mut self, mut flags: BitFlags<OpFlags>) { 267 - if flags.contains(OpFlags::Transport) { 268 - let op_role = if flags.contains(OpFlags::Inbound) { 256 + fn begin_op(&mut self, mut flags: OpFlags) { 257 + if flags.contains(OpFlags::TRANSPORT).into() { 258 + let op_role = if flags.contains(OpFlags::INBOUND).into() { 269 259 Role::Receiver 270 260 } else { 271 261 Role::Sender 272 262 }; 273 - 274 - // If uninitialized, take on the direction of the first directional operation we get 275 - if self.role.is_none() { 276 - self.role = Some(op_role); 277 - } 278 263 279 264 // So that the sender and receiver agree, toggle the I flag as necessary 280 - // This is equivalent to flags ^= is_receiver 281 - flags.set(OpFlags::Inbound, self.role.unwrap() != op_role); 265 + flags.set(OpFlags::INBOUND, self.role.ct_ne(&op_role)); 282 266 } 283 267 284 268 let old_start = self.start; ··· 287 271 // Mix in the position and flags 288 272 self.absorb(&[old_start as u8, flags.bits()]); 289 273 290 - let force_permutation = flags.contains(OpFlags::Cipher) || flags.contains(OpFlags::KeyTree); 291 - if force_permutation && self.position != 0 { 274 + let mut force_permutation = flags.intersects(OpFlags::CIPHER | OpFlags::KEYTREE); 275 + force_permutation &= self.position.ct_ne(&0); 276 + 277 + if force_permutation.into() { 292 278 self.permutation_f(); 293 279 } 294 280 } ··· 296 282 /// Performs the state / data transformation that corresponds to the given flags. If `more` is 297 283 /// given, this will treat `data` as a continuation of the data given in the previous 298 284 /// call to `operate`. 299 - fn operate(&mut self, flags: BitFlags<OpFlags>, data: &mut [u8], more: bool) { 285 + fn operate(&mut self, flags: OpFlags, data: &mut [u8], more: Choice) { 300 286 self.prev_flags = flags; 301 287 302 288 // If `more` isn't set, this is a new operation. Do the begin_op sequence 303 - if !more { 289 + if !bool::from(more) { 304 290 self.begin_op(flags); 305 291 } 306 292 307 293 // Meta-ness is only relevant for `begin_op`. Remove it to simplify the below logic. 308 - let flags = flags & !OpFlags::Meta; 294 + let flags = flags & !OpFlags::META; 295 + 296 + // Flags that don't pass this assertion should normally call `absorb`, but `absorb` does not mutate, 297 + // so the implementor should have used operate_no_mutate instead 298 + // RATCHET is special-cased to never call operate directly 299 + debug_assert!(flags != ops::KEY && bool::from(flags.contains(OpFlags::CIPHER))); 309 300 310 - // TODO?: Assert that input is empty under some flag conditions 311 - if flags.contains(OpFlags::Cipher | OpFlags::Transport) && !flags.contains(OpFlags::Inbound) 312 - { 313 - // This is equivalent to the `duplex` operation in the Python implementation, with 314 - // `cafter = True` 315 - if flags == OpFlags::Cipher | OpFlags::Transport { 316 - // This is `send_mac`. Pretend the input is all zeros 317 - self.copy_state(data); 318 - } else { 319 - self.absorb_and_set(data); 320 - } 321 - } else if flags == OpFlags::Inbound | OpFlags::App | OpFlags::Cipher { 322 - // Special case of case below. This is PRF. Use `squeeze` instead of `exchange`. 323 - self.squeeze(data); 324 - } else if flags.contains(OpFlags::Cipher) { 325 - // This is equivalent to the `duplex` operation in the Python implementation, with 326 - // `cbefore = True` 327 - self.exchange(data); 328 - } else { 329 - // This should normally call `absorb`, but `absorb` does not mutate, so the implementor 330 - // should have used operate_no_mutate instead 331 - unreachable!("operate should not be called for operations that do not require mutation") 301 + match flags { 302 + ops::PRF => self.squeeze(data), 303 + ops::SEND_MAC => self.copy_state(data), 304 + ops::SEND_ENC => self.absorb_and_set(data), 305 + _ => self.exchange(data), 332 306 } 333 307 } 334 308 335 309 /// Performs the state transformation that corresponds to the given flags. If `more` is given, 336 310 /// this will treat `data` as a continuation of the data given in the previous call to 337 311 /// `operate`. This uses non-mutating variants of the specializations of the `duplex` function. 338 - fn operate_no_mutate(&mut self, flags: BitFlags<OpFlags>, data: &[u8], more: bool) { 312 + fn operate_no_mutate(&mut self, flags: OpFlags, data: &[u8], more: Choice) { 339 313 self.prev_flags = flags; 340 314 341 315 // If `more` isn't set, this is a new operation. Do the begin_op sequence 342 - if !more { 316 + if !bool::from(more) { 343 317 self.begin_op(flags); 344 318 } 345 319 320 + // Meta-ness is only relevant for `begin_op`. Remove it to simplify the below logic. 321 + let flags = flags & !OpFlags::META; 322 + 323 + // Flags that trigger the assertion to fail are mutating operations. 324 + // RATCHET is special cased to never call operate/operate_no_mutate directly 325 + debug_assert!( 326 + flags != ops::PRF && !bool::from(flags.contains(OpFlags::CIPHER | OpFlags::TRANSPORT)) 327 + || bool::from(flags.contains(OpFlags::INBOUND)) 328 + ); 329 + 346 330 // There are no non-mutating variants of things with flags & (C | T | I) == C | T 347 - if flags.contains(OpFlags::Cipher | OpFlags::Transport) && !flags.contains(OpFlags::Inbound) 348 - { 349 - unreachable!("operate_no_mutate called on something that requires mutation") 350 - } else if flags.contains(OpFlags::Cipher) { 331 + if flags.contains(OpFlags::CIPHER).into() { 351 332 // This is equivalent to a non-mutating form of the `duplex` operation in the Python 352 333 // implementation, with `cbefore = True` 353 334 self.overwrite(data); ··· 358 339 } 359 340 } 360 341 361 - fn recv_mac_inner( 362 - &mut self, 363 - mac_copy: &mut [u8], 364 - flags: BitFlags<OpFlags>, 365 - ) -> Result<(), GarbledError> { 342 + fn recv_mac_inner(&mut self, mac_copy: &mut [u8], flags: OpFlags) -> Result<(), GarbledError> { 366 343 // recv_mac can never be streamed 367 - self.operate(flags, mac_copy, false); 344 + self.operate(flags, mac_copy, Choice::from(0u8)); 368 345 369 346 // Constant-time MAC check. This accumulates the truth values of byte == 0 370 347 let all_zero: bool = mac_copy 371 348 .iter() 372 - .fold(subtle::Choice::from(1u8), |all_zero, b| { 373 - all_zero & 0u8.ct_eq(b) 374 - }) 349 + .fold(Choice::from(1u8), |all_zero, b| all_zero & 0u8.ct_eq(b)) 375 350 .into(); 376 351 377 352 if all_zero { Ok(()) } else { Err(GarbledError) } ··· 380 355 pub fn recv_mac<const N: usize>(&mut self, mac: &[u8; N]) -> Result<(), GarbledError> { 381 356 let mut mac_copy = *mac; 382 357 383 - self.recv_mac_inner( 384 - &mut mac_copy, 385 - OpFlags::Inbound | OpFlags::Cipher | OpFlags::Transport, 386 - ) 358 + self.recv_mac_inner(&mut mac_copy, ops::RECV_MAC) 387 359 } 388 360 389 361 pub fn meta_recv_mac<const N: usize>(&mut self, mac: &[u8; N]) -> Result<(), GarbledError> { 390 362 let mut mac_copy = *mac; 391 363 392 - self.recv_mac_inner( 393 - &mut mac_copy, 394 - OpFlags::Inbound | OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta, 395 - ) 364 + self.recv_mac_inner(&mut mac_copy, ops::META_RECV_MAC) 396 365 } 397 366 398 - fn ratchet_inner(&mut self, num_bytes_to_zero: usize, flags: BitFlags<OpFlags>) { 367 + fn ratchet_inner(&mut self, num_bytes_to_zero: usize, flags: OpFlags) { 399 368 let more = self.prev_flags.bits().ct_eq(&flags.bits()); 400 369 401 370 // We don't make an `operate` call, since this is a super special case. That means we have ··· 410 379 } 411 380 412 381 pub fn ratchet(&mut self, num_bytes_to_zero: usize) { 413 - let flags = BitFlags::from(OpFlags::Cipher); 414 - 415 - self.ratchet_inner(num_bytes_to_zero, flags); 382 + self.ratchet_inner(num_bytes_to_zero, ops::RATCHET); 416 383 } 417 384 418 385 pub fn meta_ratchet(&mut self, num_bytes_to_zero: usize) { 419 - let flags = OpFlags::Cipher | OpFlags::Meta; 420 - 421 - self.ratchet_inner(num_bytes_to_zero, flags); 386 + self.ratchet_inner(num_bytes_to_zero, ops::META_RATCHET); 422 387 } 423 388 424 389 define_mut_operations! { 425 390 /// SEND ENC 426 - pub fn send_enc(OpFlags::App | OpFlags::Cipher | OpFlags::Transport); 391 + pub fn send_enc(ops::SEND_ENC); 427 392 /// META SEND ENC 428 - pub fn meta_send_enc(OpFlags::App | OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta); 393 + pub fn meta_send_enc(ops::META_SEND_ENC); 429 394 /// RECV ENV 430 - pub fn recv_enc(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher | OpFlags::Transport); 395 + pub fn recv_enc(ops::RECV_ENC); 431 396 /// META RECV ENC 432 - pub fn meta_recv_enc(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta); 397 + pub fn meta_recv_enc(ops::META_RECV_ENC); 433 398 /// SEND MAC 434 - pub fn send_mac(OpFlags::Cipher | OpFlags::Transport); 399 + pub fn send_mac(ops::SEND_MAC); 435 400 /// META SEND MAC 436 - pub fn meta_send_mac(OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta); 401 + pub fn meta_send_mac(ops::META_SEND_MAC); 437 402 /// PRF 438 - pub fn prf(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher); 403 + pub fn prf(ops::PRF); 439 404 /// META PRF 440 - pub fn meta_prf(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher | OpFlags::Meta); 405 + pub fn meta_prf(ops::META_PRF); 441 406 } 442 407 443 408 define_non_mut_operations! { 444 409 /// AD 445 - pub fn ad(BitFlags::from(OpFlags::App)); 410 + pub fn ad(ops::AD); 446 411 /// META AD 447 - pub fn meta_ad(OpFlags::App | OpFlags::Meta); 412 + pub fn meta_ad(ops::META_AD); 448 413 /// KEY 449 - pub fn key(OpFlags::App | OpFlags::Cipher); 414 + pub fn key(ops::KEY); 450 415 /// META KEY 451 - pub fn meta_key(OpFlags::App | OpFlags::Cipher | OpFlags::Meta); 416 + pub fn meta_key(ops::META_KEY); 452 417 /// SEND CLR 453 - pub fn send_clr(OpFlags::App | OpFlags::Transport); 418 + pub fn send_clr(ops::SEND_CLR); 454 419 /// META SEND CLR 455 - pub fn meta_send_clr(OpFlags::App | OpFlags::Transport | OpFlags::Meta); 420 + pub fn meta_send_clr(ops::META_SEND_CLR); 456 421 /// RECV CLR 457 - pub fn recv_clr(OpFlags::Inbound | OpFlags::App | OpFlags::Transport); 422 + pub fn recv_clr(ops::RECV_CLR); 458 423 /// META RECV CLR 459 - pub fn meta_recv_clr(OpFlags::Inbound | OpFlags::App | OpFlags::Transport | OpFlags::Meta); 424 + pub fn meta_recv_clr(ops::META_RECV_CLR); 460 425 } 461 426 } 462 427 ··· 468 433 469 434 #[test] 470 435 fn version_formatting() { 471 - let s = StrobeState::new(b"", SecurityParameter::B128); 436 + let s = StrobeState::new(b"", SecurityParameter::B128, Role::Sender); 472 437 473 438 let display = std::format!("{s}"); 474 439 let debug = std::format!("{s:?}");