A whimsical STROBE based encryption protocol
2
fork

Configure Feed

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

Initial commit

Sachymetsu e22bbfeb

+1152
+1
.gitignore
··· 1 + /target
+184
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "cfg-if" 7 + version = "1.0.4" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 10 + 11 + [[package]] 12 + name = "cpufeatures" 13 + version = "0.3.0" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" 16 + dependencies = [ 17 + "libc", 18 + ] 19 + 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 + name = "hex" 42 + version = "0.4.3" 43 + source = "registry+https://github.com/rust-lang/crates.io-index" 44 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 45 + 46 + [[package]] 47 + name = "itoa" 48 + version = "1.0.18" 49 + source = "registry+https://github.com/rust-lang/crates.io-index" 50 + checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" 51 + 52 + [[package]] 53 + name = "keccak" 54 + version = "0.2.0" 55 + source = "registry+https://github.com/rust-lang/crates.io-index" 56 + checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" 57 + dependencies = [ 58 + "cfg-if", 59 + "cpufeatures", 60 + ] 61 + 62 + [[package]] 63 + name = "libc" 64 + version = "0.2.184" 65 + source = "registry+https://github.com/rust-lang/crates.io-index" 66 + checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" 67 + 68 + [[package]] 69 + name = "memchr" 70 + version = "2.8.0" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 73 + 74 + [[package]] 75 + name = "proc-macro2" 76 + version = "1.0.106" 77 + source = "registry+https://github.com/rust-lang/crates.io-index" 78 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 79 + dependencies = [ 80 + "unicode-ident", 81 + ] 82 + 83 + [[package]] 84 + name = "quote" 85 + version = "1.0.45" 86 + source = "registry+https://github.com/rust-lang/crates.io-index" 87 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 88 + dependencies = [ 89 + "proc-macro2", 90 + ] 91 + 92 + [[package]] 93 + name = "serde" 94 + version = "1.0.228" 95 + source = "registry+https://github.com/rust-lang/crates.io-index" 96 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 97 + dependencies = [ 98 + "serde_core", 99 + "serde_derive", 100 + ] 101 + 102 + [[package]] 103 + name = "serde-big-array" 104 + version = "0.5.1" 105 + source = "registry+https://github.com/rust-lang/crates.io-index" 106 + checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" 107 + dependencies = [ 108 + "serde", 109 + ] 110 + 111 + [[package]] 112 + name = "serde_core" 113 + version = "1.0.228" 114 + source = "registry+https://github.com/rust-lang/crates.io-index" 115 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 116 + dependencies = [ 117 + "serde_derive", 118 + ] 119 + 120 + [[package]] 121 + name = "serde_derive" 122 + version = "1.0.228" 123 + source = "registry+https://github.com/rust-lang/crates.io-index" 124 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 125 + dependencies = [ 126 + "proc-macro2", 127 + "quote", 128 + "syn", 129 + ] 130 + 131 + [[package]] 132 + name = "serde_json" 133 + version = "1.0.149" 134 + source = "registry+https://github.com/rust-lang/crates.io-index" 135 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 136 + dependencies = [ 137 + "itoa", 138 + "memchr", 139 + "serde", 140 + "serde_core", 141 + "zmij", 142 + ] 143 + 144 + [[package]] 145 + name = "subtle" 146 + version = "2.6.1" 147 + source = "registry+https://github.com/rust-lang/crates.io-index" 148 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 149 + 150 + [[package]] 151 + name = "syn" 152 + version = "2.0.117" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" 155 + dependencies = [ 156 + "proc-macro2", 157 + "quote", 158 + "unicode-ident", 159 + ] 160 + 161 + [[package]] 162 + name = "unicode-ident" 163 + version = "1.0.24" 164 + source = "registry+https://github.com/rust-lang/crates.io-index" 165 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 166 + 167 + [[package]] 168 + name = "wharrgarbl" 169 + version = "0.1.0" 170 + dependencies = [ 171 + "enumflags2", 172 + "hex", 173 + "keccak", 174 + "serde", 175 + "serde-big-array", 176 + "serde_json", 177 + "subtle", 178 + ] 179 + 180 + [[package]] 181 + name = "zmij" 182 + version = "1.0.21" 183 + source = "registry+https://github.com/rust-lang/crates.io-index" 184 + checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+18
Cargo.toml
··· 1 + [package] 2 + name = "wharrgarbl" 3 + description = "A whimsical STROBE based encryption protocol" 4 + authors = ["Sachy.dev <sachymetsu@tutamail.com>"] 5 + repository = "https://tangled.org/sachy.dev/wharrgarbl" 6 + version = "0.1.0" 7 + edition = "2024" 8 + 9 + [dependencies] 10 + keccak = "0.2" 11 + enumflags2 = "0.7.12" 12 + subtle = { version = "2.6", default-features = false } 13 + 14 + [dev-dependencies] 15 + serde_json = "1" 16 + hex = "0.4" 17 + serde = { version = "1.0.210", default-features = false, features = ["derive"] } 18 + serde-big-array = { version = "0.5" }
+7
README.md
··· 1 + # WHARRGARBL 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. 4 + 5 + ## License 6 + 7 + TBA when ready
+314
src/basic_kats.rs
··· 1 + //! ## KAT, YA BASIC 2 + //! 3 + //! This module is for the initial set of KATs (Known Answer Tests) that are hard-coded. 4 + //! Some tests in the original repo are omitted because we can no longer panic from this 5 + //! implementation's public API surface. 6 + 7 + use crate::{ 8 + keccakf::KECCAK_BUFFER_SIZE, 9 + strobe::{SecurityParameter, StrobeState}, 10 + }; 11 + 12 + extern crate std; 13 + 14 + #[test] 15 + fn test_init_128() { 16 + let s = StrobeState::new(b"", SecurityParameter::B128); 17 + 18 + let expected_st: [u8; KECCAK_BUFFER_SIZE] = [ 19 + 0x9c, 0x7f, 0x16, 0x8f, 0xf8, 0xfd, 0x55, 0xda, 0x2a, 0xa7, 0x3c, 0x23, 0x55, 0x65, 0x35, 20 + 0x63, 0xdc, 0x0c, 0x47, 0x5c, 0x55, 0x15, 0x26, 0xf6, 0x73, 0x3b, 0xea, 0x22, 0xf1, 0x6c, 21 + 0xb5, 0x7c, 0xd3, 0x1f, 0x68, 0x2e, 0x66, 0x0e, 0xe9, 0x12, 0x82, 0x4a, 0x77, 0x22, 0x01, 22 + 0xee, 0x13, 0x94, 0x22, 0x6f, 0x4a, 0xfc, 0xb6, 0x2d, 0x33, 0x12, 0x93, 0xcc, 0x92, 0xe8, 23 + 0xa6, 0x24, 0xac, 0xf6, 0xe1, 0xb6, 0x00, 0x95, 0xe3, 0x22, 0xbb, 0xfb, 0xc8, 0x45, 0xe5, 24 + 0xb2, 0x69, 0x95, 0xfe, 0x7d, 0x7c, 0x84, 0x13, 0x74, 0xd1, 0xff, 0x58, 0x98, 0xc9, 0x2e, 25 + 0xe0, 0x63, 0x6b, 0x06, 0x72, 0x73, 0x21, 0xc9, 0x2a, 0x60, 0x39, 0x07, 0x03, 0x53, 0x49, 26 + 0xcc, 0xbb, 0x1b, 0x92, 0xb7, 0xb0, 0x05, 0x7e, 0x8f, 0xa8, 0x7f, 0xce, 0xbc, 0x7e, 0x88, 27 + 0x65, 0x6f, 0xcb, 0x45, 0xae, 0x04, 0xbc, 0x34, 0xca, 0xbe, 0xae, 0xbe, 0x79, 0xd9, 0x17, 28 + 0x50, 0xc0, 0xe8, 0xbf, 0x13, 0xb9, 0x66, 0x50, 0x4d, 0x13, 0x43, 0x59, 0x72, 0x65, 0xdd, 29 + 0x88, 0x65, 0xad, 0xf9, 0x14, 0x09, 0xcc, 0x9b, 0x20, 0xd5, 0xf4, 0x74, 0x44, 0x04, 0x1f, 30 + 0x97, 0xb6, 0x99, 0xdd, 0xfb, 0xde, 0xe9, 0x1e, 0xa8, 0x7b, 0xd0, 0x9b, 0xf8, 0xb0, 0x2d, 31 + 0xa7, 0x5a, 0x96, 0xe9, 0x47, 0xf0, 0x7f, 0x5b, 0x65, 0xbb, 0x4e, 0x6e, 0xfe, 0xfa, 0xa1, 32 + 0x6a, 0xbf, 0xd9, 0xfb, 0xf6, 33 + ]; 34 + 35 + assert_eq!(&s.state.0, &expected_st); 36 + } 37 + 38 + #[test] 39 + fn test_init_256() { 40 + let s = StrobeState::new(b"", SecurityParameter::B256); 41 + 42 + let expected_st: [u8; KECCAK_BUFFER_SIZE] = [ 43 + 0x37, 0xc1, 0x15, 0x06, 0xed, 0x61, 0xe7, 0xda, 0x7c, 0x1a, 0x2f, 0x2c, 0x1f, 0x49, 0x74, 44 + 0xb0, 0x71, 0x66, 0xc2, 0xea, 0x7f, 0x62, 0xec, 0xa6, 0xe0, 0x36, 0xc1, 0x6e, 0xae, 0x39, 45 + 0xb4, 0xdf, 0x3a, 0x06, 0x11, 0xf1, 0x36, 0xc7, 0x33, 0x94, 0x31, 0x13, 0x2c, 0xdb, 0x18, 46 + 0x03, 0x08, 0xc0, 0x53, 0x61, 0xab, 0xf7, 0xb9, 0xc6, 0x89, 0x49, 0xab, 0x1e, 0x5c, 0x0b, 47 + 0xbf, 0xab, 0x0a, 0xb0, 0x66, 0xa0, 0x13, 0x96, 0xdb, 0x8d, 0xb1, 0x26, 0x02, 0x0c, 0xf7, 48 + 0x96, 0xb2, 0x3f, 0x0e, 0xe1, 0xcf, 0x40, 0xda, 0x8f, 0x8b, 0xfc, 0x34, 0x27, 0x34, 0x14, 49 + 0x4a, 0x64, 0x08, 0x29, 0x44, 0x5a, 0x67, 0xab, 0x3e, 0x15, 0x46, 0xc0, 0x97, 0xe3, 0x23, 50 + 0xd3, 0xda, 0xe7, 0xc6, 0x2e, 0x62, 0xd3, 0xdd, 0xae, 0x90, 0x98, 0x31, 0xa1, 0x64, 0x9c, 51 + 0xd8, 0x07, 0x97, 0x7b, 0x5e, 0x44, 0x88, 0xae, 0x42, 0xfc, 0x36, 0xec, 0x2c, 0x5a, 0x78, 52 + 0x0d, 0x52, 0xa3, 0x22, 0xa6, 0xe9, 0xbe, 0xff, 0x73, 0x89, 0xcb, 0x8f, 0xe7, 0x6a, 0xb5, 53 + 0x5d, 0xc6, 0xa0, 0x60, 0xa7, 0x22, 0xb9, 0x64, 0xb6, 0xe8, 0xfe, 0x8b, 0xb5, 0xb9, 0x1a, 54 + 0x9b, 0xbc, 0x61, 0xc0, 0x86, 0x7e, 0x6d, 0xfc, 0x5b, 0x5c, 0x6d, 0xd5, 0xb5, 0xa7, 0x26, 55 + 0xc9, 0x18, 0xe4, 0x0b, 0xe9, 0xb1, 0xcf, 0xa7, 0xef, 0xa6, 0x92, 0xf5, 0x05, 0xdc, 0xac, 56 + 0xde, 0x80, 0x03, 0xe8, 0xbb, 57 + ]; 58 + 59 + assert_eq!(&s.state.0, &expected_st); 60 + } 61 + 62 + #[test] 63 + fn test_metadata() { 64 + // We will accumulate output over 3 operations and 3 meta-operations 65 + let mut s = StrobeState::new(b"metadatatest", SecurityParameter::B256); 66 + let mut output = std::vec::Vec::new(); 67 + 68 + let buf = b"meta1"; 69 + s.meta_send_clr(buf); 70 + output.extend_from_slice(buf); 71 + 72 + // This does not output anything 73 + s.key(b"key"); 74 + 75 + let mut buf = [0u8; 10]; 76 + s.meta_prf(&mut buf); 77 + output.extend_from_slice(&buf[..]); 78 + 79 + // We don't have to re-zero the buffer. Our internal special-casing for PRF does this for us 80 + s.prf(&mut buf); 81 + output.extend_from_slice(&buf[..]); 82 + 83 + let buf = b"meta3"; 84 + s.meta_send_clr(buf); 85 + output.extend(buf); 86 + 87 + let mut buf = b"pt".to_vec(); 88 + s.send_enc(buf.as_mut_slice()); 89 + output.extend(buf); 90 + 91 + let expected_output = [ 92 + 0x6d, 0x65, 0x74, 0x61, 0x31, 0xa7, 0xe5, 0x96, 0xe0, 0x8f, 0x39, 0x19, 0x3c, 0x4f, 0x84, 93 + 0xdb, 0x00, 0xbb, 0xce, 0xbb, 0xf3, 0x7e, 0xc6, 0x33, 0x8b, 0x6d, 0x65, 0x74, 0x61, 0x33, 94 + 0xe9, 0x0b, 95 + ]; 96 + let expected_st = [ 97 + 0xe9, 0x0b, 0x29, 0xad, 0x32, 0x0c, 0x27, 0x53, 0x07, 0x48, 0xcd, 0x38, 0xde, 0xf7, 0x23, 98 + 0xb0, 0x54, 0x21, 0x14, 0xae, 0xd1, 0xfc, 0x55, 0xd0, 0xc6, 0xc2, 0x58, 0x85, 0xaa, 0x5d, 99 + 0x30, 0x30, 0x88, 0xb9, 0x6b, 0x55, 0xf0, 0x01, 0xbd, 0x30, 0xc4, 0xd5, 0x00, 0x72, 0xad, 100 + 0x58, 0xad, 0x08, 0xbc, 0x0c, 0x7b, 0x8f, 0xad, 0xe5, 0x02, 0x57, 0xa9, 0xe4, 0xbe, 0xb0, 101 + 0x1a, 0x96, 0x44, 0xc6, 0x25, 0x9d, 0x58, 0x30, 0x85, 0xf4, 0xee, 0xe0, 0xdd, 0x32, 0x39, 102 + 0x18, 0x8d, 0x46, 0x02, 0xa2, 0x9a, 0x64, 0x3d, 0x7a, 0x4e, 0xd0, 0xaa, 0x57, 0xf1, 0x97, 103 + 0x9e, 0xb5, 0xca, 0x18, 0x6c, 0xd2, 0x2b, 0x4f, 0xb6, 0x78, 0x30, 0x2f, 0xe1, 0xb0, 0x34, 104 + 0x10, 0x21, 0xdc, 0xd6, 0xdd, 0x63, 0x2f, 0x14, 0x23, 0x41, 0x78, 0xe4, 0x98, 0x9c, 0x5c, 105 + 0x8a, 0xae, 0x00, 0x31, 0xb7, 0xa9, 0x90, 0x16, 0x91, 0x41, 0x38, 0x0a, 0xc4, 0xe2, 0x3f, 106 + 0x39, 0x4d, 0x5e, 0xc7, 0x58, 0x59, 0x5d, 0xe5, 0x31, 0xdc, 0x7c, 0x0b, 0x06, 0xca, 0x8b, 107 + 0x95, 0x75, 0x31, 0x70, 0x9a, 0xee, 0x42, 0xf3, 0x2c, 0xef, 0x88, 0x1c, 0x6e, 0xc9, 0x9b, 108 + 0x69, 0xe5, 0xaf, 0xfc, 0x30, 0x93, 0x51, 0x29, 0x17, 0x93, 0x46, 0x21, 0xc5, 0x6c, 0xc9, 109 + 0x95, 0xd4, 0x58, 0x7c, 0xeb, 0x5a, 0x9d, 0x90, 0xa6, 0x7d, 0x5d, 0x36, 0xf6, 0x3f, 0xa6, 110 + 0xde, 0xb2, 0x48, 0xe2, 0x88, 111 + ]; 112 + 113 + assert_eq!(&output, &expected_output); 114 + assert_eq!(&s.state.0, &expected_st[..]); 115 + } 116 + 117 + #[test] 118 + fn test_seq() { 119 + let mut s = StrobeState::new(b"seqtest", SecurityParameter::B256); 120 + 121 + let mut buf = [0u8; 10]; 122 + s.prf(&mut buf[..]); 123 + 124 + s.ad(b"Hello"); 125 + 126 + let mut buf = b"World".to_vec(); 127 + s.send_enc(buf.as_mut_slice()); 128 + 129 + s.send_clr(b"foo"); 130 + s.ratchet(32); 131 + s.recv_clr(b"bar"); 132 + 133 + let mut buf = b"baz".to_vec(); 134 + s.recv_enc(buf.as_mut_slice()); 135 + 136 + for i in 0..100 { 137 + let mut buf = std::vec![b'X'; i]; 138 + s.send_enc(buf.as_mut_slice()); 139 + // For this test, we must disable automatic streaming in order to have 140 + // the same resulting state as the reference, 141 + s.reset_ops(); 142 + } 143 + 144 + let mut buf = [0u8; 123]; 145 + s.prf(&mut buf[..]); 146 + 147 + let mut buf = [0u8; 16]; 148 + s.send_mac(&mut buf[..]); 149 + 150 + let final_st = &s.state.0; 151 + 152 + let expected_st = [ 153 + 0xdf, 0x7a, 0x38, 0x71, 0x06, 0xcc, 0x24, 0x82, 0x11, 0x31, 0x60, 0x43, 0xa9, 0xf0, 0xf5, 154 + 0xd0, 0x49, 0xc2, 0xce, 0xd3, 0x85, 0xfc, 0x9e, 0xa8, 0x0e, 0xc1, 0x46, 0xa4, 0xa1, 0x96, 155 + 0x02, 0x30, 0x78, 0xe6, 0x16, 0x62, 0x50, 0x1b, 0xab, 0x23, 0x5d, 0xcb, 0x85, 0x34, 0x3a, 156 + 0x67, 0xc6, 0x6c, 0xd8, 0x79, 0x45, 0xee, 0x2b, 0xaa, 0xc0, 0x09, 0x45, 0xc7, 0xf6, 0x42, 157 + 0xd9, 0xbc, 0x43, 0xe1, 0xd5, 0x2c, 0x6e, 0x71, 0x6f, 0xfa, 0x9a, 0x39, 0x9d, 0x11, 0xfd, 158 + 0x62, 0xfb, 0x15, 0x04, 0x85, 0xf9, 0xe3, 0xc1, 0x24, 0x95, 0x04, 0x84, 0x95, 0x3c, 0x74, 159 + 0x38, 0x3d, 0x5e, 0x08, 0x87, 0x64, 0xa3, 0x57, 0xdd, 0xb0, 0x40, 0x5b, 0x40, 0x25, 0x93, 160 + 0xb8, 0x3a, 0x75, 0x1d, 0xb7, 0xdf, 0xc4, 0x34, 0x4d, 0xfa, 0x94, 0xc6, 0x98, 0x13, 0xb3, 161 + 0x75, 0xf2, 0xdc, 0xd0, 0xe3, 0xe9, 0x44, 0xba, 0xfd, 0x98, 0x13, 0xc1, 0x59, 0xc7, 0x46, 162 + 0xa7, 0xb0, 0x65, 0x70, 0x20, 0x3d, 0x56, 0xeb, 0x84, 0x18, 0x1c, 0xca, 0x5b, 0x7a, 0xe4, 163 + 0xad, 0x3a, 0x57, 0x6b, 0x40, 0x80, 0x29, 0x0c, 0x63, 0x11, 0xd8, 0x6f, 0x89, 0xb8, 0x32, 164 + 0xf0, 0xb1, 0xde, 0x8c, 0x0a, 0x4f, 0x00, 0x90, 0x16, 0x0d, 0xc1, 0x9f, 0xd4, 0x69, 0x9c, 165 + 0x56, 0xb1, 0xd8, 0x9e, 0xc0, 0x8d, 0x40, 0x7a, 0x36, 0xe3, 0xb3, 0x9c, 0xd4, 0x91, 0x17, 166 + 0xd7, 0xed, 0x4c, 0x4b, 0xa5, 167 + ]; 168 + 169 + assert_eq!(final_st, &expected_st); 170 + } 171 + 172 + #[test] 173 + fn test_enc_correctness() { 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); 177 + 178 + tx.key(b"the-combination-on-my-luggage"); 179 + rx.key(b"the-combination-on-my-luggage"); 180 + 181 + // Encrypt and decrypt the original message 182 + let mut buf = orig_msg.to_vec(); 183 + tx.send_enc(buf.as_mut_slice()); 184 + rx.recv_enc(buf.as_mut_slice()); 185 + 186 + assert_eq!(orig_msg, buf.as_slice()); 187 + } 188 + 189 + #[test] 190 + 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); 193 + 194 + // Just do some stuff with the state 195 + 196 + tx.key(b"secretsauce"); 197 + let mut msg = b"attack at dawn".to_vec(); 198 + tx.send_enc(msg.as_mut_slice()); 199 + 200 + let mut mac = [0u8; 16]; 201 + tx.send_mac(&mut mac[..]); 202 + 203 + rx.key(b"secretsauce"); 204 + rx.recv_enc(&mut msg[..]); 205 + 206 + // Test that valid MACs are accepted 207 + let mut rx_copy = rx.clone(); 208 + let good_res = rx_copy.recv_mac(&mac); 209 + assert!(good_res.is_ok()); 210 + 211 + // Test that invalid MACs are rejected. Flip a bit 212 + let mut bad_mac = mac; 213 + bad_mac[0] ^= 1; 214 + let bad_res = rx.recv_mac(&bad_mac); 215 + assert!(bad_res.is_err()); 216 + } 217 + 218 + #[test] 219 + fn test_long_inputs() { 220 + let mut s = StrobeState::new(b"bigtest", SecurityParameter::B256); 221 + const BIG_N: usize = 9823; 222 + const SMALL_N: usize = 65; 223 + let big_data = [0x34u8; BIG_N]; 224 + let small_data = [0x35u8; SMALL_N]; 225 + 226 + s.meta_ad(&big_data[..]); 227 + s.ad(&big_data[..]); 228 + s.meta_key(&big_data[..]); 229 + s.key(&big_data[..]); 230 + s.meta_send_clr(&big_data[..]); 231 + s.send_clr(&big_data[..]); 232 + s.meta_recv_clr(&big_data[..]); 233 + s.recv_clr(&big_data[..]); 234 + 235 + s.meta_send_enc(big_data.to_vec().as_mut_slice()); 236 + s.send_enc(big_data.to_vec().as_mut_slice()); 237 + s.meta_recv_enc(big_data.to_vec().as_mut_slice()); 238 + s.recv_enc(big_data.to_vec().as_mut_slice()); 239 + let _ = s.meta_recv_mac(&small_data); 240 + let _ = s.recv_mac(&small_data); 241 + 242 + let mut big_buf = [0u8; BIG_N]; 243 + let mut small_buf = [0u8; SMALL_N]; 244 + 245 + s.meta_ratchet(BIG_N); 246 + s.ratchet(BIG_N); 247 + s.meta_prf(&mut big_buf); 248 + s.prf(&mut big_buf); 249 + s.meta_send_mac(&mut small_buf); 250 + s.send_mac(&mut small_buf); 251 + 252 + let expected_st = [ 253 + 0x96, 0x2f, 0xc8, 0x05, 0x48, 0xcd, 0xa3, 0xa8, 0xdf, 0xe7, 0x64, 0xd9, 0xe9, 0x81, 0xe0, 254 + 0x66, 0xdf, 0x2e, 0xd9, 0xc1, 0x74, 0x14, 0xac, 0xf0, 0x96, 0xe2, 0x96, 0xb2, 0x1f, 0x59, 255 + 0x79, 0x58, 0x82, 0xd5, 0x72, 0x22, 0x92, 0xc2, 0xd9, 0x92, 0x6b, 0xa2, 0x3d, 0x0b, 0x2d, 256 + 0xae, 0xf1, 0xff, 0x36, 0xbe, 0xfd, 0x19, 0xdf, 0xd8, 0x47, 0xcb, 0xf4, 0x35, 0x4c, 0x7f, 257 + 0x28, 0x58, 0x8d, 0x6f, 0x4d, 0x07, 0xc7, 0xbd, 0xbd, 0x6b, 0xe8, 0x45, 0xea, 0x56, 0x1e, 258 + 0xe1, 0x8c, 0x21, 0x20, 0x8c, 0x50, 0x1e, 0x0d, 0x75, 0x80, 0xd4, 0x00, 0xcd, 0x48, 0xe7, 259 + 0xf6, 0x23, 0x7b, 0x11, 0x85, 0x10, 0x53, 0x87, 0x28, 0x36, 0x14, 0x11, 0x28, 0x37, 0x1b, 260 + 0x0a, 0xfd, 0x9f, 0x21, 0x72, 0xff, 0x27, 0x4a, 0xc2, 0x7b, 0xfd, 0x86, 0x6d, 0xff, 0x4e, 261 + 0x07, 0x04, 0x2d, 0xd5, 0x3a, 0xbe, 0xeb, 0x43, 0x39, 0xbf, 0x20, 0xf0, 0x28, 0x31, 0x7b, 262 + 0xc2, 0x3f, 0x1b, 0x9b, 0x6d, 0x94, 0x84, 0x92, 0x13, 0x01, 0xf0, 0x04, 0xc7, 0xf3, 0xaa, 263 + 0x68, 0xc5, 0x4a, 0x9b, 0x26, 0x98, 0x37, 0x81, 0x99, 0xcf, 0xe7, 0x2c, 0xa2, 0xd9, 0x35, 264 + 0x0d, 0xac, 0x01, 0xe3, 0x09, 0xa9, 0x3a, 0x8c, 0x1c, 0xeb, 0x9d, 0x4d, 0xb4, 0xc7, 0x0a, 265 + 0x72, 0xf7, 0x14, 0x1a, 0xc5, 0x90, 0xdf, 0x47, 0xab, 0xa3, 0x83, 0xbd, 0x6c, 0xfe, 0xdf, 266 + 0x9b, 0xee, 0x8d, 0xcb, 0xe0, 267 + ]; 268 + 269 + assert_eq!(&s.state.0, &expected_st); 270 + } 271 + 272 + #[test] 273 + fn test_streaming_correctness() { 274 + // Compute a few things without breaking up their inputs 275 + let one_shot_st: std::vec::Vec<u8> = { 276 + let mut s = StrobeState::new(b"streamingtest", SecurityParameter::B256); 277 + 278 + s.ad(b"mynonce"); 279 + 280 + let mut buf = b"hello there".to_vec(); 281 + s.recv_enc(buf.as_mut_slice()); 282 + 283 + let mut mac = [0u8; 16]; 284 + s.send_mac(&mut mac[..]); 285 + 286 + s.ratchet(13); 287 + 288 + s.state.0.to_vec() 289 + }; 290 + // Now do the same thing but stream the inputs 291 + let streamed_st: std::vec::Vec<u8> = { 292 + let mut s = StrobeState::new(b"streamingtest", SecurityParameter::B256); 293 + 294 + s.ad(b"my"); 295 + s.ad(b"nonce"); 296 + 297 + let mut buf = b"hello".to_vec(); 298 + s.recv_enc(buf.as_mut_slice()); 299 + 300 + let mut buf = b" there".to_vec(); 301 + s.recv_enc(buf.as_mut_slice()); 302 + 303 + let mut mac = [0u8; 16]; 304 + s.send_mac(&mut mac[..10]); 305 + s.send_mac(&mut mac[10..]); 306 + 307 + s.ratchet(10); 308 + s.ratchet(3); 309 + 310 + s.state.0.to_vec() 311 + }; 312 + 313 + assert_eq!(one_shot_st, streamed_st); 314 + }
+4
src/herd_of_kats/mod.rs
··· 1 + //! ## Herding KATs 2 + //! 3 + //! This module contains a bigger test suite to evaluate the implementation of `StrobeState` 4 + //! fully.
+130
src/keccakf.rs
··· 1 + const SIZE: usize = core::mem::size_of::<u64>(); 2 + 3 + /// keccak block size in 64-bit words. This is the N parameter in the STROBE spec 4 + pub(crate) const KECCAK_BLOCK_SIZE: usize = 25; 5 + 6 + /// keccak buffer size in bytes. N * 8 (byte size of u64) 7 + pub(crate) const KECCAK_BUFFER_SIZE: usize = SIZE * KECCAK_BLOCK_SIZE; 8 + 9 + /// This is a wrapper around 200-byte buffer that's always 8-byte aligned to make pointers to it 10 + /// safely convertible to a pointer to `[u64; 25]` (since u64 words must be 8-byte aligned) 11 + #[derive(Clone)] 12 + #[repr(align(8))] 13 + pub(crate) struct KeccakF1600(pub(crate) [u8; KECCAK_BUFFER_SIZE]); 14 + 15 + impl KeccakF1600 { 16 + fn copy_into(&self, dst: &mut [u64; KECCAK_BLOCK_SIZE]) { 17 + assert_eq!(self.0.len(), dst.len() * SIZE); 18 + 19 + self.0 20 + .chunks_exact(SIZE) 21 + .flat_map(TryInto::<[u8; SIZE]>::try_into) 22 + .map(u64::from_le_bytes) 23 + .zip(dst.iter_mut()) 24 + .for_each(|(src, dst)| *dst = src); 25 + } 26 + 27 + fn copy_from(&mut self, src: &[u64; KECCAK_BLOCK_SIZE]) { 28 + assert_eq!(self.0.len(), src.len() * SIZE); 29 + 30 + src.iter() 31 + .zip(self.0.chunks_exact_mut(SIZE)) 32 + .for_each(|(src, dst)| dst.copy_from_slice(&src.to_le_bytes())); 33 + } 34 + 35 + /// Performs the keccakf\[1600\] permutation on an aligned byte buffer 36 + pub(crate) fn permute_f1600(&mut self) { 37 + let mut keccak_block = [0u64; KECCAK_BLOCK_SIZE]; 38 + 39 + // Make a little-endian copy, do the operation, then copy the bytes back. 40 + // This all optimises away so the block is written to directly, confirmed with godbolt. 41 + self.copy_into(&mut keccak_block); 42 + keccak::Keccak::new().with_f1600(|f| f(&mut keccak_block)); 43 + self.copy_from(&keccak_block); 44 + } 45 + } 46 + 47 + #[cfg(test)] 48 + mod tests { 49 + use super::*; 50 + 51 + #[test] 52 + fn zeroed_state_keccakf_matches_expected_outputs() { 53 + let mut state = KeccakF1600([0u8; KECCAK_BUFFER_SIZE]); 54 + 55 + // Do one permutation on the KeccakF state 56 + state.permute_f1600(); 57 + 58 + let mut words = [0u64; KECCAK_BLOCK_SIZE]; 59 + 60 + // Byte state should match expected word state 61 + state.copy_into(&mut words); 62 + 63 + assert_eq!( 64 + &words, 65 + &[ 66 + 0xF1258F7940E1DDE7, 67 + 0x84D5CCF933C0478A, 68 + 0xD598261EA65AA9EE, 69 + 0xBD1547306F80494D, 70 + 0x8B284E056253D057, 71 + 0xFF97A42D7F8E6FD4, 72 + 0x90FEE5A0A44647C4, 73 + 0x8C5BDA0CD6192E76, 74 + 0xAD30A6F71B19059C, 75 + 0x30935AB7D08FFC64, 76 + 0xEB5AA93F2317D635, 77 + 0xA9A6E6260D712103, 78 + 0x81A57C16DBCF555F, 79 + 0x43B831CD0347C826, 80 + 0x01F22F1A11A5569F, 81 + 0x05E5635A21D9AE61, 82 + 0x64BEFEF28CC970F2, 83 + 0x613670957BC46611, 84 + 0xB87C5A554FD00ECB, 85 + 0x8C3EE88A1CCF32C8, 86 + 0x940C7922AE3A2614, 87 + 0x1841F924A2C509E4, 88 + 0x16F53526E70465C2, 89 + 0x75F644E97F30A13B, 90 + 0xEAF1FF7B5CECA249, 91 + ] 92 + ); 93 + 94 + // Do another permutation 95 + state.permute_f1600(); 96 + 97 + state.copy_into(&mut words); 98 + 99 + assert_eq!( 100 + &words, 101 + &[ 102 + 0x2D5C954DF96ECB3C, 103 + 0x6A332CD07057B56D, 104 + 0x093D8D1270D76B6C, 105 + 0x8A20D9B25569D094, 106 + 0x4F9C4F99E5E7F156, 107 + 0xF957B9A2DA65FB38, 108 + 0x85773DAE1275AF0D, 109 + 0xFAF4F247C3D810F7, 110 + 0x1F1B9EE6F79A8759, 111 + 0xE4FECC0FEE98B425, 112 + 0x68CE61B6B9CE68A1, 113 + 0xDEEA66C4BA8F974F, 114 + 0x33C43D836EAFB1F5, 115 + 0xE00654042719DBD9, 116 + 0x7CF8A9F009831265, 117 + 0xFD5449A6BF174743, 118 + 0x97DDAD33D8994B40, 119 + 0x48EAD5FC5D0BE774, 120 + 0xE3B8C8EE55B7B03C, 121 + 0x91A0226E649E42E9, 122 + 0x900E3129E7BADD7B, 123 + 0x202A9EC5FAA3CCE8, 124 + 0x5B3402464E1C3DB6, 125 + 0x609F4E62A44C1059, 126 + 0x20D06CD26A8FBF5C, 127 + ] 128 + ) 129 + } 130 + }
+21
src/lib.rs
··· 1 + #![no_std] 2 + #![forbid(unsafe_code)] 3 + 4 + #[cfg(test)] 5 + mod basic_kats; 6 + #[cfg(test)] 7 + mod herd_of_kats; 8 + mod keccakf; 9 + pub mod strobe; 10 + 11 + /// Version of Strobe that this crate implements. 12 + pub static STROBE_VERSION: &str = "1.0.2"; 13 + 14 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 15 + pub struct GarbledError; 16 + 17 + impl core::fmt::Display for GarbledError { 18 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 19 + f.write_str("Protocol Failure") 20 + } 21 + }
+473
src/strobe.rs
··· 1 + use enumflags2::{BitFlag, BitFlags}; 2 + use subtle::ConstantTimeEq; 3 + 4 + use crate::{ 5 + GarbledError, STROBE_VERSION, 6 + keccakf::{KECCAK_BUFFER_SIZE, KeccakF1600}, 7 + }; 8 + 9 + #[enumflags2::bitflags] 10 + #[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 25 + } 26 + 27 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 + enum Role { 29 + Sender, 30 + Receiver, 31 + } 32 + 33 + #[derive(Debug, Clone, Copy)] 34 + #[repr(usize)] 35 + pub enum SecurityParameter { 36 + B128 = 128, 37 + B256 = 256, 38 + } 39 + 40 + #[derive(Clone)] 41 + pub struct StrobeState { 42 + /// Internal Keccak state 43 + pub(crate) state: KeccakF1600, 44 + /// Security parameter (128 or 256 bits) 45 + sec: SecurityParameter, 46 + /// This is the `R` parameter in the Strobe spec 47 + rate: usize, 48 + /// Index into `state` 49 + position: usize, 50 + /// Index into `state` 51 + start: usize, 52 + /// Represents whether we're a sender or a receiver or uninitialized 53 + role: Option<Role>, 54 + /// The last operation performed. This is to verify that the `more` flag is only used across 55 + /// identical operations. 56 + prev_flags: BitFlags<OpFlags>, 57 + } 58 + 59 + macro_rules! define_mut_operations { 60 + { $(#[$doc:meta] 61 + pub fn $name:ident($flags:expr); 62 + )+ } => { 63 + $( 64 + #[$doc] 65 + pub fn $name(&mut self, data: &mut [u8]) { 66 + let flags = $flags; 67 + self.operate(flags, data, self.prev_flags == flags); 68 + } 69 + )* 70 + }; 71 + } 72 + 73 + macro_rules! define_non_mut_operations { 74 + { $(#[$doc:meta] 75 + pub fn $name:ident($flags:expr); 76 + )+ } => { 77 + $( 78 + #[$doc] 79 + pub fn $name(&mut self, data: &[u8]) { 80 + let flags = $flags; 81 + self.operate_no_mutate(flags, data, self.prev_flags == flags); 82 + } 83 + )* 84 + }; 85 + } 86 + 87 + impl core::fmt::Display for StrobeState { 88 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 89 + f.write_str("Strobe-Keccak-")?; 90 + match self.sec { 91 + SecurityParameter::B128 => f.write_str("128")?, 92 + SecurityParameter::B256 => f.write_str("256")?, 93 + } 94 + f.write_str("/1600-v")?; 95 + f.write_str(STROBE_VERSION) 96 + } 97 + } 98 + 99 + impl core::fmt::Debug for StrobeState { 100 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 101 + // Do not reveal internal state of StrobeState, other than its security level 102 + f.debug_struct("StrobeState") 103 + .field("sec", &self.sec) 104 + .field("version", &STROBE_VERSION) 105 + .finish_non_exhaustive() 106 + } 107 + } 108 + 109 + impl StrobeState { 110 + /// Makes a new `StrobeTransport` object with a given protocol byte string and security parameter. 111 + pub fn new(protocol: &[u8], sec: SecurityParameter) -> Self { 112 + let rate = KECCAK_BUFFER_SIZE - (sec as usize) / 4 - 2; 113 + assert!((1..254).contains(&rate)); 114 + 115 + // Initialize state: st = F([0x01, R+2, 0x01, 0x00, 0x01, 0x60] + b"STROBEvX.Y.Z") 116 + let mut state_buffer = [0u8; KECCAK_BUFFER_SIZE]; 117 + state_buffer[0..6].copy_from_slice(&[0x01, (rate as u8) + 2, 0x01, 0x00, 0x01, 0x60]); 118 + state_buffer[6..13].copy_from_slice(b"STROBEv"); 119 + state_buffer[13..18].copy_from_slice(STROBE_VERSION.as_bytes()); 120 + 121 + let mut state = KeccakF1600(state_buffer); 122 + 123 + state.permute_f1600(); 124 + 125 + let mut strobe = Self { 126 + state, 127 + sec, 128 + rate, 129 + position: 0, 130 + start: 0, 131 + role: None, 132 + prev_flags: OpFlags::empty(), 133 + }; 134 + 135 + // Mix the protocol into the state 136 + strobe.meta_ad(protocol); 137 + 138 + // Reset operation so meta_ad doesn't stream after initialisation 139 + strobe.reset_ops(); 140 + 141 + strobe 142 + } 143 + 144 + /// Reset the internal operation state. This is to make sure the next call doesn't 145 + /// stream and to always begin as a new operation. This doesn't modify or change the 146 + /// internal Strobe state, only the tracked operation state. 147 + /// 148 + /// This is a modification to the Strobe API surface to prevent misuse of op calls, 149 + /// preventing panics/errors so that the compiler can optimise better. 150 + pub fn reset_ops(&mut self) { 151 + // This prevents streaming so to always make the prev_flags == flags 152 + // comparison always fail 153 + self.prev_flags = OpFlags::empty(); 154 + } 155 + 156 + // Runs the permutation function on the internal state 157 + fn permutation_f(&mut self) { 158 + self.state.0[self.position] ^= self.start as u8; 159 + self.state.0[self.position + 1] ^= 0x04; 160 + self.state.0[self.rate + 1] ^= 0x80; 161 + 162 + self.state.permute_f1600(); 163 + 164 + self.position = 0; 165 + self.start = 0; 166 + } 167 + 168 + fn increment_position(&mut self) { 169 + self.position += 1; 170 + 171 + if self.position == self.rate { 172 + self.permutation_f(); 173 + } 174 + } 175 + 176 + /// XORs the given data into the state. This is a special case of the `duplex` code in the 177 + /// STROBE paper. 178 + fn absorb(&mut self, data: &[u8]) { 179 + data.iter().for_each(|&b| { 180 + self.state.0[self.position] ^= b; 181 + 182 + self.increment_position(); 183 + }); 184 + } 185 + 186 + /// XORs the given data into the state, then sets the data equal the state. This is a special 187 + /// case of the `duplex` code in the STROBE paper. 188 + fn absorb_and_set(&mut self, data: &mut [u8]) { 189 + data.iter_mut().for_each(|b| { 190 + let state_byte = &mut self.state.0[self.position]; 191 + *state_byte ^= *b; 192 + *b = *state_byte; 193 + 194 + self.increment_position(); 195 + }); 196 + } 197 + 198 + /// Copies the internal state into the given buffer. This is a special case of `absorb_and_set` 199 + /// where `data` is all zeros. 200 + fn copy_state(&mut self, data: &mut [u8]) { 201 + data.iter_mut().for_each(|b| { 202 + *b = self.state.0[self.position]; 203 + 204 + self.increment_position(); 205 + }); 206 + } 207 + 208 + /// Overwrites the state with the given data while XORing the given data with the old state. 209 + /// This is a special case of the `duplex` code in the STROBE paper. 210 + fn exchange(&mut self, data: &mut [u8]) { 211 + data.iter_mut().for_each(|b| { 212 + let state_byte = &mut self.state.0[self.position]; 213 + *b ^= *state_byte; 214 + *state_byte ^= *b; 215 + 216 + self.increment_position(); 217 + }); 218 + } 219 + 220 + /// Overwrites the state with the given data. This is a special case of `Strobe::exchange`, 221 + /// where we do not want to mutate the input data. 222 + fn overwrite(&mut self, data: &[u8]) { 223 + data.iter().for_each(|&b| { 224 + self.state.0[self.position] = b; 225 + 226 + self.increment_position(); 227 + }); 228 + } 229 + 230 + /// Copies the state into the given buffer and sets the state to 0. This is a special case of 231 + /// `Strobe::exchange`, where `data` is assumed to be the all-zeros string. This is precisely 232 + /// the case when the current operation is PRF. 233 + fn squeeze(&mut self, data: &mut [u8]) { 234 + data.iter_mut().for_each(|b| { 235 + let state_byte = &mut self.state.0[self.position]; 236 + *b = *state_byte; 237 + *state_byte = 0; 238 + 239 + self.increment_position(); 240 + }); 241 + } 242 + 243 + /// Overwrites the state with a specified number of zeros. This is a special case of 244 + /// `Strobe::exchange`. More specifically, it's a special case of `Strobe::overwrite` and 245 + /// `Strobe::squeeze`. It's like `squeeze` in that we assume we've been given all zeros as 246 + /// input, and like `overwrite` in that we do not mutate (or take) any input. 247 + fn zero_state(&mut self, mut bytes_to_zero: usize) { 248 + // Do the zero-writing in chunks 249 + while bytes_to_zero > 0 { 250 + let slice_len = core::cmp::min(self.rate - self.position, bytes_to_zero); 251 + self.state.0[self.position..(self.position + slice_len)].fill(0); 252 + 253 + self.position += slice_len; 254 + bytes_to_zero -= slice_len; 255 + 256 + if self.position == self.rate { 257 + self.permutation_f(); 258 + } 259 + } 260 + } 261 + 262 + /// Mixes the current state index and flags into the state, accounting for whether we are 263 + /// sending or receiving 264 + fn begin_op(&mut self, mut flags: BitFlags<OpFlags>) { 265 + if flags.contains(OpFlags::Transport) { 266 + let op_role = if flags.contains(OpFlags::Inbound) { 267 + Role::Receiver 268 + } else { 269 + Role::Sender 270 + }; 271 + 272 + // If uninitialized, take on the direction of the first directional operation we get 273 + if self.role.is_none() { 274 + self.role = Some(op_role); 275 + } 276 + 277 + // So that the sender and receiver agree, toggle the I flag as necessary 278 + // This is equivalent to flags ^= is_receiver 279 + flags.set(OpFlags::Inbound, self.role.unwrap() != op_role); 280 + } 281 + 282 + let old_start = self.start; 283 + self.start = self.position + 1; 284 + 285 + // Mix in the position and flags 286 + self.absorb(&[old_start as u8, flags.bits()]); 287 + 288 + let force_permutation = flags.contains(OpFlags::Cipher) || flags.contains(OpFlags::KeyTree); 289 + if force_permutation && self.position != 0 { 290 + self.permutation_f(); 291 + } 292 + } 293 + 294 + /// Performs the state / data transformation that corresponds to the given flags. If `more` is 295 + /// given, this will treat `data` as a continuation of the data given in the previous 296 + /// call to `operate`. 297 + fn operate(&mut self, flags: BitFlags<OpFlags>, data: &mut [u8], more: bool) { 298 + self.prev_flags = flags; 299 + 300 + // If `more` isn't set, this is a new operation. Do the begin_op sequence 301 + if !more { 302 + self.begin_op(flags); 303 + } 304 + 305 + // Meta-ness is only relevant for `begin_op`. Remove it to simplify the below logic. 306 + let flags = flags & !OpFlags::Meta; 307 + 308 + // TODO?: Assert that input is empty under some flag conditions 309 + if flags.contains(OpFlags::Cipher | OpFlags::Transport) && !flags.contains(OpFlags::Inbound) 310 + { 311 + // This is equivalent to the `duplex` operation in the Python implementation, with 312 + // `cafter = True` 313 + if flags == OpFlags::Cipher | OpFlags::Transport { 314 + // This is `send_mac`. Pretend the input is all zeros 315 + self.copy_state(data); 316 + } else { 317 + self.absorb_and_set(data); 318 + } 319 + } else if flags == OpFlags::Inbound | OpFlags::App | OpFlags::Cipher { 320 + // Special case of case below. This is PRF. Use `squeeze` instead of `exchange`. 321 + self.squeeze(data); 322 + } else if flags.contains(OpFlags::Cipher) { 323 + // This is equivalent to the `duplex` operation in the Python implementation, with 324 + // `cbefore = True` 325 + self.exchange(data); 326 + } else { 327 + // This should normally call `absorb`, but `absorb` does not mutate, so the implementor 328 + // should have used operate_no_mutate instead 329 + unreachable!("operate should not be called for operations that do not require mutation") 330 + } 331 + } 332 + 333 + /// Performs the state transformation that corresponds to the given flags. If `more` is given, 334 + /// this will treat `data` as a continuation of the data given in the previous call to 335 + /// `operate`. This uses non-mutating variants of the specializations of the `duplex` function. 336 + fn operate_no_mutate(&mut self, flags: BitFlags<OpFlags>, data: &[u8], more: bool) { 337 + self.prev_flags = flags; 338 + 339 + // If `more` isn't set, this is a new operation. Do the begin_op sequence 340 + if !more { 341 + self.begin_op(flags); 342 + } 343 + 344 + // There are no non-mutating variants of things with flags & (C | T | I) == C | T 345 + if flags.contains(OpFlags::Cipher | OpFlags::Transport) && !flags.contains(OpFlags::Inbound) 346 + { 347 + unreachable!("operate_no_mutate called on something that requires mutation") 348 + } else if flags.contains(OpFlags::Cipher) { 349 + // This is equivalent to a non-mutating form of the `duplex` operation in the Python 350 + // implementation, with `cbefore = True` 351 + self.overwrite(data); 352 + } else { 353 + // This is equivalent to the `duplex` operation in the Python implementation, with 354 + // `cbefore = cafter = False` 355 + self.absorb(data); 356 + } 357 + } 358 + 359 + fn recv_mac_inner( 360 + &mut self, 361 + mac_copy: &mut [u8], 362 + flags: BitFlags<OpFlags>, 363 + ) -> Result<(), GarbledError> { 364 + // recv_mac can never be streamed 365 + self.operate(flags, mac_copy, false); 366 + 367 + // Constant-time MAC check. This accumulates the truth values of byte == 0 368 + let all_zero: bool = mac_copy 369 + .iter() 370 + .fold(subtle::Choice::from(1u8), |all_zero, b| { 371 + all_zero & 0u8.ct_eq(b) 372 + }) 373 + .into(); 374 + 375 + if all_zero { Ok(()) } else { Err(GarbledError) } 376 + } 377 + 378 + pub fn recv_mac<const N: usize>(&mut self, mac: &[u8; N]) -> Result<(), GarbledError> { 379 + let mut mac_copy = *mac; 380 + 381 + self.recv_mac_inner( 382 + &mut mac_copy, 383 + OpFlags::Inbound | OpFlags::Cipher | OpFlags::Transport, 384 + ) 385 + } 386 + 387 + pub fn meta_recv_mac<const N: usize>(&mut self, mac: &[u8; N]) -> Result<(), GarbledError> { 388 + let mut mac_copy = *mac; 389 + 390 + self.recv_mac_inner( 391 + &mut mac_copy, 392 + OpFlags::Inbound | OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta, 393 + ) 394 + } 395 + 396 + fn ratchet_inner(&mut self, num_bytes_to_zero: usize, more: bool, flags: BitFlags<OpFlags>) { 397 + // We don't make an `operate` call, since this is a super special case. That means we have 398 + // to make the `begin_op` call manually. 399 + self.prev_flags = flags; 400 + 401 + if !more { 402 + self.begin_op(flags); 403 + } 404 + 405 + self.zero_state(num_bytes_to_zero); 406 + } 407 + 408 + pub fn ratchet(&mut self, num_bytes_to_zero: usize) { 409 + let flags = OpFlags::Cipher.into(); 410 + self.ratchet_inner(num_bytes_to_zero, self.prev_flags == flags, flags); 411 + } 412 + 413 + pub fn meta_ratchet(&mut self, num_bytes_to_zero: usize) { 414 + let flags = OpFlags::Cipher | OpFlags::Meta; 415 + self.ratchet_inner(num_bytes_to_zero, self.prev_flags == flags, flags); 416 + } 417 + 418 + define_mut_operations! { 419 + /// SEND ENC 420 + pub fn send_enc(OpFlags::App | OpFlags::Cipher | OpFlags::Transport); 421 + /// META SEND ENC 422 + pub fn meta_send_enc(OpFlags::App | OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta); 423 + /// RECV ENV 424 + pub fn recv_enc(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher | OpFlags::Transport); 425 + /// META RECV ENC 426 + pub fn meta_recv_enc(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta); 427 + /// SEND MAC 428 + pub fn send_mac(OpFlags::Cipher | OpFlags::Transport); 429 + /// META SEND MAC 430 + pub fn meta_send_mac(OpFlags::Cipher | OpFlags::Transport | OpFlags::Meta); 431 + /// PRF 432 + pub fn prf(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher); 433 + /// META PRF 434 + pub fn meta_prf(OpFlags::Inbound | OpFlags::App | OpFlags::Cipher | OpFlags::Meta); 435 + } 436 + 437 + define_non_mut_operations! { 438 + /// AD 439 + pub fn ad(OpFlags::App.into()); 440 + /// META AD 441 + pub fn meta_ad(OpFlags::App | OpFlags::Meta); 442 + /// KEY 443 + pub fn key(OpFlags::App | OpFlags::Cipher); 444 + /// META KEY 445 + pub fn meta_key(OpFlags::App | OpFlags::Cipher | OpFlags::Meta); 446 + /// SEND CLR 447 + pub fn send_clr(OpFlags::App | OpFlags::Transport); 448 + /// META SEND CLR 449 + pub fn meta_send_clr(OpFlags::App | OpFlags::Transport | OpFlags::Meta); 450 + /// RECV CLR 451 + pub fn recv_clr(OpFlags::Inbound | OpFlags::App | OpFlags::Transport); 452 + /// META RECV CLR 453 + pub fn meta_recv_clr(OpFlags::Inbound | OpFlags::App | OpFlags::Transport | OpFlags::Meta); 454 + } 455 + } 456 + 457 + #[cfg(test)] 458 + mod tests { 459 + use super::*; 460 + 461 + extern crate std; 462 + 463 + #[test] 464 + fn version_formatting() { 465 + let s = StrobeState::new(b"", SecurityParameter::B128); 466 + 467 + let display = std::format!("{s}"); 468 + let debug = std::format!("{s:?}"); 469 + 470 + assert_eq!(&display, "Strobe-Keccak-128/1600-v1.0.2"); 471 + assert_eq!(&debug, "StrobeState { sec: B128, version: \"1.0.2\", .. }"); 472 + } 473 + }