A whimsical STROBE based encryption protocol
0
fork

Configure Feed

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

at main 322 lines 8.8 kB view raw
1use core::marker::PhantomData; 2 3use aead::{AeadInOut, Buffer, Key, KeySizeUser, TagPosition, common::IvSizeUser}; 4use ctutils::{CtEq, CtSelect}; 5use hybrid_array::AssocArraySize; 6use wharrgarbl_neko::{NekoSec, NekoState, NekoTag}; 7 8use crate::{Role, WHARRGHARBL_PROTO}; 9 10pub struct AeadNeko<S: NekoSec> { 11 pub(crate) key: Key<Self>, 12 param: PhantomData<S>, 13} 14 15impl<S: NekoSec> aead::KeySizeUser for AeadNeko<S> { 16 type KeySize = <NekoState<S> as KeySizeUser>::KeySize; 17} 18 19impl<S: NekoSec> IvSizeUser for AeadNeko<S> { 20 type IvSize = <NekoState<S> as IvSizeUser>::IvSize; 21} 22 23impl<S: NekoSec> aead::AeadCore for AeadNeko<S> { 24 type NonceSize = <AeadNeko<S> as IvSizeUser>::IvSize; 25 type TagSize = <NekoTag as AssocArraySize>::Size; 26 const TAG_POSITION: TagPosition = TagPosition::Postfix; 27} 28 29impl<S: NekoSec> aead::KeyInit for AeadNeko<S> { 30 fn new(key: &Key<Self>) -> Self { 31 Self { 32 key: *key, 33 param: PhantomData, 34 } 35 } 36} 37 38impl<S: NekoSec> aead::AeadInOut for AeadNeko<S> { 39 fn encrypt_inout_detached( 40 &self, 41 nonce: &aead::Nonce<Self>, 42 associated_data: &[u8], 43 mut buffer: aead::inout::InOutBuf<'_, '_, u8>, 44 ) -> aead::Result<aead::Tag<Self>> { 45 let mut neko = NekoState::<S>::new(WHARRGHARBL_PROTO.as_bytes()); 46 47 neko.key(&self.key); 48 neko.nonce(nonce); 49 neko.ad(associated_data); 50 neko.encrypt(buffer.get_out()); 51 52 Ok(neko.create_mac()) 53 } 54 55 fn decrypt_inout_detached( 56 &self, 57 nonce: &aead::Nonce<Self>, 58 associated_data: &[u8], 59 mut buffer: aead::inout::InOutBuf<'_, '_, u8>, 60 tag: &aead::Tag<Self>, 61 ) -> aead::Result<()> { 62 let mut neko = NekoState::<S>::new(WHARRGHARBL_PROTO.as_bytes()); 63 64 neko.key(&self.key); 65 neko.nonce(nonce); 66 neko.ad(associated_data); 67 neko.decrypt(buffer.get_out()); 68 69 neko.verify_mac(tag) 70 } 71} 72 73impl<S: NekoSec> zeroize::Zeroize for AeadNeko<S> { 74 fn zeroize(&mut self) { 75 self.key.zeroize(); 76 } 77} 78 79pub struct AeadTransport<S: NekoSec> { 80 pub(crate) aead: AeadNeko<S>, 81 pub(crate) epstein: aead::Nonce<AeadNeko<S>>, 82 pub(crate) trump: aead::Nonce<AeadNeko<S>>, 83 handshake_role: Role, 84} 85 86impl<S: NekoSec> zeroize::Zeroize for AeadTransport<S> { 87 fn zeroize(&mut self) { 88 self.aead.zeroize(); 89 self.epstein.zeroize(); 90 self.trump.zeroize(); 91 } 92} 93 94impl<S: NekoSec> zeroize::ZeroizeOnDrop for AeadTransport<S> {} 95 96impl<S: NekoSec> Drop for AeadTransport<S> { 97 fn drop(&mut self) { 98 zeroize::Zeroize::zeroize(self); 99 } 100} 101 102impl<S: NekoSec> AeadTransport<S> { 103 pub(crate) fn new( 104 key: Key<AeadNeko<S>>, 105 outbound: aead::Nonce<AeadNeko<S>>, 106 inbound: aead::Nonce<AeadNeko<S>>, 107 role: Role, 108 ) -> Self { 109 assert_ne!( 110 &inbound, &outbound, 111 "The Base Nonces MUST NOT equal to each other" 112 ); 113 114 Self { 115 aead: AeadNeko { 116 key, 117 param: PhantomData, 118 }, 119 epstein: outbound, 120 trump: inbound, 121 handshake_role: role, 122 } 123 } 124 125 fn select_nonce(&self, sending_role: Role) -> aead::Nonce<AeadNeko<S>> { 126 let role_context = self.handshake_role ^ sending_role; 127 128 self.epstein.ct_select(&self.trump, role_context.ct_eq(&1)) 129 } 130 131 fn mix_nonce(&self, position: [u8; 8], sending_role: Role) -> aead::Nonce<AeadNeko<S>> { 132 let mut nonce = self.select_nonce(sending_role); 133 134 let mid = nonce.len() - position.len(); 135 136 nonce[mid..] 137 .iter_mut() 138 .zip(position) 139 .for_each(|(n, p)| *n ^= p); 140 141 nonce 142 } 143 144 pub fn split(&self) -> (SendState<'_, S>, RecvState<'_, S>) { 145 ( 146 SendState { 147 transport: self, 148 counter: 0, 149 }, 150 RecvState { 151 transport: self, 152 counter: 0, 153 }, 154 ) 155 } 156} 157 158pub struct SendState<'a, S: NekoSec> { 159 transport: &'a AeadTransport<S>, 160 counter: u64, 161} 162 163impl<S: NekoSec> SendState<'_, S> { 164 pub fn encrypt(&mut self, buffer: &mut dyn Buffer, ad: &[u8]) -> aead::Result<()> { 165 match self.counter.checked_add(1) { 166 Some(inc) => self.counter = inc, 167 None => return Err(aead::Error), 168 } 169 170 self.transport.aead.encrypt_in_place( 171 &self 172 .transport 173 .mix_nonce(self.counter.to_be_bytes(), Role::Sender), 174 ad, 175 buffer, 176 ) 177 } 178} 179 180pub struct RecvState<'a, S: NekoSec> { 181 transport: &'a AeadTransport<S>, 182 counter: u64, 183} 184 185impl<S: NekoSec> RecvState<'_, S> { 186 pub fn decrypt(&mut self, buffer: &mut dyn Buffer, ad: &[u8]) -> aead::Result<()> { 187 match self.counter.checked_add(1) { 188 Some(inc) => self.counter = inc, 189 None => return Err(aead::Error), 190 } 191 192 // If the message's MAC doesn't evaluate successfully, this op will fail. 193 self.transport.aead.decrypt_in_place( 194 &self 195 .transport 196 .mix_nonce(self.counter.to_be_bytes(), Role::Receiver), 197 ad, 198 buffer, 199 ) 200 } 201} 202 203#[cfg(test)] 204mod tests { 205 use wharrgarbl_neko::{Neko128, Neko256}; 206 207 use super::*; 208 209 #[test] 210 fn two_way_transport_sync_works() -> aead::Result<()> { 211 let shared_secret = [ 212 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 213 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 214 0x9c, 0x9d, 0x9e, 0x9f, 215 ]; 216 217 let outbound = 123u128.to_ne_bytes(); 218 let inbound = 234u128.to_ne_bytes(); 219 220 let alice = AeadTransport::<Neko128>::new( 221 shared_secret.into(), 222 outbound.into(), 223 inbound.into(), 224 Role::Sender, 225 ); 226 let bob = AeadTransport::<Neko128>::new( 227 shared_secret.into(), 228 outbound.into(), 229 inbound.into(), 230 Role::Receiver, 231 ); 232 233 let (mut alice_send, mut alice_recv) = alice.split(); 234 let (mut bob_send, mut bob_recv) = bob.split(); 235 236 let orig = b"Test Message, Please ignore. AAAAAAAAAAAAAAAAAAaaaaaaaaaaa | B ."; 237 238 let ad = b"random"; 239 240 let mut msg = orig.to_vec(); 241 242 // a -> b 243 alice_send.encrypt(&mut msg, ad)?; 244 245 assert_ne!(orig.as_slice(), msg.as_slice()); 246 let ct1 = msg.clone(); 247 248 bob_recv.decrypt(&mut msg, ad)?; 249 250 // a -> b 251 alice_send.encrypt(&mut msg, b"")?; 252 253 assert_ne!(msg.as_slice(), ct1.as_slice()); 254 let ct2 = msg.clone(); 255 256 bob_recv.decrypt(&mut msg, b"")?; 257 258 // b -> a 259 bob_send.encrypt(&mut msg, ad)?; 260 261 // None of the ciphertexts should match each other 262 assert_ne!(msg.as_slice(), ct1.as_slice()); 263 assert_ne!(msg.as_slice(), ct2.as_slice()); 264 assert_ne!(ct1.as_slice(), ct2.as_slice()); 265 266 alice_recv.decrypt(&mut msg, ad)?; 267 268 assert_eq!(orig.as_slice(), msg.as_slice()); 269 270 // Counters are tracked from sender to receiver 271 assert_eq!(alice_send.counter, bob_recv.counter); 272 assert_eq!(bob_send.counter, alice_recv.counter); 273 274 // Counters are not linked on the same side 275 assert_ne!(alice_send.counter, alice_recv.counter); 276 assert_ne!(bob_send.counter, bob_recv.counter); 277 278 Ok(()) 279 } 280 281 #[test] 282 #[should_panic] 283 fn two_way_transport_fails_with_security_level_mismatch() { 284 let shared_secret = [ 285 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 286 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 287 0x9c, 0x9d, 0x9e, 0x9f, 288 ]; 289 290 let outbound = 123u128.to_ne_bytes(); 291 let inbound = 234u128.to_ne_bytes(); 292 293 let alice = AeadTransport::<Neko128>::new( 294 shared_secret.into(), 295 outbound.into(), 296 inbound.into(), 297 Role::Sender, 298 ); 299 let bob = AeadTransport::<Neko256>::new( 300 shared_secret.into(), 301 outbound.into(), 302 inbound.into(), 303 Role::Receiver, 304 ); 305 306 let (mut alice_send, mut _alice_recv) = alice.split(); 307 let (mut _bob_send, mut bob_recv) = bob.split(); 308 309 let orig = b"Test Message, Please ignore."; 310 311 let ad = b"random"; 312 313 let mut msg = orig.to_vec(); 314 315 // a -> b 316 alice_send.encrypt(&mut msg, ad).unwrap(); 317 318 assert_ne!(orig.as_slice(), msg.as_slice()); 319 320 bob_recv.decrypt(&mut msg, ad).unwrap(); 321 } 322}