From 019aeb325bcfd57f2f26bc6e4eaaec731778daae Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Apr 2024 14:50:43 -0700 Subject: [PATCH 1/5] Convert write to no std --- protocol/src/lib.rs | 85 ++++++++++++++++++++++++++++----------------- proxy/src/lib.rs | 2 +- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index caadfcb..80be961 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -218,44 +218,70 @@ pub struct PacketWriter { } impl PacketWriter { - /// Encrypt plaintext bytes to be sent over the wire. + /// Encrypt plaintext bytes and serialize into a packet to be sent over the wire. /// /// # Arguments /// - /// - `plaintext` - Plaintext to be encrypted. + /// - `plaintext` - Plaintext content to be encrypted. /// - `aad` - Optional authentication for the peer, currently only used for the first round of messages. + /// - `packet` - Buffer to write backet bytes too which must have enough capacity for the plaintext length in bytes + 20. /// - `decoy` - Should the peer ignore this message. /// - /// # Returns - /// - /// An encrypted packet to send over the wire. - /// /// # Errors /// /// Fails if the packet was not encrypted properly. - pub fn prepare_v2_packet( + pub fn prepare_packet( &mut self, - plaintext: Vec, - aad: Option>, + plaintext: &[u8], + aad: Option<&[u8]>, + packet: &mut [u8], decoy: bool, - ) -> Result, Error> { - let mut packet: Vec = Vec::new(); - let mut header: u8 = 0; - if decoy { - header = DECOY; + ) -> Result<(), Error> { + // Validate buffer capacity. + if packet.len() < plaintext.len() + LENGTH_BYTES + DECOY_BYTES + TAG_BYTES { + return Err(Error::MessageLengthTooSmall); } - let mut content_len = [0u8; 3]; - content_len.copy_from_slice(&(plaintext.len() as u32).to_le_bytes()[0..LENGTH_BYTES]); - let mut content = vec![header]; - content.extend(plaintext); + + let plaintext_length = plaintext.len(); + let decoy_index = LENGTH_BYTES + DECOY_BYTES - 1; + let plaintext_start_index = decoy_index + 1; + let plaintext_end_index = plaintext_start_index + plaintext_length; + + // Set decoy byte. + packet[decoy_index] = if decoy { DECOY } else { 0 }; + packet[plaintext_start_index..plaintext_end_index].copy_from_slice(plaintext); + + // Encrypt decoy byte and plaintext in place and produce tag. let auth = aad.unwrap_or_default(); + let tag = self + .packet_encoding_cipher + .encrypt(auth, &mut packet[decoy_index..plaintext_end_index])?; + + // Encrypt plaintext length. + let mut content_len = [0u8; 3]; + content_len.copy_from_slice(&(plaintext_length as u32).to_le_bytes()[0..LENGTH_BYTES]); self.length_encoding_cipher .crypt(&mut content_len) .expect("encrypt length"); - let tag = self.packet_encoding_cipher.encrypt(&auth, &mut content)?; - packet.extend(&content_len); - packet.extend(content); - packet.extend(tag); + + // Copy over encrypted length and the tag to the final packet (plaintext already encrypted). + packet[0..LENGTH_BYTES].copy_from_slice(&content_len); + packet[plaintext_end_index..(plaintext_end_index + TAG_BYTES)].copy_from_slice(&tag); + + Ok(()) + } + + /// Encrypt plaintext bytes and serialize into a packet to be sent over the wire with + /// necessary memory allocation. + #[cfg(feature = "std")] + pub fn prepare_packet_with_alloc( + &mut self, + plaintext: &[u8], + aad: Option<&[u8]>, + decoy: bool, + ) -> Result, Error> { + let mut packet = vec![0u8; plaintext.len() + LENGTH_BYTES + DECOY_BYTES + TAG_BYTES]; + self.prepare_packet(plaintext, aad, &mut packet, decoy)?; Ok(packet) } } @@ -334,10 +360,11 @@ impl PacketHandler { pub fn prepare_v2_packet( &mut self, contents: Vec, - aad: Option>, + aad: Option<&[u8]>, decoy: bool, ) -> Result, Error> { - self.packet_writer.prepare_v2_packet(contents, aad, decoy) + self.packet_writer + .prepare_packet_with_alloc(&contents, aad, decoy) } /// Decode the length, in bytes, of the of the rest imbound message. Intended for use with `TcpStream` and `read_exact`. @@ -671,11 +698,7 @@ impl<'a> Handshake<'a> { // TODO: Support decoy packets. // Empty vec is signaling version. - let version_packet = packet_handler.prepare_v2_packet( - Vec::new(), - self.garbage.map(|s| s.to_vec()), - false, - )?; + let version_packet = packet_handler.prepare_v2_packet(Vec::new(), self.garbage, false)?; response[16..16 + version_packet.len()].copy_from_slice(&version_packet); @@ -989,7 +1012,7 @@ mod tests { let mut bob_packet_handler = PacketHandler::new(session_keys, Role::Responder); let auth_garbage = gen_garbage(200, &mut rng); let enc_packet = alice_packet_handler - .prepare_v2_packet(Vec::new(), Some(auth_garbage.clone()), false) + .prepare_v2_packet(Vec::new(), Some(&auth_garbage), false) .unwrap(); let _ = bob_packet_handler .receive_v2_packets(enc_packet, Some(auth_garbage)) @@ -1292,7 +1315,7 @@ mod tests { let contents = Vec::from_hex("054290a6c6ba8d80478172e89d32bf690913ae9835de6dcf206ff1f4d652286fe0ddf74deba41d55de3edc77c42a32af79bbea2c00bae7492264c60866ae5a").unwrap(); let aad = Vec::from_hex("84932a55aac22b51e7b128d31d9f0550da28e6a3f394224707d878603386b2f9d0c6bcd8046679bfed7b68c517e7431e75d9dd34605727d2ef1c2babbf680ecc8d68d2c4886e9953a4034abde6da4189cd47c6bb3192242cf714d502ca6103ee84e08bc2ca4fd370d5ad4e7d06c7fbf496c6c7cc7eb19c40c61fb33df2a9ba48497a96c98d7b10c1f91098a6b7b16b4bab9687f27585ade1491ae0dba6a79e1e2d85dd9d9d45c5135ca5fca3f0f99a60ea39edbc9efc7923111c937913f225d67788d5f7e8852b697e26b92ec7bfcaa334a1665511c2b4c0a42d06f7ab98a9719516c8fd17f73804555ee84ab3b7d1762f6096b778d3cb9c799cbd49a9e4a325197b4e6cc4a5c4651f8b41ff88a92ec428354531f970263b467c77ed11312e2617d0d53fe9a8707f51f9f57a77bfb49afe3d89d85ec05ee17b9186f360c94ab8bb2926b65ca99dae1d6ee1af96cad09de70b6767e949023e4b380e66669914a741ed0fa420a48dbc7bfae5ef2019af36d1022283dd90655f25eec7151d471265d22a6d3f91dc700ba749bb67c0fe4bc0888593fbaf59d3c6fff1bf756a125910a63b9682b597c20f560ecb99c11a92c8c8c3f7fbfaa103146083a0ccaecf7a5f5e735a784a8820155914a289d57d8141870ffcaf588882332e0bcd8779efa931aa108dab6c3cce76691e345df4a91a03b71074d66333fd3591bff071ea099360f787bbe43b7b3dff2a59c41c7642eb79870222ad1c6f2e5a191ed5acea51134679587c9cf71c7d8ee290be6bf465c4ee47897a125708704ad610d8d00252d01959209d7cd04d5ecbbb1419a7e84037a55fefa13dee464b48a35c96bcb9a53e7ed461c3a1607ee00c3c302fd47cd73fda7493e947c9834a92d63dcfbd65aa7c38c3e3a2748bb5d9a58e7495d243d6b741078c8f7ee9c8813e473a323375702702b0afae1550c8341eedf5247627343a95240cb02e3e17d5dca16f8d8d3b2228e19c06399f8ec5c5e9dbe4caef6a0ea3ffb1d3c7eac03ae030e791fa12e537c80d56b55b764cadf27a8701052df1282ba8b5e3eb62b5dc7973ac40160e00722fa958d95102fc25c549d8c0e84bed95b7acb61ba65700c4de4feebf78d13b9682c52e937d23026fb4c6193e6644e2d3c99f91f4f39a8b9fc6d013f89c3793ef703987954dc0412b550652c01d922f525704d32d70d6d4079bc3551b563fb29577b3aecdc9505011701dddfd94830431e7a4918927ee44fb3831ce8c4513839e2deea1287f3fa1ab9b61a256c09637dbc7b4f0f8fbb783840f9c24526da883b0df0c473cf231656bd7bc1aaba7f321fec0971c8c2c3444bff2f55e1df7fea66ec3e440a612db9aa87bb505163a59e06b96d46f50d8120b92814ac5ab146bc78dbbf91065af26107815678ce6e33812e6bf3285d4ef3b7b04b076f21e7820dcbfdb4ad5218cf4ff6a65812d8fcb98ecc1e95e2fa58e3efe4ce26cd0bd400d6036ab2ad4f6c713082b5e3f1e04eb9e3b6c8f63f57953894b9e220e0130308e1fd91f72d398c1e7962ca2c31be83f31d6157633581a0a6910496de8d55d3d07090b6aa087159e388b7e7dec60f5d8a60d93ca2ae91296bd484d916bfaaa17c8f45ea4b1a91b37c82821199a2b7596672c37156d8701e7352aa48671d3b1bbbd2bd5f0a2268894a25b0cb2514af39c8743f8cce8ab4b523053739fd8a522222a09acf51ac704489cf17e4b7125455cb8f125b4d31af1eba1f8cf7f81a5a100a141a7ee72e8083e065616649c241f233645c5fc865d17f0285f5c52d9f45312c979bfb3ce5f2a1b951deddf280ffb3f370410cffd1583bfa90077835aa201a0712d1dcd1293ee177738b14e6b5e2a496d05220c3253bb6578d6aff774be91946a614dd7e879fb3dcf7451e0b9adb6a8c44f53c2c464bcc0019e9fad89cac7791a0a3f2974f759a9856351d4d2d7c5612c17cfc50f8479945df57716767b120a590f4bf656f4645029a525694d8a238446c5f5c2c1c995c09c1405b8b1eb9e0352ffdf766cc964f8dcf9f8f043dfab6d102cf4b298021abd78f1d9025fa1f8e1d710b38d9d1652f2d88d1305874ec41609b6617b65c5adb19b6295dc5c5da5fdf69f28144ea12f17c3c6fcce6b9b5157b3dfc969d6725fa5b098a4d9b1d31547ed4c9187452d281d0a5d456008caf1aa251fac8f950ca561982dc2dc908d3691ee3b6ad3ae3d22d002577264ca8e49c523bd51c4846be0d198ad9407bf6f7b82c79893eb2c05fe9981f687a97a4f01fe45ff8c8b7ecc551135cd960a0d6001ad35020be07ffb53cb9e731522ca8ae9364628914b9b8e8cc2f37f03393263603cc2b45295767eb0aac29b0930390eb89587ab2779d2e3decb8042acece725ba42eda650863f418f8d0d50d104e44fbbe5aa7389a4a144a8cecf00f45fb14c39112f9bfb56c0acbd44fa3ff261f5ce4acaa5134c2c1d0cca447040820c81ab1bcdc16aa075b7c68b10d06bbb7ce08b5b805e0238f24402cf24a4b4e00701935a0c68add3de090903f9b85b153cb179a582f57113bfc21c2093803f0cfa4d9d4672c2b05a24f7e4c34a8e9101b70303a7378b9c50b6cddd46814ef7fd73ef6923feceab8fc5aa8b0d185f2e83c7a99dcb1077c0ab5c1f5d5f01ba2f0420443f75c4417db9ebf1665efbb33dca224989920a64b44dc26f682cc77b4632c8454d49135e52503da855bc0f6ff8edc1145451a9772c06891f41064036b66c3119a0fc6e80dffeb65dc456108b7ca0296f4175fff3ed2b0f842cd46bd7e86f4c62dfaf1ddbf836263c00b34803de164983d0811cebfac86e7720c726d3048934c36c23189b02386a722ca9f0fe00233ab50db928d3bccea355cc681144b8b7edcaae4884d5a8f04425c0890ae2c74326e138066d8c05f4c82b29df99b034ea727afde590a1f2177ace3af99cfb1729d6539ce7f7f7314b046aab74497e63dd399e1f7d5f16517c23bd830d1fdee810f3c3b77573dd69c4b97d80d71fb5a632e00acdfa4f8e829faf3580d6a72c40b28a82172f8dcd4627663ebf6069736f21735fd84a226f427cd06bb055f94e7c92f31c48075a2955d82a5b9d2d0198ce0d4e131a112570a8ee40fb80462a81436a58e7db4e34b6e2c422e82f934ecda9949893da5730fc5c23c7c920f363f85ab28cc6a4206713c3152669b47efa8238fa826735f17b4e78750276162024ec85458cd5808e06f40dd9fd43775a456a3ff6cae90550d76d8b2899e0762ad9a371482b3e38083b1274708301d6346c22fea9bb4b73db490ff3ab05b2f7f9e187adef139a7794454b7300b8cc64d3ad76c0e4bc54e08833a4419251550655380d675bc91855aeb82585220bb97f03e976579c08f321b5f8f70988d3061f41465517d53ac571dbf1b24b94443d2e9a8e8a79b392b3d6a4ecdd7f626925c365ef6221305105ce9b5f5b6ecc5bed3d702bd4b7f5008aa8eb8c7aa3ade8ecf6251516fbefeea4e1082aa0e1848eddb31ffe44b04792d296054402826e4bd054e671f223e5557e4c94f89ca01c25c44f1a2ff2c05a70b43408250705e1b858bf0670679fdcd379203e36be3500dd981b1a6422c3cf15224f7fefdef0a5f225c5a09d15767598ecd9e262460bb33a4b5d09a64591efabc57c923d3be406979032ae0bc0997b65336a06dd75b253332ad6a8b63ef043f780a1b3fb6d0b6cad98b1ef4a02535eb39e14a866cfc5fc3a9c5deb2261300d71280ebe66a0776a151469551c3c5fa308757f956655278ec6330ae9e3625468c5f87e02cd9a6489910d4143c1f4ee13aa21a6859d907b788e28572fecee273d44e4a900fa0aa668dd861a60fb6b6b12c2c5ef3c8df1bd7ef5d4b0d1cdb8c15fffbb365b9784bd94abd001c6966216b9b67554ad7cb7f958b70092514f7800fc40244003e0fd1133a9b850fb17f4fcafde07fc87b07fb510670654a5d2d6fc9876ac74728ea41593beef003d6858786a52d3a40af7529596767c17000bfaf8dc52e871359f4ad8bf6e7b2853e5229bdf39657e213580294a5317c5df172865e1e17fe37093b585e04613f5f078f761b2b1752eb32983afda24b523af8851df9a02b37e77f543f18888a782a994a50563334282bf9cdfccc183fdf4fcd75ad86ee0d94f91ee2300a5befbccd14e03a77fc031a8cfe4f01e4c5290f5ac1da0d58ea054bd4837cfd93e5e34fc0eb16e48044ba76131f228d16cde9b0bb978ca7cdcd10653c358bdb26fdb723a530232c32ae0a4cecc06082f46e1c1d596bfe60621ad1e354e01e07b040cc7347c016653f44d926d13ca74e6cbc9d4ab4c99f4491c95c76fff5076b3936eb9d0a286b97c035ca88a3c6309f5febfd4cdaac869e4f58ed409b1e9eb4192fb2f9c2f12176d460fd98286c9d6df84598f260119fd29c63f800c07d8df83d5cc95f8c2fea2812e7890e8a0718bb1e031ecbebc0436dcf3e3b9a58bcc06b4c17f711f80fe1dffc3326a6eb6e00283055c6dabe20d311bfd5019591b7954f8163c9afad9ef8390a38f3582e0a79cdf0353de8eeb6b5f9f27b16ffdef7dd62869b4840ee226ccdce95e02c4545eb981b60571cd83f03dc5eaf8c97a0829a4318a9b3dc06c0e003db700b2260ff1fa8fee66890e637b109abb03ec901b05ca599775f48af50154c0e67d82bf0f558d7d3e0778dc38bea1eb5f74dc8d7f90abdf5511a424be66bf8b6a3cacb477d2e7ef4db68d2eba4d5289122d851f9501ba7e9c4957d8eba3be3fc8e785c4265a1d65c46f2809b70846c693864b169c9dcb78be26ea14b8613f145b01887222979a9e67aee5f800caa6f5c4229bdeefc901232ace6143c9865e4d9c07f51aa200afaf7e48a7d1d8faf366023beab12906ffcb3eaf72c0eb68075e4daf3c080e0c31911befc16f0cc4a09908bb7c1e26abab38bd7b788e1a09c0edf1a35a38d2ff1d3ed47fcdaae2f0934224694f5b56705b9409b6d3d64f3833b686f7576ec64bbdd6ff174e56c2d1edac0011f904681a73face26573fbba4e34652f7ae84acfb2fa5a5b3046f98178cd0831df7477de70e06a4c00e305f31aafc026ef064dd68fd3e4252b1b91d617b26c6d09b6891a00df68f105b5962e7f9d82da101dd595d286da721443b72b2aba2377f6e7772e33b3a5e3753da9c2578c5d1daab80187f55518c72a64ee150a7cb5649823c08c9f62cd7d020b45ec2cba8310db1a7785a46ab24785b4d54ff1660b5ca78e05a9a55edba9c60bf044737bc468101c4e8bd1480d749be5024adefca1d998abe33eaeb6b11fbb39da5d905fdd3f611b2e51517ccee4b8af72c2d948573505590d61a6783ab7278fc43fe55b1fcc0e7216444d3c8039bb8145ef1ce01c50e95a3f3feab0aee883fdb94cc13ee4d21c542aa795e18932228981690f4d4c57ca4db6eb5c092e29d8a05139d509a8aeb48baa1eb97a76e597a32b280b5e9d6c36859064c98ff96ef5126130264fa8d2f49213870d9fb036cff95da51f270311d9976208554e48ffd486470d0ecdb4e619ccbd8226147204baf8e235f54d8b1cba8fa34a9a4d055de515cdf180d2bb6739a175183c472e30b5c914d09eeb1b7dafd6872b38b48c6afc146101200e6e6a44fe5684e220adc11f5c403ddb15df8051e6bdef09117a3a5349938513776286473a3cf1d2788bb875052a2e6459fa7926da33380149c7f98d7700528a60c954e6f5ecb65842fde69d614be69eaa2040a4819ae6e756accf936e14c1e894489744a79c1f2c1eb295d13e2d767c09964b61f9cfe497649f712").unwrap(); let auth = alice_packet_handler - .prepare_v2_packet(contents, Some(aad), false) + .prepare_v2_packet(contents, Some(&aad), false) .unwrap(); let challenge = Vec::from_hex("8da7de6ea7bf2a81a396a42880ba1f5756734c4821309ac9aeffa2a26ce86873b9dc4935a772de6ec5162c6d075b14536800fb174841153511bfb597e992e2fe8a450c4bce102cc550bb37fd564c4d60bf884e").unwrap(); assert_eq!(auth, challenge); diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index e328462..3b20a59 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -226,7 +226,7 @@ pub async fn write_v2( contents.extend_from_slice(msg.payload.as_slice()); let write_bytes = encrypter - .prepare_v2_packet(contents, None, false) + .prepare_packet_with_alloc(&contents, None, false) .expect("encryption"); Ok(output.write_all(&write_bytes).await?) } From a4e5c9c31dd0529c664497d5267a5adea3b2dbb4 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Apr 2024 15:11:10 -0700 Subject: [PATCH 2/5] Add docs --- protocol/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 80be961..9d289e1 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -273,6 +273,10 @@ impl PacketWriter { /// Encrypt plaintext bytes and serialize into a packet to be sent over the wire with /// necessary memory allocation. + /// + /// - `plaintext` - Plaintext content to be encrypted. + /// - `aad` - Optional authentication for the peer, currently only used for the first round of messages. + /// - `decoy` - Should the peer ignore this message. #[cfg(feature = "std")] pub fn prepare_packet_with_alloc( &mut self, From 697d2d490f34cdc646d800871e7f277b38f71c43 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Apr 2024 15:16:28 -0700 Subject: [PATCH 3/5] Add std flag --- protocol/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 9d289e1..024a0d7 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -361,6 +361,7 @@ impl PacketHandler { /// # Errors /// /// Fails if the packet was not encrypted properly. + #[cfg(feature = "std")] pub fn prepare_v2_packet( &mut self, contents: Vec, From e4e76994db4bb74022a80877004fce200885ec98 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Apr 2024 15:33:22 -0700 Subject: [PATCH 4/5] Flag std usage tests --- protocol/src/lib.rs | 1 + protocol/tests/round_trips.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 024a0d7..4587852 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -918,6 +918,7 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn test_packet_handler() { let alice = SecretKey::from_str("61062ea5071d800bbfd59e2e8b53d47d194b095ae5a4df04936b49772ef0d4d7") diff --git a/protocol/tests/round_trips.rs b/protocol/tests/round_trips.rs index dac95b9..f456305 100644 --- a/protocol/tests/round_trips.rs +++ b/protocol/tests/round_trips.rs @@ -2,6 +2,7 @@ use bip324::{Handshake, Role}; use bitcoin::Network; #[test] +#[cfg(feature = "std")] fn hello_world_happy_path() { let mut init_message = vec![0u8; 64]; let mut init_handshake = From f08987fe832b9a2d10c9985f700515006ee1b04b Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Fri, 19 Apr 2024 15:47:46 -0700 Subject: [PATCH 5/5] Get std out of handshake --- protocol/src/lib.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 4587852..a1cf131 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -34,6 +34,8 @@ const TAG_BYTES: usize = 16; const LENGTH_BYTES: usize = 3; /// Value for decoy flag. const DECOY: u8 = 128; +/// Version content is always empty for the current version of the protocol. +const VERSION_CONTENT: [u8; 0] = []; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { @@ -700,12 +702,15 @@ impl<'a> Handshake<'a> { let mut packet_handler = PacketHandler::new(materials, self.role.clone()); - // TODO: Support decoy packets. + // TODO: Support sending decoy packets before the version packet. // Empty vec is signaling version. - let version_packet = packet_handler.prepare_v2_packet(Vec::new(), self.garbage, false)?; - - response[16..16 + version_packet.len()].copy_from_slice(&version_packet); + packet_handler.packet_writer.prepare_packet( + &VERSION_CONTENT, + self.garbage, + &mut response[16..16 + LENGTH_BYTES + DECOY_BYTES + TAG_BYTES], + false, + )?; self.packet_handler = Some(packet_handler);