A whimsical STROBE based encryption protocol
2
fork

Configure Feed

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

Implement NEKO encryption protocol

authored by

Sachymetsu and committed by
Tangled
671f1df2 5cdfd287

+1140 -361
+16 -1
Cargo.lock
··· 931 931 "hybrid-array", 932 932 "ml-kem", 933 933 "rand_core", 934 - "wharrgarbl-strobe", 934 + "wharrgarbl-neko", 935 + "wharrgarbl-utils", 936 + "zeroize", 937 + ] 938 + 939 + [[package]] 940 + name = "wharrgarbl-neko" 941 + version = "0.1.0" 942 + dependencies = [ 943 + "aead", 944 + "ctutils", 945 + "hybrid-array", 946 + "keccak", 935 947 "wharrgarbl-utils", 948 + "zerocopy", 936 949 "zeroize", 937 950 ] 938 951 ··· 957 970 version = "0.1.0" 958 971 dependencies = [ 959 972 "aead", 973 + "ctutils", 974 + "zeroize", 960 975 ] 961 976 962 977 [[package]]
+2 -2
Cargo.toml
··· 1 1 [workspace] 2 2 resolver = "3" 3 - members = ["wharrgarbl-utils", "wharrgarbl-strobe"] 3 + members = ["wharrgarbl-utils", "wharrgarbl-strobe", "wharrgarbl-neko"] 4 4 5 5 [workspace.package] 6 6 authors = ["Sachy.dev <sachymetsu@tutamail.com>"] ··· 26 26 27 27 [dependencies] 28 28 wharrgarbl-utils = { path = "./wharrgarbl-utils", version = "0.1" } 29 - wharrgarbl-strobe = { path = "./wharrgarbl-strobe", version = "0.1" } 29 + wharrgarbl-neko = { path = "./wharrgarbl-neko", version = "0.1" } 30 30 ctutils.workspace = true 31 31 zeroize.workspace = true 32 32 ml-kem = { version = "0.3.0-rc.2", features = ["zeroize"] }
+5 -1
README.md
··· 1 1 # WHARRGARBL 2 2 3 - A WIP Strobe+AKE protocol with a whimsical name. Based off the [reference rust implementation here](https://github.com/rozbb/strobe-rs), though with diverging API choices for experimentation. 3 + A WIP Strobe inspired HPKE protocol with a whimsical name. Inspired by the [reference rust implementation here](https://github.com/rozbb/strobe-rs), though with diverging API + Spec choices for experimentation. 4 + 5 + ## NEKO Encryption 6 + 7 + NEKO is a modification of the STROBE spec, to remove redundancies in the spec and to simplify further its API surface. By doing so, it is possible to improve the throughput of the encryption and also tighten some of its APIs to be stronger against misuse. For more deets, look at the NEKO crate for further explanation. 4 8 5 9 ☢️ **WARNING: DO NOT USE IN PRODUCTION. THIS CRATE IS NOT AUDITED AND IS WIP, MAYBE EVEN NOT AS SECURE AS THOUGHT. AAAAAAAAAA** ☢️ 6 10
+8 -7
benches/garbl_bench.rs
··· 12 12 text.repeat(128).as_bytes().to_vec() 13 13 } 14 14 15 - fn wharrgarbl_benchmark(c: &mut Criterion) { 16 - use wharrgarbl::{Sec128, Sec256, transport::AeadStrobe}; 15 + fn wharrgarbl_neko_benchmark(c: &mut Criterion) { 16 + use wharrgarbl::transport::AeadNeko; 17 + use wharrgarbl_neko::{Neko128, Neko256}; 17 18 18 19 let key = Array([77u8; 32]); 19 20 let nonce = Array([12u8; 16]); 20 21 21 - let transport_128 = AeadStrobe::<Sec128>::new(&key); 22 - let transport_256 = AeadStrobe::<Sec256>::new(&key); 22 + let transport_128 = AeadNeko::<Neko128>::new(&key); 23 + let transport_256 = AeadNeko::<Neko256>::new(&key); 23 24 24 - c.bench_function("WHGL-128 TRANSPORT ROUNDTRIP", |b| { 25 + c.bench_function("NEKO-128 TRANSPORT ROUNDTRIP", |b| { 25 26 b.iter_batched_ref( 26 27 generate_text, 27 28 |text| { ··· 32 33 ); 33 34 }); 34 35 35 - c.bench_function("WHGL-256 TRANSPORT ROUNDTRIP", |b| { 36 + c.bench_function("NEKO-256 TRANSPORT ROUNDTRIP", |b| { 36 37 b.iter_batched_ref( 37 38 generate_text, 38 39 |text| { ··· 67 68 pub fn benches() { 68 69 let mut criterion: Criterion<_> = Criterion::default().configure_from_args(); 69 70 70 - wharrgarbl_benchmark(&mut criterion); 71 + wharrgarbl_neko_benchmark(&mut criterion); 71 72 chacha20poly1305_benchmark(&mut criterion); 72 73 } 73 74
+80 -76
src/handshake.rs
··· 1 1 use core::marker::PhantomData; 2 2 3 - use aead::Buffer; 3 + use aead::{ 4 + Buffer, 5 + consts::{U32, U64}, 6 + }; 4 7 use alloc::boxed::Box; 5 - use hybrid_array::{Array, typenum::Unsigned}; 6 - use ml_kem::{Encapsulate, Kem, KeyExport, ParameterSet, TryKeyInit, kem::Decapsulate}; 8 + use hybrid_array::{Array, SliceExt, typenum::Unsigned}; 9 + use ml_kem::{Encapsulate, Kem, KeyExport, ParameterSet, SharedKey, TryKeyInit, kem::Decapsulate}; 7 10 use rand_core::CryptoRng; 8 - use wharrgarbl_strobe::{StrobeRole, StrobeState, traits::StrobeSecurity}; 11 + use wharrgarbl_neko::{NekoSec, NekoState}; 12 + use zeroize::Zeroize; 9 13 10 14 use crate::{ 11 - WHARRGHARBL_PROTO, 12 - transport::{AeadState, AeadStrobe}, 15 + Role, WHARRGHARBL_PROTO, 16 + transport::{AeadNeko, AeadTranport}, 13 17 }; 14 18 15 - pub struct ClientHandshake<S: StrobeSecurity, K: Kem + ParameterSet> { 19 + pub struct ClientHandshake<S: NekoSec, K: Kem + ParameterSet> { 16 20 kem_sec: PhantomData<K>, 17 - strobe: StrobeState<S>, 21 + neko: NekoState<S>, 18 22 decap: Option<Box<K::DecapsulationKey>>, 19 23 } 20 24 21 - impl<S: StrobeSecurity, K: Kem + ParameterSet> ClientHandshake<S, K> 25 + impl<S: NekoSec, K: Kem<SharedKeySize = U32> + ParameterSet> ClientHandshake<S, K> 22 26 where 23 27 K::DecapsulationKey: Decapsulate, 24 28 { 25 29 pub fn new(psk: Option<&[u8; 32]>) -> Self { 26 - let mut strobe = StrobeState::<S>::new(WHARRGHARBL_PROTO.as_bytes(), StrobeRole::Sender); 30 + let mut neko = NekoState::<S>::new(WHARRGHARBL_PROTO.as_bytes()); 27 31 28 - if let Some(psk) = psk { 29 - strobe.key(psk); 32 + if let Some(psk) = psk.and_then(|psk| psk.as_hybrid_array()) { 33 + neko.key(psk); 30 34 } 31 35 32 - strobe.meta_ad(&K::K::to_u8().to_le_bytes()); 33 - strobe.meta_ad(&S::to_bytes()); 36 + neko.ad(&K::K::to_u8().to_le_bytes()); 37 + neko.ad(&S::to_bytes()); 38 + neko.ratchet(); 34 39 35 40 Self { 36 41 kem_sec: PhantomData, 37 - strobe, 42 + neko, 38 43 decap: None, 39 44 } 40 45 } 41 46 42 47 pub fn send(&mut self, rng: &mut impl CryptoRng, buf: &mut dyn Buffer) -> aead::Result<()> { 43 - let mut tag: aead::Tag<AeadStrobe<S>> = Default::default(); 44 48 let (decap, encap) = K::generate_keypair_from_rng(rng); 45 49 46 50 self.decap = Some(Box::new(decap)); 47 51 48 52 buf.extend_from_slice(&encap.to_bytes())?; 49 53 50 - self.strobe.send_clr(buf.as_ref()); 51 - self.strobe.send_mac(&mut tag); 52 - self.strobe.ratchet(S::ratchet_bytes()); 54 + self.neko.cleartext(buf.as_ref()); 53 55 54 - buf.extend_from_slice(&tag)?; 56 + buf.extend_from_slice(&self.neko.create_mac())?; 55 57 56 58 Ok(()) 57 59 } 58 60 59 - pub fn receive(&mut self, ciphertext: &[u8]) -> aead::Result<AeadState<S>> { 61 + pub fn receive(&mut self, ciphertext: &[u8]) -> aead::Result<AeadTranport<S>> { 60 62 let decap = self.decap.as_ref().ok_or(aead::Error)?; 61 63 62 64 let tag = ciphertext 63 65 .len() 64 - .checked_sub(<AeadStrobe<S> as aead::AeadCore>::TagSize::to_usize()) 66 + .checked_sub(<AeadNeko<S> as aead::AeadCore>::TagSize::to_usize()) 65 67 .ok_or(aead::Error)?; 66 68 67 69 let (ciphertext, tag) = ciphertext.split_at(tag); 68 70 69 - let tag: aead::Tag<AeadStrobe<S>> = tag.try_into().unwrap(); 71 + let tag: aead::Tag<AeadNeko<S>> = tag.try_into().unwrap(); 70 72 71 - self.strobe.recv_clr(ciphertext); 72 - self.strobe.recv_mac(&tag)?; 73 - self.strobe.ratchet(S::ratchet_bytes()); 73 + self.neko.cleartext(ciphertext); 74 + self.neko.verify_mac(&tag)?; 74 75 75 76 let shared = decap 76 77 .decapsulate_slice(ciphertext) ··· 79 80 Ok(self.finish(shared)) 80 81 } 81 82 82 - fn finish(&mut self, shared: Array<u8, K::SharedKeySize>) -> AeadState<S> { 83 - self.strobe.ad(&shared); 83 + fn finish(&mut self, mut shared: SharedKey) -> AeadTranport<S> { 84 + self.neko.key(&shared); 85 + shared.zeroize(); 86 + 87 + self.neko.ratchet(); 88 + 89 + let mut entropy: Array<u8, U64> = Default::default(); 84 90 85 - let mut key: aead::Key<AeadStrobe<S>> = Default::default(); 86 - let mut inbound: aead::Nonce<AeadStrobe<S>> = Default::default(); 87 - let mut outbound: aead::Nonce<AeadStrobe<S>> = Default::default(); 91 + self.neko.prf(&mut entropy); 92 + 93 + let (key, rest) = entropy.split(); 94 + let (outbound, inbound) = rest.split(); 88 95 89 - self.strobe.prf(&mut key); 90 - self.strobe.prf(&mut inbound); 91 - self.strobe.prf(&mut outbound); 96 + entropy.zeroize(); 92 97 93 - AeadState::new(key, outbound, inbound, StrobeRole::Sender) 98 + AeadTranport::new(key, outbound, inbound, Role::Sender) 94 99 } 95 100 } 96 101 97 - pub struct ServerHandshake<S: StrobeSecurity, K: Kem + ParameterSet> { 102 + pub struct ServerHandshake<S: NekoSec, K: Kem + ParameterSet> { 98 103 kem_sec: PhantomData<K>, 99 - strobe: StrobeState<S>, 104 + neko: NekoState<S>, 100 105 } 101 106 102 - impl<S: StrobeSecurity, K: Kem + ParameterSet> ServerHandshake<S, K> { 107 + impl<S: NekoSec, K: Kem<SharedKeySize = U32> + ParameterSet> ServerHandshake<S, K> { 103 108 pub fn new(psk: Option<&[u8; 32]>) -> Self { 104 - let mut strobe = StrobeState::<S>::new(WHARRGHARBL_PROTO.as_bytes(), StrobeRole::Receiver); 109 + let mut neko = NekoState::<S>::new(WHARRGHARBL_PROTO.as_bytes()); 105 110 106 - if let Some(psk) = psk { 107 - strobe.key(psk); 111 + if let Some(psk) = psk.and_then(|psk| psk.as_hybrid_array()) { 112 + neko.key(psk); 108 113 } 109 114 110 - strobe.meta_ad(&K::K::to_u8().to_le_bytes()); 111 - strobe.meta_ad(&S::to_bytes()); 115 + neko.ad(&K::K::to_u8().to_le_bytes()); 116 + neko.ad(&S::to_bytes()); 117 + neko.ratchet(); 112 118 113 119 Self { 114 120 kem_sec: PhantomData, 115 - strobe, 121 + neko, 116 122 } 117 123 } 118 124 ··· 120 126 &mut self, 121 127 rng: &mut impl CryptoRng, 122 128 buf: &mut dyn Buffer, 123 - ) -> aead::Result<AeadState<S>> { 129 + ) -> aead::Result<AeadTranport<S>> { 124 130 let slice = buf.as_ref(); 125 131 126 132 let tag = slice 127 133 .len() 128 - .checked_sub(<AeadStrobe<S> as aead::AeadCore>::TagSize::to_usize()) 134 + .checked_sub(<AeadNeko<S> as aead::AeadCore>::TagSize::to_usize()) 129 135 .ok_or(aead::Error)?; 130 136 131 137 let (encap, tag) = buf.as_ref().split_at(tag); 132 138 133 - let tag: aead::Tag<AeadStrobe<S>> = tag.try_into().unwrap(); 139 + let tag: aead::Tag<AeadNeko<S>> = tag.try_into().unwrap(); 134 140 135 - self.strobe.recv_clr(encap); 136 - self.strobe.recv_mac(&tag)?; 137 - self.strobe.ratchet(S::ratchet_bytes()); 141 + self.neko.cleartext(encap); 142 + self.neko.verify_mac(&tag)?; 138 143 139 144 let encap = K::EncapsulationKey::new_from_slice(encap).map_err(|_| aead::Error)?; 140 145 141 146 let (cipher, shared) = encap.encapsulate_with_rng(rng); 142 147 143 - let mut tag: aead::Tag<AeadStrobe<S>> = Default::default(); 144 - 145 148 buf.truncate(0); 146 149 147 - self.strobe.send_clr(cipher.as_ref()); 148 - self.strobe.send_mac(&mut tag); 149 - self.strobe.ratchet(S::ratchet_bytes()); 150 - 151 150 buf.extend_from_slice(cipher.as_ref())?; 152 - buf.extend_from_slice(&tag)?; 151 + self.neko.cleartext(cipher.as_ref()); 152 + buf.extend_from_slice(&self.neko.create_mac())?; 153 153 154 154 Ok(self.finish(shared)) 155 155 } 156 156 157 - fn finish(&mut self, shared: Array<u8, K::SharedKeySize>) -> AeadState<S> { 158 - self.strobe.ad(&shared); 157 + fn finish(&mut self, mut shared: SharedKey) -> AeadTranport<S> { 158 + self.neko.key(&shared); 159 + shared.zeroize(); 159 160 160 - let mut key: aead::Key<AeadStrobe<S>> = Default::default(); 161 - let mut inbound: aead::Nonce<AeadStrobe<S>> = Default::default(); 162 - let mut outbound: aead::Nonce<AeadStrobe<S>> = Default::default(); 161 + self.neko.ratchet(); 163 162 164 - self.strobe.prf(&mut key); 165 - self.strobe.prf(&mut inbound); 166 - self.strobe.prf(&mut outbound); 163 + let mut entropy: Array<u8, U64> = Default::default(); 167 164 168 - AeadState::new(key, outbound, inbound, StrobeRole::Receiver) 165 + self.neko.prf(&mut entropy); 166 + 167 + let (key, rest) = entropy.split(); 168 + let (outbound, inbound) = rest.split(); 169 + 170 + entropy.zeroize(); 171 + 172 + AeadTranport::new(key, outbound, inbound, Role::Receiver) 169 173 } 170 174 } 171 175 172 176 #[cfg(test)] 173 177 mod tests { 174 178 use ml_kem::{MlKem512, MlKem768}; 175 - use wharrgarbl_strobe::{Sec128, Sec256}; 179 + use wharrgarbl_neko::{Neko128, Neko256}; 176 180 177 181 use crate::utils::BufferSlice; 178 182 ··· 185 189 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, 186 190 ]; 187 191 188 - let mut alice = ClientHandshake::<Sec128, MlKem512>::new(Some(&psk)); 189 - let mut bob = ServerHandshake::<Sec128, MlKem512>::new(Some(&psk)); 192 + let mut alice = ClientHandshake::<Neko128, MlKem512>::new(Some(&psk)); 193 + let mut bob = ServerHandshake::<Neko128, MlKem512>::new(Some(&psk)); 190 194 191 195 // BufferSlice acts as our transport across the webz 192 196 let mut buf = alloc::vec![0u8; 2048]; ··· 216 220 217 221 #[test] 218 222 #[should_panic] 219 - fn handshake_fails_with_strobe_security_mismatch() { 223 + fn handshake_fails_with_neko_security_mismatch() { 220 224 let psk: [u8; 32] = [ 221 225 31, 48, 29, 177, 88, 236, 186, 84, 65, 51, 214, 243, 174, 24, 45, 101, 229, 129, 62, 222 226 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, 223 227 ]; 224 228 225 - let mut alice = ClientHandshake::<Sec128, MlKem512>::new(Some(&psk)); 226 - let mut bob = ServerHandshake::<Sec256, MlKem512>::new(Some(&psk)); 229 + let mut alice = ClientHandshake::<Neko128, MlKem512>::new(Some(&psk)); 230 + let mut bob = ServerHandshake::<Neko256, MlKem512>::new(Some(&psk)); 227 231 228 232 // BufferSlice acts as our transport across the webz 229 233 let mut buf = alloc::vec![0u8; 2048]; ··· 245 249 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, 246 250 ]; 247 251 248 - let mut alice = ClientHandshake::<Sec128, MlKem512>::new(Some(&psk)); 249 - let mut bob = ServerHandshake::<Sec128, MlKem768>::new(Some(&psk)); 252 + let mut alice = ClientHandshake::<Neko128, MlKem512>::new(Some(&psk)); 253 + let mut bob = ServerHandshake::<Neko128, MlKem768>::new(Some(&psk)); 250 254 251 255 // BufferSlice acts as our transport across the webz 252 256 let mut buf = alloc::vec![0u8; 2048]; ··· 268 272 132, 45, 174, 183, 65, 89, 73, 107, 177, 77, 90, 164, 251, 269 273 ]; 270 274 271 - let mut alice = ClientHandshake::<Sec128, MlKem512>::new(None); 272 - let mut bob = ServerHandshake::<Sec128, MlKem512>::new(Some(&psk)); 275 + let mut alice = ClientHandshake::<Neko128, MlKem512>::new(None); 276 + let mut bob = ServerHandshake::<Neko128, MlKem512>::new(Some(&psk)); 273 277 274 278 // BufferSlice acts as our transport across the webz 275 279 let mut buf = alloc::vec![0u8; 2048];
+22 -7
src/lib.rs
··· 2 2 #![forbid(unsafe_code)] 3 3 4 4 use ml_kem::{MlKem512, MlKem768}; 5 - pub use wharrgarbl_strobe::{Sec128, Sec256}; 5 + pub use wharrgarbl_neko::{Neko128, Neko256}; 6 6 7 7 pub mod handshake; 8 8 pub mod transport; ··· 10 10 extern crate alloc; 11 11 12 12 /// Version of WHARRGARBL that this crate implements. 13 - pub static WHARRGHARBL_PROTO: &str = "WGBL-v0.1-STv1.0.2"; 13 + pub static WHARRGHARBL_PROTO: &str = "WGBLv0.1"; 14 + 15 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 16 + #[repr(u8)] 17 + enum Role { 18 + Sender = 0, 19 + Receiver = 1, 20 + } 21 + 22 + impl core::ops::BitXor for Role { 23 + fn bitxor(self, rhs: Self) -> Self::Output { 24 + (self as u8) ^ (rhs as u8) 25 + } 26 + 27 + type Output = u8; 28 + } 14 29 15 30 pub mod utils { 16 - pub use wharrgarbl_utils::BufferSlice; 31 + pub use wharrgarbl_utils::{Buffer, BufferSlice}; 17 32 } 18 33 19 - pub type ClientHandshake128 = handshake::ClientHandshake<Sec128, MlKem512>; 20 - pub type ServerHandshake128 = handshake::ServerHandshake<Sec128, MlKem512>; 21 - pub type ClientHandshake256 = handshake::ClientHandshake<Sec256, MlKem768>; 22 - pub type ServerHandshake256 = handshake::ServerHandshake<Sec256, MlKem768>; 34 + pub type NekoClientHandshake128 = handshake::ClientHandshake<Neko128, MlKem512>; 35 + pub type NekoServerHandshake128 = handshake::ServerHandshake<Neko128, MlKem512>; 36 + pub type NekoClientHandshake256 = handshake::ClientHandshake<Neko256, MlKem768>; 37 + pub type NekoServerHandshake256 = handshake::ServerHandshake<Neko256, MlKem768>;
+70 -83
src/transport.rs
··· 1 1 use core::marker::PhantomData; 2 2 3 - use aead::{ 4 - AeadInOut, Buffer, Key, TagPosition, 5 - consts::{U16, U32}, 6 - }; 3 + use aead::{AeadInOut, Buffer, Key, KeySizeUser, TagPosition}; 7 4 use ctutils::{CtEq, CtSelect}; 8 - use wharrgarbl_strobe::{StrobeRole, StrobeState, traits::StrobeSecurity}; 5 + use hybrid_array::AssocArraySize; 6 + use wharrgarbl_neko::{NekoNonce, NekoSec, NekoState, NekoTag}; 9 7 10 - use crate::WHARRGHARBL_PROTO; 8 + use crate::{Role, WHARRGHARBL_PROTO}; 11 9 12 - pub struct AeadStrobe<S: StrobeSecurity> { 10 + pub struct AeadNeko<S: NekoSec> { 13 11 pub(crate) key: Key<Self>, 14 12 param: PhantomData<S>, 15 13 } 16 14 17 - impl<S: StrobeSecurity> aead::AeadCore for AeadStrobe<S> { 18 - type NonceSize = U16; 19 - type TagSize = U16; 20 - const TAG_POSITION: TagPosition = TagPosition::Postfix; 15 + impl<S: NekoSec> aead::KeySizeUser for AeadNeko<S> { 16 + type KeySize = <NekoState<S> as KeySizeUser>::KeySize; 21 17 } 22 18 23 - impl<S: StrobeSecurity> aead::KeySizeUser for AeadStrobe<S> { 24 - type KeySize = U32; 19 + impl<S: NekoSec> aead::AeadCore for AeadNeko<S> { 20 + type NonceSize = <NekoNonce as AssocArraySize>::Size; 21 + type TagSize = <NekoTag as AssocArraySize>::Size; 22 + const TAG_POSITION: TagPosition = TagPosition::Postfix; 25 23 } 26 24 27 - impl<S: StrobeSecurity> aead::KeyInit for AeadStrobe<S> { 25 + impl<S: NekoSec> aead::KeyInit for AeadNeko<S> { 28 26 fn new(key: &Key<Self>) -> Self { 29 27 Self { 30 28 key: *key, ··· 33 31 } 34 32 } 35 33 36 - impl<S: StrobeSecurity> aead::AeadInOut for AeadStrobe<S> { 34 + impl<S: NekoSec> aead::AeadInOut for AeadNeko<S> { 37 35 fn encrypt_inout_detached( 38 36 &self, 39 37 nonce: &aead::Nonce<Self>, 40 38 associated_data: &[u8], 41 39 mut buffer: aead::inout::InOutBuf<'_, '_, u8>, 42 40 ) -> aead::Result<aead::Tag<Self>> { 43 - let mut tag: aead::Tag<Self> = Default::default(); 44 - let mut strobe = StrobeState::<S>::new(WHARRGHARBL_PROTO.as_bytes(), StrobeRole::Sender); 41 + let mut neko = NekoState::<S>::new(WHARRGHARBL_PROTO.as_bytes()); 45 42 46 - strobe.key(&self.key); 47 - strobe.meta_ad(&S::to_bytes()); 48 - strobe.meta_ad(nonce); 49 - strobe.ad(associated_data); 50 - strobe.send_enc(buffer.get_out()); 43 + neko.key(&self.key); 44 + neko.nonce(nonce); 45 + neko.ad(associated_data); 46 + neko.encrypt(buffer.get_out()); 51 47 52 - strobe.send_mac(&mut tag); 53 - 54 - Ok(tag) 48 + Ok(neko.create_mac()) 55 49 } 56 50 57 51 fn decrypt_inout_detached( ··· 61 55 mut buffer: aead::inout::InOutBuf<'_, '_, u8>, 62 56 tag: &aead::Tag<Self>, 63 57 ) -> aead::Result<()> { 64 - let mut strobe = StrobeState::<S>::new(WHARRGHARBL_PROTO.as_bytes(), StrobeRole::Receiver); 58 + let mut neko = NekoState::<S>::new(WHARRGHARBL_PROTO.as_bytes()); 65 59 66 - strobe.key(&self.key); 67 - strobe.meta_ad(&S::to_bytes()); 68 - strobe.meta_ad(nonce); 69 - strobe.ad(associated_data); 70 - strobe.recv_enc(buffer.get_out()); 60 + neko.key(&self.key); 61 + neko.nonce(nonce); 62 + neko.ad(associated_data); 63 + neko.decrypt(buffer.get_out()); 71 64 72 - strobe.recv_mac(tag) 65 + neko.verify_mac(tag) 73 66 } 74 67 } 75 68 76 - impl<S: StrobeSecurity> zeroize::Zeroize for AeadStrobe<S> { 69 + impl<S: NekoSec> zeroize::Zeroize for AeadNeko<S> { 77 70 fn zeroize(&mut self) { 78 71 self.key.zeroize(); 79 72 } 80 73 } 81 74 82 - pub struct AeadState<S: StrobeSecurity> { 83 - pub(crate) aead: AeadStrobe<S>, 84 - pub(crate) epstein: aead::Nonce<AeadStrobe<S>>, 85 - pub(crate) trump: aead::Nonce<AeadStrobe<S>>, 86 - handshake_role: StrobeRole, 75 + pub struct AeadTranport<S: NekoSec> { 76 + pub(crate) aead: AeadNeko<S>, 77 + pub(crate) epstein: aead::Nonce<AeadNeko<S>>, 78 + pub(crate) trump: aead::Nonce<AeadNeko<S>>, 79 + handshake_role: Role, 87 80 } 88 81 89 - impl<S: StrobeSecurity> zeroize::Zeroize for AeadState<S> { 82 + impl<S: NekoSec> zeroize::Zeroize for AeadTranport<S> { 90 83 fn zeroize(&mut self) { 91 84 self.aead.zeroize(); 92 85 self.epstein.zeroize(); ··· 94 87 } 95 88 } 96 89 97 - impl<S: StrobeSecurity> zeroize::ZeroizeOnDrop for AeadState<S> {} 90 + impl<S: NekoSec> zeroize::ZeroizeOnDrop for AeadTranport<S> {} 98 91 99 - impl<S: StrobeSecurity> Drop for AeadState<S> { 92 + impl<S: NekoSec> Drop for AeadTranport<S> { 100 93 fn drop(&mut self) { 101 94 zeroize::Zeroize::zeroize(self); 102 95 } 103 96 } 104 97 105 - impl<S: StrobeSecurity> AeadState<S> { 98 + impl<S: NekoSec> AeadTranport<S> { 106 99 pub(crate) fn new( 107 - key: Key<AeadStrobe<S>>, 108 - outbound: aead::Nonce<AeadStrobe<S>>, 109 - inbound: aead::Nonce<AeadStrobe<S>>, 110 - role: StrobeRole, 100 + key: Key<AeadNeko<S>>, 101 + outbound: aead::Nonce<AeadNeko<S>>, 102 + inbound: aead::Nonce<AeadNeko<S>>, 103 + role: Role, 111 104 ) -> Self { 112 105 assert_ne!( 113 106 &inbound, &outbound, ··· 115 108 ); 116 109 117 110 Self { 118 - aead: AeadStrobe { 111 + aead: AeadNeko { 119 112 key, 120 113 param: PhantomData, 121 114 }, ··· 125 118 } 126 119 } 127 120 128 - fn select_nonce(&self, sending_role: StrobeRole) -> aead::Nonce<AeadStrobe<S>> { 121 + fn select_nonce(&self, sending_role: Role) -> aead::Nonce<AeadNeko<S>> { 129 122 let role_context = self.handshake_role ^ sending_role; 130 123 131 124 self.epstein.ct_select(&self.trump, role_context.ct_eq(&1)) 132 125 } 133 126 134 - fn mix_nonce(&self, position: [u8; 8], sending_role: StrobeRole) -> aead::Nonce<AeadStrobe<S>> { 127 + fn mix_nonce(&self, position: [u8; 8], sending_role: Role) -> aead::Nonce<AeadNeko<S>> { 135 128 let mut nonce = self.select_nonce(sending_role); 136 129 137 130 let mid = nonce.len() - position.len(); ··· 158 151 } 159 152 } 160 153 161 - pub struct SendState<'a, S: StrobeSecurity> { 162 - transport: &'a AeadState<S>, 154 + pub struct SendState<'a, S: NekoSec> { 155 + transport: &'a AeadTranport<S>, 163 156 counter: u64, 164 157 } 165 158 166 - impl<S: StrobeSecurity> SendState<'_, S> { 159 + impl<S: NekoSec> SendState<'_, S> { 167 160 pub fn encrypt(&mut self, buffer: &mut dyn Buffer, ad: &[u8]) -> aead::Result<()> { 168 - if self.counter.ct_eq(&u64::MAX).into() { 169 - return Err(aead::Error); 161 + match self.counter.checked_add(1) { 162 + Some(inc) => self.counter = inc, 163 + None => return Err(aead::Error), 170 164 } 171 165 172 - let encryption_result = self.transport.aead.encrypt_in_place( 166 + self.transport.aead.encrypt_in_place( 173 167 &self 174 168 .transport 175 - .mix_nonce(self.counter.to_be_bytes(), StrobeRole::Sender), 169 + .mix_nonce(self.counter.to_be_bytes(), Role::Sender), 176 170 ad, 177 171 buffer, 178 - ); 179 - 180 - self.counter = self.counter.wrapping_add(1); 181 - 182 - encryption_result 172 + ) 183 173 } 184 174 } 185 175 186 - pub struct RecvState<'a, S: StrobeSecurity> { 187 - transport: &'a AeadState<S>, 176 + pub struct RecvState<'a, S: NekoSec> { 177 + transport: &'a AeadTranport<S>, 188 178 counter: u64, 189 179 } 190 180 191 - impl<S: StrobeSecurity> RecvState<'_, S> { 181 + impl<S: NekoSec> RecvState<'_, S> { 192 182 pub fn decrypt(&mut self, buffer: &mut dyn Buffer, ad: &[u8]) -> aead::Result<()> { 193 - if self.counter.ct_eq(&u64::MAX).into() { 194 - return Err(aead::Error); 183 + match self.counter.checked_add(1) { 184 + Some(inc) => self.counter = inc, 185 + None => return Err(aead::Error), 195 186 } 196 187 197 188 // If the message's MAC doesn't evaluate successfully, this op will fail. 198 - let decryption_result = self.transport.aead.decrypt_in_place( 189 + self.transport.aead.decrypt_in_place( 199 190 &self 200 191 .transport 201 - .mix_nonce(self.counter.to_be_bytes(), StrobeRole::Receiver), 192 + .mix_nonce(self.counter.to_be_bytes(), Role::Receiver), 202 193 ad, 203 194 buffer, 204 - ); 205 - 206 - self.counter = self.counter.wrapping_add(1); 207 - 208 - decryption_result 195 + ) 209 196 } 210 197 } 211 198 212 199 #[cfg(test)] 213 200 mod tests { 214 - use wharrgarbl_strobe::{Sec128, Sec256}; 201 + use wharrgarbl_neko::{Neko128, Neko256}; 215 202 216 203 use super::*; 217 204 ··· 226 213 let outbound = 123u128.to_ne_bytes(); 227 214 let inbound = 234u128.to_ne_bytes(); 228 215 229 - let alice = AeadState::<Sec128>::new( 216 + let alice = AeadTranport::<Neko128>::new( 230 217 shared_secret.into(), 231 218 outbound.into(), 232 219 inbound.into(), 233 - StrobeRole::Sender, 220 + Role::Sender, 234 221 ); 235 - let bob = AeadState::<Sec128>::new( 222 + let bob = AeadTranport::<Neko128>::new( 236 223 shared_secret.into(), 237 224 outbound.into(), 238 225 inbound.into(), 239 - StrobeRole::Receiver, 226 + Role::Receiver, 240 227 ); 241 228 242 229 let (mut alice_send, mut alice_recv) = alice.split(); 243 230 let (mut bob_send, mut bob_recv) = bob.split(); 244 231 245 - let orig = b"Test Message, Please ignore."; 232 + let orig = b"Test Message, Please ignore. AAAAAAAAAAAAAAAAAAaaaaaaaaaaa | B ."; 246 233 247 234 let ad = b"random"; 248 235 ··· 299 286 let outbound = 123u128.to_ne_bytes(); 300 287 let inbound = 234u128.to_ne_bytes(); 301 288 302 - let alice = AeadState::<Sec128>::new( 289 + let alice = AeadTranport::<Neko128>::new( 303 290 shared_secret.into(), 304 291 outbound.into(), 305 292 inbound.into(), 306 - StrobeRole::Sender, 293 + Role::Sender, 307 294 ); 308 - let bob = AeadState::<Sec256>::new( 295 + let bob = AeadTranport::<Neko256>::new( 309 296 shared_secret.into(), 310 297 outbound.into(), 311 298 inbound.into(), 312 - StrobeRole::Receiver, 299 + Role::Receiver, 313 300 ); 314 301 315 302 let (mut alice_send, mut _alice_recv) = alice.split();
+19
wharrgarbl-neko/Cargo.toml
··· 1 + [package] 2 + name = "wharrgarbl-neko" 3 + authors.workspace = true 4 + edition.workspace = true 5 + repository.workspace = true 6 + version.workspace = true 7 + license.workspace = true 8 + 9 + [dependencies] 10 + wharrgarbl-utils = { path = "../wharrgarbl-utils", version = "0.1" } 11 + aead.workspace = true 12 + zeroize.workspace = true 13 + ctutils.workspace = true 14 + keccak = { version = "0.2" } 15 + hybrid-array.workspace = true 16 + zerocopy = "0.8.48" 17 + 18 + # [dev-dependencies] 19 + # zerocopy = "0.8.48"
+13
wharrgarbl-neko/README.md
··· 1 + # NEKO 2 + 3 + A whimsical encryption crate that is inspired by the STROBE spec, though it takes diverging choices with regards to its internals and API surface for better performance while retaining security guarantees. 4 + 5 + ## SPEC 6 + 7 + WIP 8 + 9 + ☢️ **WARNING: DO NOT USE IN PRODUCTION. THIS CRATE IS NOT AUDITED AND IS WIP, MAYBE EVEN NOT AS SECURE AS THOUGHT. AAAAAAAAAA** ☢️ 10 + 11 + ## License 12 + 13 + Code dual-licensed under Apache 2.0 or MIT.
+6
wharrgarbl-neko/src/flags.rs
··· 1 + use wharrgarbl_utils::OpFlags; 2 + 3 + pub const TRANSPORT: OpFlags = OpFlags::new(1 << 0); 4 + pub const APP: OpFlags = OpFlags::new(1 << 1); 5 + pub const CIPHER: OpFlags = OpFlags::new(1 << 2); 6 + pub const META: OpFlags = OpFlags::new(1 << 3);
+286
wharrgarbl-neko/src/kats.rs
··· 1 + use hybrid_array::Array; 2 + use zerocopy::IntoBytes; 3 + 4 + use crate::{Neko128, Neko256, NekoState}; 5 + 6 + extern crate alloc; 7 + 8 + #[test] 9 + fn neko_128_init_state() { 10 + let neko = NekoState::<Neko128>::new(b"test"); 11 + 12 + let first = neko.state[0].to_le_bytes(); 13 + let second = neko.state[1].to_le_bytes(); 14 + let third = neko.state[2].to_le_bytes(); 15 + 16 + assert_eq!(&first, b"\x01\x16\x07\x60NEKO"); 17 + // Values that don't fill the block entirely leave padding 18 + assert_eq!(&second, b"v0.1.0\0\0"); 19 + assert_eq!(&third[..4], b"test"); 20 + // The rest of the state is zeroed 21 + assert_eq!(&neko.state[3..], &[0; 22]); 22 + } 23 + 24 + #[test] 25 + fn neko_256_init_state() { 26 + let neko = NekoState::<Neko256>::new(b"test"); 27 + 28 + let first = neko.state[0].to_le_bytes(); 29 + let second = neko.state[1].to_le_bytes(); 30 + let third = neko.state[2].to_le_bytes(); 31 + 32 + assert_eq!(&first, b"\x01\x14\x07\x60NEKO"); 33 + // Values that don't fill the block entirely leave padding 34 + assert_eq!(&second, b"v0.1.0\0\0"); 35 + assert_eq!(&third[..4], b"test"); 36 + // The rest of the state is zeroed 37 + assert_eq!(&neko.state[3..], &[0; 22]); 38 + } 39 + 40 + #[test] 41 + fn happy_path() { 42 + let mut tx = NekoState::<Neko128>::new(b"test"); 43 + let mut rx = NekoState::<Neko128>::new(b"test"); 44 + 45 + let key = Array::from([13u8; 32]); 46 + 47 + let nonce = Array::from([42u8; 16]); 48 + 49 + let ad = b"guuhhh"; 50 + 51 + tx.key(&key); 52 + rx.key(&key); 53 + 54 + tx.nonce(&nonce); 55 + rx.nonce(&nonce); 56 + 57 + tx.ad(ad); 58 + rx.ad(ad); 59 + 60 + let orig = b"Test message, please ignore."; 61 + 62 + let mut msg = orig.to_vec(); 63 + 64 + tx.encrypt(&mut msg); 65 + let mac = tx.create_mac(); 66 + 67 + assert_ne!(&msg, orig); 68 + assert_ne!(&mac, &[0u8; 16]); 69 + 70 + rx.decrypt(&mut msg); 71 + let verification = rx.verify_mac(&mac); 72 + 73 + assert_eq!(&msg, orig); 74 + assert!(verification.is_ok()); 75 + } 76 + 77 + #[test] 78 + fn non_cipher_flag_ops_dont_permute_by_default() { 79 + let key = Array([1u8; 32]); 80 + let nonce = Array([2u8; 16]); 81 + let ad = b"addz"; 82 + 83 + let cleartext = b"Message."; 84 + 85 + let mut neko = NekoState::<Neko128>::new(b"test"); 86 + 87 + neko.key(&key); 88 + neko.nonce(&nonce); 89 + neko.ad(ad); 90 + 91 + let op = neko.state[3].as_bytes(); 92 + let state = neko.state[4..8].as_bytes(); 93 + 94 + // OPs are XOR'd onto the state, but when the state is zeroed, it 95 + // is the same as overwriting, so the ops are visible. 96 + assert_eq!(op, b"\x00\x08nyaan~"); 97 + assert_eq!(state, key.as_slice()); 98 + 99 + let op = neko.state[8].as_bytes(); 100 + let state = neko.state[9..11].as_bytes(); 101 + 102 + assert_eq!(op, b"\x04\x09nyaan~"); 103 + assert_eq!(state, nonce.as_slice()); 104 + 105 + let op = neko.state[11].as_bytes(); 106 + let state = neko.state[12].as_bytes(); 107 + 108 + assert_eq!(op, b"\x09\x0Anyaan~"); 109 + assert_eq!(state, b"addz\0\0\0\0"); 110 + 111 + assert_eq!(&neko.state[13..], &[0; 12]); 112 + 113 + // Cleartext will permute the state 114 + neko.cleartext(cleartext); 115 + 116 + for block in neko.state[0..22].iter().map(IntoBytes::as_bytes) { 117 + // The permuted state means the message is mixed, so it can no longer be "read" 118 + // directly. 119 + assert_ne!(block, b"\x0c\x03nyaan~"); 120 + assert_ne!(block, cleartext); 121 + } 122 + let orig_text = u64::from_le_bytes(*cleartext); 123 + let orig_state = neko.state[0] ^ orig_text; 124 + let recreated = neko.state[0] ^ orig_state; 125 + 126 + // After XOR'ing from the state, we can reconstruct the cleartext, so we can confirm 127 + // it is there. 128 + assert_eq!(orig_text, recreated); 129 + } 130 + 131 + #[test] 132 + fn cipher_ops_permute_state_by_default() { 133 + let key = Array([1u8; 32]); 134 + 135 + let cleartext = b"MESSAGE ENCRYPT."; 136 + 137 + let mut message = cleartext.to_vec(); 138 + 139 + let mut neko = NekoState::<Neko128>::new(b"test"); 140 + 141 + neko.key(&key); 142 + 143 + // This op will cause the state to be permuted. 144 + // Encrypted data will be mixed into the permuted state. 145 + neko.encrypt(&mut message); 146 + 147 + // The encrypted message no longer matches the cleartext 148 + assert_ne!(cleartext, message.as_slice()); 149 + // The message now is the same as the internal state. 150 + // This means that on a receiving state, the message can 151 + // be decrypted via exchanging the state with the ciphertext. 152 + assert_eq!(neko.state[0..2].as_bytes(), &message); 153 + 154 + let expected_state = [ 155 + 0x14bf7a55ecbfef9f, 156 + 0xbf881650acfa3419, 157 + 0x19e0cf77f80c9b9c, 158 + 0x926cb071dcc542bf, 159 + 0xb0c2ff19b2a1088a, 160 + 0x28e1786b57d258b5, 161 + 0xa10f75b4a49c5830, 162 + 0xe0d2a4c88992ddab, 163 + 0xf26af8755e5d7ce8, 164 + 0xa130fa26d8697da0, 165 + 0x2d6266942d30bb73, 166 + 0x0c47cacae106048e, 167 + 0x1f78293be88890d7, 168 + 0xcf24691274e96761, 169 + 0x0501d35fa5a7db34, 170 + 0xd242e3a40bf885f8, 171 + 0x717c3e620ea21bdf, 172 + 0x12af362391a15895, 173 + 0x8df18b22be842ea5, 174 + 0xa32fd58b7771026c, 175 + 0x4a61af391e658ec5, 176 + 0x189f225280d6d986, 177 + 0x2718bc59d75e8d60, 178 + 0x0be0f806c2086f5a, 179 + 0xd5f2731cc9c0fed0, 180 + ]; 181 + 182 + assert_eq!(&neko.state, &expected_state); 183 + 184 + // Ratchet permutes the state, and then zeros out 128 bits or 256 bits 185 + // depending on its security strength to ensure forward secrecy. 186 + neko.ratchet(); 187 + 188 + let ratched_expected_state = [ 189 + 0x0000000000000000, 190 + 0x0000000000000000, 191 + 0xaeaae19feedba131, 192 + 0x142c658c8c5d4d41, 193 + 0x5b15a77c99a73daa, 194 + 0xe8060d814eb54fe2, 195 + 0xaf03a57bc4107f06, 196 + 0x20a98fb94a745e23, 197 + 0x311dd2be1529baad, 198 + 0x4d50f23f34057523, 199 + 0xea89ea72449476d5, 200 + 0x82457373a57d4062, 201 + 0x0aad58af56986ea2, 202 + 0x8548e7cb9b7907c3, 203 + 0xca95c689bed5fbbb, 204 + 0x426b6a66b930e5ad, 205 + 0x514c8865b88f89ea, 206 + 0x361e3d8a73cd12f1, 207 + 0xbcd3e3c6949cec98, 208 + 0x6b210034af33aeac, 209 + 0x2d06ca5415ba4459, 210 + 0x57dfabd826619f1e, 211 + 0x69f4dd80c2791cc4, 212 + 0xd9086f9fde008f52, 213 + 0xd71233e8001d2c80, 214 + ]; 215 + 216 + assert_eq!(&neko.state, &ratched_expected_state); 217 + // The rest of the non-zeroed state should not match the previous state 218 + assert_ne!(&expected_state[2..], &ratched_expected_state[2..]); 219 + } 220 + 221 + #[test] 222 + #[should_panic] 223 + fn mac_correctly_fails_when_it_doesnt_match() { 224 + let key = Array([1u8; 32]); 225 + 226 + let cleartext = b"MESSAGE ENCRYPT."; 227 + 228 + let mut message = cleartext.to_vec(); 229 + 230 + let mut tx = NekoState::<Neko128>::new(b"test"); 231 + let mut rx = NekoState::<Neko128>::new(b"test"); 232 + 233 + tx.key(&key); 234 + rx.key(&key); 235 + 236 + tx.encrypt(&mut message); 237 + let mut tag = tx.create_mac(); 238 + 239 + #[rustfmt::skip] 240 + let expected_tag = [ 241 + 0xc7, 0xd3, 0xa9, 0x29, 0x61, 0x4a, 0x95, 0xdb, 242 + 0x39, 0xc3, 0xe2, 0x3f, 0x7e, 0xe1, 0x2e, 0x9f, 243 + ]; 244 + 245 + assert_eq!(&tag, &expected_tag); 246 + 247 + // Modify the tag so that it always fails 248 + tag[5] ^= 3; 249 + 250 + rx.decrypt(&mut message); 251 + let verification = rx.verify_mac(&tag); 252 + assert!(verification.is_ok()); 253 + } 254 + 255 + #[test] 256 + #[should_panic] 257 + fn mac_correctly_fails_when_state_doesnt_match() { 258 + let key = Array([1u8; 32]); 259 + 260 + let cleartext = b"MESSAGE ENCRYPT."; 261 + 262 + let mut message = cleartext.to_vec(); 263 + 264 + let mut tx = NekoState::<Neko128>::new(b"test"); 265 + let mut rx = NekoState::<Neko128>::new(b"test"); 266 + 267 + tx.key(&key); 268 + rx.key(&key); 269 + 270 + tx.encrypt(&mut message); 271 + let tag = tx.create_mac(); 272 + 273 + #[rustfmt::skip] 274 + let expected_tag = [ 275 + 0xc7, 0xd3, 0xa9, 0x29, 0x61, 0x4a, 0x95, 0xdb, 276 + 0x39, 0xc3, 0xe2, 0x3f, 0x7e, 0xe1, 0x2e, 0x9f, 277 + ]; 278 + 279 + message.extend_from_slice(&[0, 0, 0]); 280 + 281 + assert_eq!(&tag, &expected_tag); 282 + // Decrypt a message that has had its length extended 283 + rx.decrypt(&mut message); 284 + let verification = rx.verify_mac(&tag); 285 + assert!(verification.is_ok()); 286 + }
+300
wharrgarbl-neko/src/lib.rs
··· 1 + #![no_std] 2 + #![forbid(unsafe_code)] 3 + 4 + mod flags; 5 + #[cfg(test)] 6 + mod kats; 7 + mod operators; 8 + mod ops; 9 + mod traits; 10 + 11 + use core::marker::PhantomData; 12 + 13 + use aead::{ 14 + KeySizeUser, 15 + consts::{U4, U10, U16, U25, U32, U128, U256}, 16 + }; 17 + use ctutils::CtEq; 18 + use hybrid_array::Array; 19 + use wharrgarbl_utils::OpFlags; 20 + 21 + use crate::operators::{NekoOperate, NekoOperateMut}; 22 + pub use crate::traits::NekoSec; 23 + 24 + pub type Neko128 = U128; 25 + pub type Neko256 = U256; 26 + pub type NekoNonce = Array<u8, U16>; 27 + pub type NekoKey<S> = Array<u8, <NekoState<S> as KeySizeUser>::KeySize>; 28 + pub type NekoTag = Array<u8, U16>; 29 + 30 + pub static NEKO_VERSION: &str = "NEKOv0.1.0"; 31 + 32 + impl<S: NekoSec> KeySizeUser for NekoState<S> { 33 + type KeySize = U32; 34 + } 35 + 36 + #[derive(Clone)] 37 + pub struct NekoState<S: NekoSec> { 38 + pub(crate) state: Array<u64, U25>, 39 + position: usize, 40 + start: usize, 41 + sec: PhantomData<S>, 42 + } 43 + 44 + impl<S: NekoSec> zeroize::Zeroize for NekoState<S> { 45 + fn zeroize(&mut self) { 46 + self.state.zeroize(); 47 + self.position.zeroize(); 48 + self.position.zeroize(); 49 + } 50 + } 51 + 52 + impl<S: NekoSec> zeroize::ZeroizeOnDrop for NekoState<S> {} 53 + 54 + impl<S: NekoSec> Drop for NekoState<S> { 55 + fn drop(&mut self) { 56 + zeroize::Zeroize::zeroize(self); 57 + } 58 + } 59 + 60 + impl<Sec: NekoSec> NekoState<Sec> { 61 + pub fn new(protocol: &[u8]) -> Self { 62 + let mut strobe = Self { 63 + state: Array([0u64; keccak::PLEN]), 64 + position: 0, 65 + start: 0, 66 + sec: PhantomData, 67 + }; 68 + 69 + let preamble: Array<u8, U4> = Array::from([0x01, Sec::rate() as u8, 0x07, 0x60]); 70 + 71 + // This is safe because the static string is always 10 bytes long. 72 + let version: Array<u8, U10> = Array::try_from(NEKO_VERSION.as_bytes()).unwrap(); 73 + 74 + let combined = preamble.concat(version); 75 + 76 + strobe.overwrite(&combined); 77 + strobe.overwrite(protocol); 78 + 79 + strobe 80 + } 81 + 82 + #[inline] 83 + fn begin_op(&mut self, red_flags: OpFlags) { 84 + let old_start = self.start; 85 + self.start = self.position + 1; 86 + 87 + self.absorb(&[ 88 + old_start as u8, 89 + red_flags.bits(), 90 + b'n', 91 + b'y', 92 + b'a', 93 + b'a', 94 + b'n', 95 + b'~', 96 + ]); 97 + } 98 + 99 + fn permutation_f(&mut self) { 100 + let permuter = [ 101 + self.start as u8, 102 + self.position as u8, 103 + 0x04, 104 + 0x80, 105 + b'M', 106 + b'E', 107 + b'O', 108 + b'W', 109 + ]; 110 + 111 + // XOR the permuter into first entropy block 112 + self.state[Sec::rate()] ^= u64::from_le_bytes(permuter); 113 + 114 + keccak::Keccak::new().with_f1600(|permute| permute(&mut self.state.0)); 115 + 116 + self.position = 0; 117 + self.start = 0; 118 + } 119 + 120 + #[inline] 121 + fn absorb(&mut self, data: &[u8]) { 122 + NekoOperate::new(self, data).operate(|(state, byte)| *state ^= *byte); 123 + } 124 + 125 + #[inline] 126 + fn absorb_and_set(&mut self, data: &mut [u8]) { 127 + NekoOperateMut::new(self, data).operate_mut(|(state, byte)| { 128 + *state ^= *byte; 129 + *byte = *state; 130 + }); 131 + } 132 + 133 + #[inline] 134 + fn copy_state(&mut self, data: &mut [u8]) { 135 + NekoOperateMut::new(self, data).operate_mut(|(state, byte)| *byte = *state); 136 + } 137 + 138 + #[inline] 139 + fn exchange(&mut self, data: &mut [u8]) { 140 + NekoOperateMut::new(self, data).operate_mut(|(state, byte)| { 141 + *byte ^= *state; 142 + *state ^= *byte; 143 + }); 144 + } 145 + 146 + #[inline] 147 + fn overwrite(&mut self, data: &[u8]) { 148 + NekoOperate::new(self, data).operate(|(state, byte)| *state = *byte); 149 + } 150 + 151 + #[inline] 152 + fn squeeze(&mut self, data: &mut [u8]) { 153 + NekoOperateMut::new(self, data).operate_mut(|(state, byte)| { 154 + *byte = *state; 155 + *state = 0; 156 + }); 157 + } 158 + 159 + #[inline] 160 + fn zero_state(&mut self) { 161 + let ratchet_bytes = Sec::ratchet_bytes(); 162 + 163 + self.state[0..ratchet_bytes].iter_mut().for_each(|block| { 164 + *block = 0; 165 + }); 166 + 167 + self.position += ratchet_bytes; 168 + } 169 + 170 + pub fn key(&mut self, data: &NekoKey<Sec>) { 171 + self.begin_op(ops::KEY); 172 + 173 + self.overwrite(data); 174 + } 175 + 176 + pub fn nonce(&mut self, data: &NekoNonce) { 177 + self.begin_op(ops::NONCE); 178 + 179 + self.absorb(data); 180 + } 181 + 182 + /// Absorb associated data into the cipher. This should include a nonce at least, 183 + /// along with other data coming from the app. 184 + pub fn ad(&mut self, data: &[u8]) { 185 + self.begin_op(ops::AD); 186 + 187 + self.absorb(data); 188 + } 189 + 190 + pub fn prf(&mut self, data: &mut [u8]) { 191 + self.begin_op(ops::PRF); 192 + 193 + self.permutation_f(); 194 + 195 + self.squeeze(data); 196 + } 197 + 198 + pub fn create_mac(&mut self) -> NekoTag { 199 + self.begin_op(ops::MAC); 200 + 201 + self.permutation_f(); 202 + 203 + let mut tag: NekoTag = Default::default(); 204 + 205 + self.copy_state(&mut tag); 206 + 207 + tag 208 + } 209 + 210 + pub fn verify_mac(&mut self, data: &NekoTag) -> aead::Result<()> { 211 + self.begin_op(ops::MAC); 212 + 213 + self.permutation_f(); 214 + 215 + let mut mac_copy = *data; 216 + 217 + self.exchange(&mut mac_copy); 218 + 219 + let all_zero: NekoTag = Default::default(); 220 + 221 + if mac_copy.ct_eq(&all_zero).to_bool() { 222 + Ok(()) 223 + } else { 224 + Err(aead::Error) 225 + } 226 + } 227 + 228 + pub fn encrypt(&mut self, data: &mut [u8]) { 229 + self.begin_op(ops::ENC); 230 + 231 + self.permutation_f(); 232 + 233 + self.absorb_and_set(data); 234 + } 235 + 236 + pub fn decrypt(&mut self, data: &mut [u8]) { 237 + self.begin_op(ops::ENC); 238 + 239 + self.permutation_f(); 240 + 241 + self.exchange(data); 242 + } 243 + 244 + /// This is for mixing a cleartext message into the state. This is meant to be used 245 + /// both for when you send the cleartext message on one side, then when you receive the 246 + /// message on the other side. 247 + pub fn cleartext(&mut self, data: &[u8]) { 248 + self.begin_op(ops::CLR); 249 + 250 + self.permutation_f(); 251 + 252 + self.absorb(data); 253 + } 254 + 255 + pub fn ratchet(&mut self) { 256 + self.begin_op(ops::RATCHET); 257 + 258 + self.permutation_f(); 259 + 260 + self.zero_state(); 261 + } 262 + } 263 + 264 + impl<S: NekoSec> core::fmt::Display for NekoState<S> { 265 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 266 + f.write_str(NEKO_VERSION)?; 267 + write!(f, "/1600-{}", S::to_usize()) 268 + } 269 + } 270 + 271 + impl<S: NekoSec> core::fmt::Debug for NekoState<S> { 272 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 273 + // Do not reveal internal state of StrobeState, other than its security level 274 + f.debug_struct("NekoState") 275 + .field("sec", &S::to_usize()) 276 + .field("version", &NEKO_VERSION) 277 + .finish_non_exhaustive() 278 + } 279 + } 280 + 281 + #[cfg(test)] 282 + mod tests { 283 + use super::*; 284 + 285 + extern crate std; 286 + 287 + #[test] 288 + fn version_formatting() { 289 + let s = NekoState::<Neko128>::new(b""); 290 + 291 + let display = std::format!("{s}"); 292 + let debug = std::format!("{s:?}"); 293 + 294 + assert_eq!(&display, "NEKOv0.1.0/1600-128"); 295 + assert_eq!( 296 + &debug, 297 + "NekoState { sec: 128, version: \"NEKOv0.1.0\", .. }" 298 + ); 299 + } 300 + }
+95
wharrgarbl-neko/src/operators.rs
··· 1 + use zerocopy::IntoBytes; 2 + 3 + use crate::{NekoSec, NekoState}; 4 + 5 + const U64_CHUNK: usize = core::mem::size_of::<u64>(); 6 + 7 + pub(crate) struct NekoOperateMut<'s, S: NekoSec> { 8 + neko: &'s mut NekoState<S>, 9 + data: &'s mut [u8], 10 + } 11 + 12 + impl<'s, S: NekoSec> NekoOperateMut<'s, S> { 13 + #[inline] 14 + pub(crate) const fn new(neko: &'s mut NekoState<S>, data: &'s mut [u8]) -> Self { 15 + Self { neko, data } 16 + } 17 + 18 + #[inline] 19 + fn next_block(&self) -> Option<usize> { 20 + match self 21 + .data 22 + .len() 23 + .min((S::rate() - self.neko.position) * U64_CHUNK) 24 + { 25 + 0 => None, 26 + take => Some(take), 27 + } 28 + } 29 + 30 + #[inline] 31 + pub(crate) fn operate_mut(&'s mut self, operation: fn((&mut u8, &mut u8))) { 32 + while let Some(take) = self.next_block() { 33 + let (block, rest) = self.data.split_at_mut(take); 34 + 35 + self.data = rest; 36 + 37 + // Trans the neko 38 + let transed_bytes = self.neko.state[self.neko.position..].as_mut_bytes(); 39 + 40 + transed_bytes.iter_mut().zip(block).for_each(operation); 41 + 42 + if !self.data.is_empty() { 43 + self.neko.permutation_f(); 44 + } else { 45 + self.neko.position += take.div_ceil(U64_CHUNK); 46 + break; 47 + } 48 + } 49 + } 50 + } 51 + 52 + pub(crate) struct NekoOperate<'s, S: NekoSec> { 53 + neko: &'s mut NekoState<S>, 54 + data: &'s [u8], 55 + } 56 + 57 + impl<'s, S: NekoSec> NekoOperate<'s, S> { 58 + #[inline] 59 + pub(crate) const fn new(neko: &'s mut NekoState<S>, data: &'s [u8]) -> Self { 60 + Self { neko, data } 61 + } 62 + 63 + #[inline] 64 + fn next_block(&self) -> Option<usize> { 65 + match self 66 + .data 67 + .len() 68 + .min((S::rate() - self.neko.position) * U64_CHUNK) 69 + { 70 + 0 => None, 71 + take => Some(take), 72 + } 73 + } 74 + 75 + #[inline] 76 + pub(crate) fn operate(&'s mut self, operation: fn((&mut u8, &u8))) { 77 + while let Some(take) = self.next_block() { 78 + let (block, rest) = self.data.split_at(take); 79 + 80 + self.data = rest; 81 + 82 + // Trans the neko 83 + let transed_bytes = self.neko.state[self.neko.position..].as_mut_bytes(); 84 + 85 + transed_bytes.iter_mut().zip(block).for_each(operation); 86 + 87 + if !self.data.is_empty() { 88 + self.neko.permutation_f(); 89 + } else { 90 + self.neko.position += take.div_ceil(U64_CHUNK); 91 + break; 92 + } 93 + } 94 + } 95 + }
+13
wharrgarbl-neko/src/ops.rs
··· 1 + use wharrgarbl_utils::OpFlags; 2 + 3 + use crate::flags::*; 4 + 5 + pub const CLR: OpFlags = OpFlags::new(APP.bits() | TRANSPORT.bits()); 6 + pub const MAC: OpFlags = OpFlags::new(CIPHER.bits() | TRANSPORT.bits()); 7 + pub const ENC: OpFlags = OpFlags::new(APP.bits() | CIPHER.bits() | TRANSPORT.bits()); 8 + 9 + pub const KEY: OpFlags = OpFlags::new(META.bits()); 10 + pub const NONCE: OpFlags = OpFlags::new(META.bits() | TRANSPORT.bits()); 11 + pub const AD: OpFlags = OpFlags::new(META.bits() | APP.bits()); 12 + pub const RATCHET: OpFlags = OpFlags::new(META.bits() | CIPHER.bits()); 13 + pub const PRF: OpFlags = OpFlags::new(META.bits() | APP.bits() | CIPHER.bits());
+19
wharrgarbl-neko/src/traits.rs
··· 1 + use aead::consts::{U128, U256}; 2 + use hybrid_array::typenum::Unsigned; 3 + 4 + pub trait NekoSec: Unsigned { 5 + fn to_bytes() -> [u8; 2] { 6 + Self::to_u16().to_le_bytes() 7 + } 8 + 9 + fn ratchet_bytes() -> usize { 10 + Self::to_usize().wrapping_shr(6) 11 + } 12 + 13 + fn rate() -> usize { 14 + keccak::PLEN - Self::ratchet_bytes() - 1 15 + } 16 + } 17 + 18 + impl NekoSec for U128 {} 19 + impl NekoSec for U256 {}
-13
wharrgarbl-strobe/src/herding_kats/harness.rs
··· 183 183 ); 184 184 } 185 185 186 - // fn security_param_from_bits<'de, D: Deserializer<'de>>( 187 - // deserializer: D, 188 - // ) -> Result<StrobeSecurity, D::Error> { 189 - // match u64::deserialize(deserializer)? { 190 - // 128 => Ok(StrobeSecurity::B128), 191 - // 256 => Ok(StrobeSecurity::B256), 192 - // n => Err(de::Error::custom(std::format!( 193 - // "Invalid security parameter: {}", 194 - // n 195 - // ))), 196 - // } 197 - // } 198 - 199 186 fn bytes_from_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error> 200 187 where 201 188 D: Deserializer<'de>,
-1
wharrgarbl-strobe/src/lib.rs
··· 2 2 #![forbid(unsafe_code)] 3 3 4 4 mod keccakf; 5 - mod opflags; 6 5 mod ops; 7 6 mod strobe; 8 7
+29 -34
wharrgarbl-strobe/src/opflags.rs wharrgarbl-utils/src/opflags.rs
··· 8 8 9 9 impl OpFlags { 10 10 pub const EMPTY: OpFlags = OpFlags(0); 11 - pub const INBOUND: OpFlags = OpFlags(1 << 0); 12 - pub const APP: OpFlags = OpFlags(1 << 1); 13 - pub const CIPHER: OpFlags = OpFlags(1 << 2); 14 - pub const TRANSPORT: OpFlags = OpFlags(1 << 3); 15 - pub const META: OpFlags = OpFlags(1 << 4); 16 - pub const KEYTREE: OpFlags = OpFlags(1 << 5); 17 11 18 - pub(crate) const fn new(val: u8) -> Self { 12 + pub const fn new(val: u8) -> Self { 19 13 Self(val) 20 14 } 21 15 ··· 27 21 (*self & other).ct_ne(&OpFlags::EMPTY) 28 22 } 29 23 30 - fn insert(&mut self, flags: OpFlags) { 24 + pub fn insert(&mut self, flags: OpFlags) { 31 25 *self |= flags; 32 26 } 33 27 34 - fn remove(&mut self, flags: OpFlags) { 28 + pub fn remove(&mut self, flags: OpFlags) { 35 29 *self &= !flags; 36 30 } 37 31 ··· 135 129 136 130 extern crate std; 137 131 132 + pub const INBOUND: OpFlags = OpFlags(1 << 0); 133 + pub const APP: OpFlags = OpFlags(1 << 1); 134 + pub const CIPHER: OpFlags = OpFlags(1 << 2); 135 + pub const TRANSPORT: OpFlags = OpFlags(1 << 3); 136 + pub const META: OpFlags = OpFlags(1 << 4); 137 + pub const KEYTREE: OpFlags = OpFlags(1 << 5); 138 + 138 139 #[test] 139 140 fn debug_shows_toggled_bits() { 140 - let test_case = OpFlags::INBOUND | OpFlags::APP | OpFlags::META; 141 + let test_case = INBOUND | APP | META; 141 142 142 143 let debug = std::format!("{test_case:?}"); 143 144 ··· 146 147 147 148 #[test] 148 149 fn contains_works() { 149 - assert!(!bool::from(OpFlags::EMPTY.contains(OpFlags::INBOUND))); 150 - assert!(bool::from(OpFlags(0b110).contains(OpFlags::CIPHER))); 151 - assert!(bool::from(OpFlags(0b110).contains(OpFlags::APP))); 152 - assert!(bool::from( 153 - OpFlags(0b110).contains(OpFlags::CIPHER | OpFlags::APP) 154 - )); 155 - assert!(!bool::from( 156 - OpFlags(0b100).contains(OpFlags::CIPHER | OpFlags::APP) 157 - )); 158 - assert!(!bool::from(OpFlags(0b110).contains(OpFlags::INBOUND))); 150 + assert!(!bool::from(OpFlags::EMPTY.contains(INBOUND))); 151 + assert!(bool::from(OpFlags(0b110).contains(CIPHER))); 152 + assert!(bool::from(OpFlags(0b110).contains(APP))); 153 + assert!(bool::from(OpFlags(0b110).contains(CIPHER | APP))); 154 + assert!(!bool::from(OpFlags(0b100).contains(CIPHER | APP))); 155 + assert!(!bool::from(OpFlags(0b110).contains(INBOUND))); 159 156 } 160 157 161 158 #[test] 162 159 fn intersects_works() { 163 - let intersection = OpFlags::CIPHER | OpFlags::KEYTREE; 160 + let intersection = CIPHER | KEYTREE; 164 161 165 - let test_1 = OpFlags::CIPHER | OpFlags::TRANSPORT; 166 - let test_2 = OpFlags::KEYTREE | OpFlags::META; 167 - let test_3 = OpFlags::APP | OpFlags::TRANSPORT; 162 + let test_1 = CIPHER | TRANSPORT; 163 + let test_2 = KEYTREE | META; 164 + let test_3 = APP | TRANSPORT; 168 165 169 166 assert!(bool::from(test_1.intersects(intersection))); 170 167 assert_eq!( 171 168 bool::from(test_1.intersects(intersection)), 172 - bool::from(test_1.contains(OpFlags::KEYTREE)) 173 - || bool::from(test_1.contains(OpFlags::CIPHER)) 169 + bool::from(test_1.contains(KEYTREE)) || bool::from(test_1.contains(CIPHER)) 174 170 ); 175 171 176 172 assert!(bool::from(test_2.intersects(intersection))); ··· 178 174 assert!(!bool::from(test_3.intersects(intersection))); 179 175 assert_eq!( 180 176 bool::from(test_3.intersects(intersection)), 181 - bool::from(test_3.contains(OpFlags::KEYTREE)) 182 - || bool::from(test_3.contains(OpFlags::CIPHER)) 177 + bool::from(test_3.contains(KEYTREE)) || bool::from(test_3.contains(CIPHER)) 183 178 ); 184 179 } 185 180 186 181 #[test] 187 182 fn set_works() { 188 - let mut test_case = OpFlags::INBOUND | OpFlags::APP | OpFlags::META; 183 + let mut test_case = INBOUND | APP | META; 189 184 190 - test_case.set(OpFlags::META, Choice::from(0)); 185 + test_case.set(META, Choice::from(0)); 191 186 192 - assert!(!bool::from(test_case.contains(OpFlags::META))); 187 + assert!(!bool::from(test_case.contains(META))); 193 188 194 - assert_eq!(test_case, OpFlags::INBOUND | OpFlags::APP); 189 + assert_eq!(test_case, INBOUND | APP); 195 190 196 - test_case.set(OpFlags::META, Choice::from(1)); 191 + test_case.set(META, Choice::from(1)); 197 192 198 - assert!(bool::from(test_case.contains(OpFlags::META))); 193 + assert!(bool::from(test_case.contains(META))); 199 194 } 200 195 }
+28 -21
wharrgarbl-strobe/src/ops.rs
··· 1 - use crate::opflags::OpFlags; 1 + use wharrgarbl_utils::OpFlags; 2 + 3 + pub const INBOUND: OpFlags = OpFlags::new(1 << 0); 4 + pub const APP: OpFlags = OpFlags::new(1 << 1); 5 + pub const CIPHER: OpFlags = OpFlags::new(1 << 2); 6 + pub const TRANSPORT: OpFlags = OpFlags::new(1 << 3); 7 + pub const META: OpFlags = OpFlags::new(1 << 4); 8 + pub const KEYTREE: OpFlags = OpFlags::new(1 << 5); 2 9 3 10 macro_rules! define_ops { 4 11 { ··· 13 20 } 14 21 15 22 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()); 23 + SEND_ENC(APP.bits() | CIPHER.bits() | TRANSPORT.bits()); 24 + META_SEND_ENC(APP.bits() | CIPHER.bits() | TRANSPORT.bits() | META.bits()); 25 + RECV_ENC(INBOUND.bits() | APP.bits() | CIPHER.bits() | TRANSPORT.bits()); 26 + META_RECV_ENC(INBOUND.bits() | APP.bits() | CIPHER.bits() | TRANSPORT.bits() | META.bits()); 20 27 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()); 28 + SEND_MAC(CIPHER.bits() | TRANSPORT.bits()); 29 + META_SEND_MAC(CIPHER.bits() | TRANSPORT.bits() | META.bits()); 30 + RECV_MAC(INBOUND.bits() | CIPHER.bits() | TRANSPORT.bits()); 31 + META_RECV_MAC(INBOUND.bits() | CIPHER.bits() | TRANSPORT.bits() | META.bits()); 25 32 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()); 33 + PRF(INBOUND.bits() | APP.bits() | CIPHER.bits()); 34 + META_PRF(INBOUND.bits() | APP.bits() | CIPHER.bits() | META.bits()); 28 35 29 - RATCHET(OpFlags::CIPHER.bits()); 30 - META_RATCHET(OpFlags::CIPHER.bits() | OpFlags::META.bits()); 36 + RATCHET(CIPHER.bits()); 37 + META_RATCHET(CIPHER.bits() | META.bits()); 31 38 32 - AD(OpFlags::APP.bits()); 33 - META_AD(OpFlags::APP.bits() | OpFlags::META.bits()); 39 + AD(APP.bits()); 40 + META_AD(APP.bits() | META.bits()); 34 41 35 - KEY(OpFlags::APP.bits() | OpFlags::CIPHER.bits()); 36 - META_KEY(OpFlags::APP.bits() | OpFlags::CIPHER.bits() | OpFlags::META.bits()); 42 + KEY(APP.bits() | CIPHER.bits()); 43 + META_KEY(APP.bits() | CIPHER.bits() | META.bits()); 37 44 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()); 45 + SEND_CLR(APP.bits() | TRANSPORT.bits()); 46 + META_SEND_CLR(APP.bits() | TRANSPORT.bits() | META.bits()); 47 + RECV_CLR(INBOUND.bits() | APP.bits() | TRANSPORT.bits()); 48 + META_RECV_CLR(INBOUND.bits() | APP.bits() | TRANSPORT.bits() | META.bits()); 42 49 }
+10 -10
wharrgarbl-strobe/src/strobe.rs
··· 2 2 3 3 use ctutils::{Choice, CtAssign, CtEq, CtLt, CtSelect}; 4 4 use hybrid_array::{Array, ArraySize}; 5 + use wharrgarbl_utils::OpFlags; 5 6 use zeroize::Zeroize; 6 7 7 8 use crate::{ 8 9 STROBE_VERSION, StrobeRole, 9 10 keccakf::{KECCAK_BUFFER_SIZE, KeccakF1600}, 10 - opflags::OpFlags, 11 11 ops, 12 12 traits::StrobeSecurity, 13 13 }; ··· 273 273 /// sending or receiving 274 274 #[inline] 275 275 fn begin_op(&mut self, mut flags: OpFlags) { 276 - if flags.contains(OpFlags::TRANSPORT).to_bool() { 277 - let op_role = role::SENDER.ct_select(&role::RECEIVER, flags.contains(OpFlags::INBOUND)); 276 + if flags.contains(ops::TRANSPORT).to_bool() { 277 + let op_role = role::SENDER.ct_select(&role::RECEIVER, flags.contains(ops::INBOUND)); 278 278 279 279 // So that the sender and receiver agree, toggle the I flag as necessary 280 - flags.set(OpFlags::INBOUND, self.role.ct_ne(&op_role)); 280 + flags.set(ops::INBOUND, self.role.ct_ne(&op_role)); 281 281 } 282 282 283 283 let old_start = self.start; ··· 286 286 // Mix in the position and flags 287 287 self.absorb(&[old_start as u8, flags.bits()]); 288 288 289 - let mut force_permutation = flags.intersects(OpFlags::CIPHER | OpFlags::KEYTREE); 289 + let mut force_permutation = flags.intersects(ops::CIPHER | ops::KEYTREE); 290 290 force_permutation &= self.position.ct_ne(&0); 291 291 292 292 if force_permutation.to_bool() { ··· 307 307 } 308 308 309 309 // Meta-ness is only relevant for `begin_op`. Remove it to simplify the below logic. 310 - flags &= !OpFlags::META; 310 + flags &= !ops::META; 311 311 312 312 // Flags that don't pass this assertion should normally call `absorb`, but `absorb` does not mutate, 313 313 // so the implementor should have used operate_no_mutate instead 314 314 // RATCHET is special-cased to never call operate directly 315 - debug_assert!(flags != ops::KEY && flags.contains(OpFlags::CIPHER).to_bool()); 315 + debug_assert!(flags != ops::KEY && flags.contains(ops::CIPHER).to_bool()); 316 316 317 317 static SPECIAL_CASES: [OpFlags; 3] = [ops::PRF, ops::SEND_MAC, ops::SEND_ENC]; 318 318 let ops: [fn(&mut Self, data: &mut [u8]); 4] = [ ··· 349 349 } 350 350 351 351 // Meta-ness is only relevant for `begin_op`. Remove it to simplify the below logic. 352 - flags &= !OpFlags::META; 352 + flags &= !ops::META; 353 353 354 354 // Flags that trigger the assertion to fail are mutating operations. 355 355 // RATCHET is special cased to never call operate/operate_no_mutate directly 356 - debug_assert!(flags == ops::KEY || !flags.contains(OpFlags::CIPHER).to_bool()); 356 + debug_assert!(flags == ops::KEY || !flags.contains(ops::CIPHER).to_bool()); 357 357 358 358 let ops: [fn(&mut Self, data: &[u8]); 2] = [StrobeState::absorb, StrobeState::overwrite]; 359 359 ··· 403 403 self.begin_op(flags); 404 404 } 405 405 406 - flags &= !OpFlags::META; 406 + flags &= !ops::META; 407 407 408 408 self.zero_state(num_bytes_to_zero); 409 409 }
+2
wharrgarbl-utils/Cargo.toml
··· 9 9 10 10 [dependencies] 11 11 aead.workspace = true 12 + zeroize.workspace = true 13 + ctutils.workspace = true
+112
wharrgarbl-utils/src/buffer_slice.rs
··· 1 + #[derive(Debug)] 2 + pub struct BufferSlice<'slice> { 3 + buffer: &'slice mut [u8], 4 + end: usize, 5 + } 6 + 7 + impl<'slice> BufferSlice<'slice> { 8 + pub const fn new(buffer: &'slice mut [u8]) -> Self { 9 + Self { end: 0, buffer } 10 + } 11 + 12 + #[inline] 13 + pub const fn fill(&mut self) { 14 + self.end = self.buffer.len(); 15 + } 16 + } 17 + 18 + impl AsRef<[u8]> for BufferSlice<'_> { 19 + #[inline] 20 + fn as_ref(&self) -> &[u8] { 21 + &self.buffer[..self.end] 22 + } 23 + } 24 + 25 + impl AsMut<[u8]> for BufferSlice<'_> { 26 + #[inline] 27 + fn as_mut(&mut self) -> &mut [u8] { 28 + &mut self.buffer[..self.end] 29 + } 30 + } 31 + 32 + impl aead::Buffer for BufferSlice<'_> { 33 + #[inline] 34 + fn extend_from_slice(&mut self, other: &[u8]) -> aead::Result<()> { 35 + let index = self.end + other.len(); 36 + 37 + if index > self.buffer.len() { 38 + return Err(aead::Error); 39 + } 40 + 41 + self.buffer[self.end..index].copy_from_slice(other); 42 + 43 + self.end = index; 44 + 45 + Ok(()) 46 + } 47 + 48 + #[inline] 49 + fn truncate(&mut self, len: usize) { 50 + self.end = len.min(self.end); 51 + } 52 + } 53 + 54 + #[cfg(test)] 55 + mod tests { 56 + use super::*; 57 + use aead::Buffer; 58 + 59 + extern crate alloc; 60 + 61 + #[test] 62 + fn defaults_to_empty_buffer_size() { 63 + let mut buf = alloc::vec![0u8; 128]; 64 + 65 + let buf_slice = BufferSlice::new(&mut buf); 66 + 67 + assert_eq!(buf_slice.len(), 0); 68 + } 69 + 70 + #[test] 71 + fn truncate_doesnt_extend_past_max_buffer_length() { 72 + let mut buf = alloc::vec![0u8; 128]; 73 + 74 + let mut buf_slice = BufferSlice::new(&mut buf); 75 + 76 + buf_slice.extend_from_slice(&[0; 70]).unwrap(); 77 + 78 + buf_slice.truncate(64); 79 + 80 + assert_eq!(buf_slice.len(), 64); 81 + 82 + buf_slice.truncate(256); 83 + 84 + assert_eq!(buf_slice.len(), 64); 85 + } 86 + 87 + #[test] 88 + fn extend_from_slice_only_works_within_slice_length() { 89 + let mut buf = alloc::vec![0u8; 128]; 90 + 91 + let mut buf_slice = BufferSlice::new(&mut buf); 92 + 93 + assert_eq!(buf_slice.len(), 0); 94 + assert_eq!(buf_slice.extend_from_slice(&[0; 129]), Err(aead::Error)); 95 + 96 + assert_eq!(buf_slice.extend_from_slice(&[0, 0, 0, 0, 0, 0]), Ok(())); 97 + assert_eq!(buf_slice.len(), 6); 98 + } 99 + 100 + #[test] 101 + fn fill_sets_length_to_buffer_max_length() { 102 + let mut buf = alloc::vec![0u8; 128]; 103 + 104 + let mut buf_slice = BufferSlice::new(&mut buf); 105 + 106 + assert_eq!(buf_slice.len(), 0); 107 + 108 + buf_slice.fill(); 109 + 110 + assert_eq!(buf_slice.len(), 128); 111 + } 112 + }
+5 -105
wharrgarbl-utils/src/lib.rs
··· 1 1 #![no_std] 2 2 #![forbid(unsafe_code)] 3 3 4 - #[derive(Debug)] 5 - pub struct BufferSlice<'slice> { 6 - buffer: &'slice mut [u8], 7 - end: usize, 8 - } 9 - 10 - impl<'slice> BufferSlice<'slice> { 11 - pub const fn new(buffer: &'slice mut [u8]) -> Self { 12 - Self { end: 0, buffer } 13 - } 14 - 15 - pub const fn fill(&mut self) { 16 - self.end = self.buffer.len(); 17 - } 18 - } 19 - 20 - impl AsRef<[u8]> for BufferSlice<'_> { 21 - fn as_ref(&self) -> &[u8] { 22 - &self.buffer[..self.end] 23 - } 24 - } 25 - 26 - impl AsMut<[u8]> for BufferSlice<'_> { 27 - fn as_mut(&mut self) -> &mut [u8] { 28 - &mut self.buffer[..self.end] 29 - } 30 - } 31 - 32 - impl aead::Buffer for BufferSlice<'_> { 33 - fn extend_from_slice(&mut self, other: &[u8]) -> aead::Result<()> { 34 - let index = self.end + other.len(); 35 - 36 - if index > self.buffer.len() { 37 - return Err(aead::Error); 38 - } 39 - 40 - self.buffer[self.end..index].copy_from_slice(other); 41 - 42 - self.end = index; 43 - 44 - Ok(()) 45 - } 46 - 47 - fn truncate(&mut self, len: usize) { 48 - self.end = len.min(self.end); 49 - } 50 - } 51 - 52 - #[cfg(test)] 53 - mod tests { 54 - use super::*; 55 - use aead::Buffer; 56 - 57 - extern crate alloc; 58 - 59 - #[test] 60 - fn defaults_to_empty_buffer_size() { 61 - let mut buf = alloc::vec![0u8; 128]; 62 - 63 - let buf_slice = BufferSlice::new(&mut buf); 64 - 65 - assert_eq!(buf_slice.len(), 0); 66 - } 67 - 68 - #[test] 69 - fn truncate_doesnt_extend_past_max_buffer_length() { 70 - let mut buf = alloc::vec![0u8; 128]; 71 - 72 - let mut buf_slice = BufferSlice::new(&mut buf); 73 - 74 - buf_slice.extend_from_slice(&[0; 70]).unwrap(); 75 - 76 - buf_slice.truncate(64); 77 - 78 - assert_eq!(buf_slice.len(), 64); 4 + mod buffer_slice; 5 + mod opflags; 79 6 80 - buf_slice.truncate(256); 7 + pub use aead::Buffer; 81 8 82 - assert_eq!(buf_slice.len(), 64); 83 - } 84 - 85 - #[test] 86 - fn extend_from_slice_only_works_within_slice_length() { 87 - let mut buf = alloc::vec![0u8; 128]; 88 - 89 - let mut buf_slice = BufferSlice::new(&mut buf); 90 - 91 - assert_eq!(buf_slice.len(), 0); 92 - assert_eq!(buf_slice.extend_from_slice(&[0; 129]), Err(aead::Error)); 93 - 94 - assert_eq!(buf_slice.extend_from_slice(&[0, 0, 0, 0, 0, 0]), Ok(())); 95 - assert_eq!(buf_slice.len(), 6); 96 - } 97 - 98 - #[test] 99 - fn fill_sets_length_to_buffer_max_length() { 100 - let mut buf = alloc::vec![0u8; 128]; 101 - 102 - let mut buf_slice = BufferSlice::new(&mut buf); 103 - 104 - assert_eq!(buf_slice.len(), 0); 105 - 106 - buf_slice.fill(); 107 - 108 - assert_eq!(buf_slice.len(), 128); 109 - } 110 - } 9 + pub use buffer_slice::BufferSlice; 10 + pub use opflags::OpFlags;