russh-cryptovec-0.59.0/.cargo_vcs_info.json0000644000000001470000000000100143110ustar { "git": { "sha1": "1a55f50fdc7dc3b603bbd6919064b9b5103710ed" }, "path_in_vcs": "cryptovec" }russh-cryptovec-0.59.0/Cargo.lock0000644000000306530000000000100122710ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64ct" version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "futures-core" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-task" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-task", "pin-project-lite", "slab", ] [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libm" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "minicov" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" dependencies = [ "cc", "walkdir", ] [[package]] name = "nix" version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "nu-ansi-term" version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ "windows-sys", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oorandom" version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "pem-rfc7468" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "russh-cryptovec" version = "0.59.0" dependencies = [ "log", "nix", "ssh-encoding", "wasm-bindgen-test", "windows-sys", ] [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", "serde", "serde_core", "zmij", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "ssh-encoding" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" dependencies = [ "base64ct", "bytes", "pem-rfc7468", ] [[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", "futures-util", "js-sys", "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6311c867385cc7d5602463b31825d454d0837a3aba7cdb5e56d5201792a3f7fe" dependencies = [ "async-trait", "cast", "js-sys", "libm", "minicov", "nu-ansi-term", "num-traits", "oorandom", "serde", "serde_json", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", "wasm-bindgen-test-shared", ] [[package]] name = "wasm-bindgen-test-macro" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67008cdde4769831958536b0f11b3bdd0380bde882be17fff9c2f34bb4549abd" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "wasm-bindgen-test-shared" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe29135b180b72b04c74aa97b2b4a2ef275161eff9a6c7955ea9eaedc7e1d4e" [[package]] name = "web-sys" version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ "windows-sys", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" russh-cryptovec-0.59.0/Cargo.toml0000644000000030220000000000100123020ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.85" name = "russh-cryptovec" version = "0.59.0" authors = [ "Pierre-Étienne Meunier ", "Eugeny ", "Eugeny ) -> std::fmt::Result { if self.size == 0 { return f.write_str(""); } write!(f, "<{:?}>", self.size) } } impl Unpin for CryptoVec {} unsafe impl Send for CryptoVec {} unsafe impl Sync for CryptoVec {} // Common traits implementations impl AsRef<[u8]> for CryptoVec { fn as_ref(&self) -> &[u8] { self.deref() } } impl AsMut<[u8]> for CryptoVec { fn as_mut(&mut self) -> &mut [u8] { self.deref_mut() } } impl Deref for CryptoVec { type Target = [u8]; fn deref(&self) -> &[u8] { unsafe { std::slice::from_raw_parts(self.p, self.size) } } } impl DerefMut for CryptoVec { fn deref_mut(&mut self) -> &mut [u8] { unsafe { std::slice::from_raw_parts_mut(self.p, self.size) } } } impl From for CryptoVec { fn from(e: String) -> Self { CryptoVec::from(e.into_bytes()) } } impl From<&str> for CryptoVec { fn from(e: &str) -> Self { CryptoVec::from(e.as_bytes()) } } impl From<&[u8]> for CryptoVec { fn from(e: &[u8]) -> Self { CryptoVec::from_slice(e) } } impl From> for CryptoVec { fn from(e: Vec) -> Self { let mut c = CryptoVec::new_zeroed(e.len()); c.clone_from_slice(&e[..]); c } } // Indexing implementations impl Index> for CryptoVec { type Output = [u8]; fn index(&self, index: RangeFrom) -> &[u8] { self.deref().index(index) } } impl Index> for CryptoVec { type Output = [u8]; fn index(&self, index: RangeTo) -> &[u8] { self.deref().index(index) } } impl Index> for CryptoVec { type Output = [u8]; fn index(&self, index: Range) -> &[u8] { self.deref().index(index) } } impl Index for CryptoVec { type Output = [u8]; fn index(&self, _: RangeFull) -> &[u8] { self.deref() } } impl IndexMut for CryptoVec { fn index_mut(&mut self, _: RangeFull) -> &mut [u8] { self.deref_mut() } } impl IndexMut> for CryptoVec { fn index_mut(&mut self, index: RangeFrom) -> &mut [u8] { self.deref_mut().index_mut(index) } } impl IndexMut> for CryptoVec { fn index_mut(&mut self, index: RangeTo) -> &mut [u8] { self.deref_mut().index_mut(index) } } impl IndexMut> for CryptoVec { fn index_mut(&mut self, index: Range) -> &mut [u8] { self.deref_mut().index_mut(index) } } impl Index for CryptoVec { type Output = u8; fn index(&self, index: usize) -> &u8 { self.deref().index(index) } } // IO-related implementation impl std::io::Write for CryptoVec { fn write(&mut self, buf: &[u8]) -> Result { self.extend(buf); Ok(buf.len()) } fn flush(&mut self) -> Result<(), std::io::Error> { Ok(()) } } // Default implementation impl Default for CryptoVec { fn default() -> Self { CryptoVec { p: std::ptr::NonNull::dangling().as_ptr(), size: 0, capacity: 0, } } } impl CryptoVec { /// Creates a new `CryptoVec`. pub fn new() -> CryptoVec { CryptoVec::default() } /// Creates a new `CryptoVec` with `n` zeros. pub fn new_zeroed(size: usize) -> CryptoVec { unsafe { let capacity = size.next_power_of_two(); let layout = std::alloc::Layout::from_size_align_unchecked(capacity, 1); let p = std::alloc::alloc_zeroed(layout); let _ = mlock(p, capacity); CryptoVec { p, capacity, size } } } /// Creates a new `CryptoVec` with capacity `capacity`. pub fn with_capacity(capacity: usize) -> CryptoVec { unsafe { let capacity = capacity.next_power_of_two(); let layout = std::alloc::Layout::from_size_align_unchecked(capacity, 1); let p = std::alloc::alloc_zeroed(layout); let _ = mlock(p, capacity); CryptoVec { p, capacity, size: 0, } } } /// Length of this `CryptoVec`. /// /// ``` /// assert_eq!(russh_cryptovec::CryptoVec::new().len(), 0) /// ``` pub fn len(&self) -> usize { self.size } /// Returns `true` if and only if this CryptoVec is empty. /// /// ``` /// assert!(russh_cryptovec::CryptoVec::new().is_empty()) /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Resize this CryptoVec, appending zeros at the end. This may /// perform at most one reallocation, overwriting the previous /// version with zeros. pub fn resize(&mut self, size: usize) { if size <= self.capacity && size > self.size { // If this is an expansion within capacity, the memory is already zeroed. self.size = size } else if size <= self.size { // If this is a truncation, securely erase the extra memory. // Uses zeroize (optimization_barrier) to prevent dead-store elimination. unsafe { zeroize(self.p.add(size), self.size - size); } self.size = size; } else { // realloc ! and erase the previous memory. unsafe { let next_capacity = size.next_power_of_two(); let old_ptr = self.p; let next_layout = std::alloc::Layout::from_size_align_unchecked(next_capacity, 1); self.p = std::alloc::alloc_zeroed(next_layout); let _ = mlock(self.p, next_capacity); if self.capacity > 0 { std::ptr::copy_nonoverlapping(old_ptr, self.p, self.size); zeroize(old_ptr, self.size); let _ = munlock(old_ptr, self.capacity); let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1); std::alloc::dealloc(old_ptr, layout); } if self.p.is_null() { #[allow(clippy::panic)] { panic!("Realloc failed, pointer = {self:?} {size:?}") } } else { self.capacity = next_capacity; self.size = size; } } } } /// Clear this CryptoVec (retaining the memory). /// /// ``` /// let mut v = russh_cryptovec::CryptoVec::new(); /// v.extend(b"blabla"); /// v.clear(); /// assert!(v.is_empty()) /// ``` pub fn clear(&mut self) { self.resize(0); } /// Append a new byte at the end of this CryptoVec. pub fn push(&mut self, s: u8) { let size = self.size; self.resize(size + 1); unsafe { *self.p.add(size) = s } } /// Read `n_bytes` from `r`, and append them at the end of this /// `CryptoVec`. Returns the number of bytes read (and appended). pub fn read( &mut self, n_bytes: usize, mut r: R, ) -> Result { let cur_size = self.size; self.resize(cur_size + n_bytes); let s = unsafe { std::slice::from_raw_parts_mut(self.p.add(cur_size), n_bytes) }; // Resize the buffer to its appropriate size. match r.read(s) { Ok(n) => { self.resize(cur_size + n); Ok(n) } Err(e) => { self.resize(cur_size); Err(e) } } } /// Write all this CryptoVec to the provided `Write`. Returns the /// number of bytes actually written. /// /// ``` /// let mut v = russh_cryptovec::CryptoVec::new(); /// v.extend(b"blabla"); /// let mut s = std::io::stdout(); /// v.write_all_from(0, &mut s).unwrap(); /// ``` pub fn write_all_from( &self, offset: usize, mut w: W, ) -> Result { assert!(offset < self.size); // if we're past this point, self.p cannot be null. unsafe { let s = std::slice::from_raw_parts(self.p.add(offset), self.size - offset); w.write(s) } } /// Resize this CryptoVec, returning a mutable borrow to the extra bytes. /// /// ``` /// let mut v = russh_cryptovec::CryptoVec::new(); /// v.resize_mut(4).clone_from_slice(b"test"); /// ``` pub fn resize_mut(&mut self, n: usize) -> &mut [u8] { let size = self.size; self.resize(size + n); unsafe { std::slice::from_raw_parts_mut(self.p.add(size), n) } } /// Append a slice at the end of this CryptoVec. /// /// ``` /// let mut v = russh_cryptovec::CryptoVec::new(); /// v.extend(b"test"); /// ``` pub fn extend(&mut self, s: &[u8]) { let size = self.size; self.resize(size + s.len()); unsafe { std::ptr::copy_nonoverlapping(s.as_ptr(), self.p.add(size), s.len()); } } /// Create a `CryptoVec` from a slice /// /// ``` /// russh_cryptovec::CryptoVec::from_slice(b"test"); /// ``` pub fn from_slice(s: &[u8]) -> CryptoVec { let mut v = CryptoVec::new(); v.resize(s.len()); unsafe { std::ptr::copy_nonoverlapping(s.as_ptr(), v.p, s.len()); } v } } impl Clone for CryptoVec { fn clone(&self) -> Self { let mut v = Self::new(); v.extend(self); v } } // Drop implementation impl Drop for CryptoVec { fn drop(&mut self) { if self.capacity > 0 { unsafe { zeroize(self.p, self.size); let _ = munlock(self.p, self.capacity); let layout = std::alloc::Layout::from_size_align_unchecked(self.capacity, 1); std::alloc::dealloc(self.p, layout); } } } } unsafe fn zeroize(dst: *mut u8, size: usize) { unsafe { std::ptr::write_bytes(dst, 0, size); } optimization_barrier(dst, size); } // https://github.com/RustCrypto/utils/blob/a9f3f461baa3e02f69a205c772b6b2d3eac4eda8/zeroize/src/barrier.rs fn optimization_barrier(dst: *mut u8, size: usize) { #[cfg(all( not(miri), any( target_arch = "aarch64", target_arch = "arm", target_arch = "arm64ec", target_arch = "loongarch64", target_arch = "riscv32", target_arch = "riscv64", target_arch = "s390x", target_arch = "x86", target_arch = "x86_64", ) ))] { let _ = size; unsafe { core::arch::asm!( "# {}", in(reg) dst, options(readonly, preserves_flags, nostack), ); } } #[cfg(not(all( not(miri), any( target_arch = "aarch64", target_arch = "arm", target_arch = "arm64ec", target_arch = "loongarch64", target_arch = "riscv32", target_arch = "riscv64", target_arch = "s390x", target_arch = "x86", target_arch = "x86_64", ) )))] { /// Custom version of `core::hint::black_box` implemented using /// `#[inline(never)]` and `read_volatile`. #[inline(never)] fn custom_black_box(p: *const u8) { let _ = unsafe { core::ptr::read_volatile(p) }; } core::hint::black_box(dst); if size > 0 { custom_black_box(dst); } } } #[cfg(test)] mod test { use super::CryptoVec; #[test] fn test_new() { let crypto_vec = CryptoVec::new(); assert_eq!(crypto_vec.size, 0); assert_eq!(crypto_vec.capacity, 0); } #[test] fn test_resize_expand() { let mut crypto_vec = CryptoVec::new_zeroed(5); crypto_vec.resize(10); assert_eq!(crypto_vec.size, 10); assert!(crypto_vec.capacity >= 10); assert!(crypto_vec.iter().skip(5).all(|&x| x == 0)); // Ensure newly added elements are zeroed } #[test] fn test_resize_shrink() { let mut crypto_vec = CryptoVec::new_zeroed(10); crypto_vec.resize(5); assert_eq!(crypto_vec.size, 5); // Ensure shrinking keeps the previous elements intact assert_eq!(crypto_vec.len(), 5); } #[test] fn test_resize_zero() { let mut crypto_vec = CryptoVec::new(); crypto_vec.resize(0); assert_eq!(crypto_vec.size, 0); assert_eq!(crypto_vec.len(), 0); } #[test] fn test_push() { let mut crypto_vec = CryptoVec::new(); crypto_vec.push(1); crypto_vec.push(2); assert_eq!(crypto_vec.size, 2); assert_eq!(crypto_vec[0], 1); assert_eq!(crypto_vec[1], 2); } #[test] fn test_write_trait() { use std::io::Write; let mut crypto_vec = CryptoVec::new(); let bytes_written = crypto_vec.write(&[1, 2, 3]).unwrap(); assert_eq!(bytes_written, 3); assert_eq!(crypto_vec.size, 3); assert_eq!(crypto_vec.as_ref(), &[1, 2, 3]); } #[test] fn test_as_ref_as_mut() { let mut crypto_vec = CryptoVec::new_zeroed(5); let slice_ref: &[u8] = crypto_vec.as_ref(); assert_eq!(slice_ref.len(), 5); let slice_mut: &mut [u8] = crypto_vec.as_mut(); slice_mut[0] = 1; assert_eq!(crypto_vec[0], 1); } #[test] fn test_from_string() { let input = String::from("hello"); let crypto_vec: CryptoVec = input.into(); assert_eq!(crypto_vec.as_ref(), b"hello"); } #[test] fn test_from_str() { let input = "hello"; let crypto_vec: CryptoVec = input.into(); assert_eq!(crypto_vec.as_ref(), b"hello"); } #[test] fn test_from_byte_slice() { let input = b"hello".as_slice(); let crypto_vec: CryptoVec = input.into(); assert_eq!(crypto_vec.as_ref(), b"hello"); } #[test] fn test_from_vec() { let input = vec![1, 2, 3, 4]; let crypto_vec: CryptoVec = input.into(); assert_eq!(crypto_vec.as_ref(), &[1, 2, 3, 4]); } #[test] fn test_index() { let crypto_vec = CryptoVec::from(vec![1, 2, 3, 4, 5]); assert_eq!(crypto_vec[0], 1); assert_eq!(crypto_vec[4], 5); assert_eq!(&crypto_vec[1..3], &[2, 3]); } #[test] fn test_drop() { let mut crypto_vec = CryptoVec::new_zeroed(10); // Ensure vector is filled with non-zero data crypto_vec.extend(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); drop(crypto_vec); // Check that memory zeroing was done during the drop // This part is more difficult to test directly since it involves // private memory management. However, with Rust's unsafe features, // it may be checked using tools like Valgrind or manual inspection. } #[test] fn test_new_zeroed() { let crypto_vec = CryptoVec::new_zeroed(10); assert_eq!(crypto_vec.size, 10); assert!(crypto_vec.capacity >= 10); assert!(crypto_vec.iter().all(|&x| x == 0)); // Ensure all bytes are zeroed } #[test] fn test_clear() { let mut crypto_vec = CryptoVec::new(); crypto_vec.extend(b"blabla"); crypto_vec.clear(); assert!(crypto_vec.is_empty()); } #[test] fn test_extend() { let mut crypto_vec = CryptoVec::new(); crypto_vec.extend(b"test"); assert_eq!(crypto_vec.as_ref(), b"test"); } #[test] fn test_write_all_from() { let mut crypto_vec = CryptoVec::new(); crypto_vec.extend(b"blabla"); let mut output: Vec = Vec::new(); let written_size = crypto_vec.write_all_from(0, &mut output).unwrap(); assert_eq!(written_size, 6); // "blabla" has 6 bytes assert_eq!(output, b"blabla"); } #[test] fn test_resize_mut() { let mut crypto_vec = CryptoVec::new(); crypto_vec.resize_mut(4).clone_from_slice(b"test"); assert_eq!(crypto_vec.as_ref(), b"test"); } // DocTests cannot be run on with wasm_bindgen_test #[cfg(target_arch = "wasm32")] mod wasm32 { use wasm_bindgen_test::wasm_bindgen_test; use super::*; wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen_test] fn test_push_u32_be() { let mut crypto_vec = CryptoVec::new(); let value = 43554u32; crypto_vec.push_u32_be(value); assert_eq!(crypto_vec.len(), 4); // u32 is 4 bytes long assert_eq!(crypto_vec.read_u32_be(0), value); } #[wasm_bindgen_test] fn test_read_u32_be() { let mut crypto_vec = CryptoVec::new(); let value = 99485710u32; crypto_vec.push_u32_be(value); assert_eq!(crypto_vec.read_u32_be(0), value); } } } russh-cryptovec-0.59.0/src/lib.rs000064400000000000000000000015741046102023000150110ustar 00000000000000#![deny( clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, clippy::panic )] // Copyright 2016 Pierre-Étienne Meunier // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Re-export CryptoVec from the cryptovec module mod cryptovec; pub use cryptovec::CryptoVec; // Platform-specific modules mod platform; #[cfg(feature = "ssh-encoding")] mod ssh; russh-cryptovec-0.59.0/src/platform/mod.rs000064400000000000000000000031371046102023000166430ustar 00000000000000#[cfg(windows)] mod windows; #[cfg(not(windows))] #[cfg(not(target_arch = "wasm32"))] mod unix; #[cfg(target_arch = "wasm32")] mod wasm; // Re-export functions based on the platform #[cfg(not(windows))] #[cfg(not(target_arch = "wasm32"))] pub use unix::{mlock, munlock}; #[cfg(target_arch = "wasm32")] pub use wasm::{mlock, munlock}; #[cfg(windows)] pub use windows::{mlock, munlock}; #[cfg(not(target_arch = "wasm32"))] mod error { use std::error::Error; use std::fmt::Display; use std::sync::atomic::{AtomicBool, Ordering}; use log::warn; #[derive(Debug)] pub struct MemoryLockError { message: String, } impl MemoryLockError { pub fn new(message: String) -> Self { let warning_previously_shown = MLOCK_WARNING_SHOWN.swap(true, Ordering::Relaxed); if !warning_previously_shown { warn!( "Security warning: OS has failed to lock/unlock memory for a cryptographic buffer: {message}" ); #[cfg(unix)] warn!("You might need to increase the RLIMIT_MEMLOCK limit."); warn!("This warning will only be shown once."); } Self { message } } } static MLOCK_WARNING_SHOWN: AtomicBool = AtomicBool::new(false); impl Display for MemoryLockError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "failed to lock/unlock memory: {}", self.message) } } impl Error for MemoryLockError {} } #[cfg(not(target_arch = "wasm32"))] pub use error::MemoryLockError; russh-cryptovec-0.59.0/src/platform/unix.rs000064400000000000000000000014771046102023000170540ustar 00000000000000use std::ffi::c_void; use std::ptr::NonNull; use nix::errno::Errno; use super::MemoryLockError; /// Unlock memory on drop for Unix-based systems. pub fn munlock(ptr: *const u8, len: usize) -> Result<(), MemoryLockError> { unsafe { Errno::clear(); let ptr = NonNull::new_unchecked(ptr as *mut c_void); nix::sys::mman::munlock(ptr, len).map_err(|e| { MemoryLockError::new(format!("munlock: {} (0x{:x})", e.desc(), e as i32)) })?; } Ok(()) } pub fn mlock(ptr: *const u8, len: usize) -> Result<(), MemoryLockError> { unsafe { Errno::clear(); let ptr = NonNull::new_unchecked(ptr as *mut c_void); nix::sys::mman::mlock(ptr, len) .map_err(|e| MemoryLockError::new(format!("mlock: {} (0x{:x})", e.desc(), e as i32)))?; } Ok(()) } russh-cryptovec-0.59.0/src/platform/wasm.rs000064400000000000000000000004131046102023000170250ustar 00000000000000use std::convert::Infallible; // WASM does not support synchronization primitives pub fn munlock(_ptr: *const u8, _len: usize) -> Result<(), Infallible> { // No-op Ok(()) } pub fn mlock(_ptr: *const u8, _len: usize) -> Result<(), Infallible> { Ok(()) } russh-cryptovec-0.59.0/src/platform/windows.rs000064400000000000000000000276561046102023000175720ustar 00000000000000//! Windows memory locking via VirtualLock/VirtualUnlock. //! //! Pins crypto buffer pages in physical RAM to prevent key material from //! being written to the page file. Maintains per-page reference counts //! since Windows does not support nested VirtualLock calls on the same page. //! //! When VirtualLock fails with ERROR_WORKING_SET_QUOTA (the default minimum //! working set is too small), the process working set is grown incrementally //! and the lock is retried. use std::collections::BTreeMap; use std::collections::btree_map::Entry; use std::sync::{Mutex, OnceLock}; use windows_sys::Win32::System::Memory::{ GetProcessWorkingSetSizeEx, SetProcessWorkingSetSizeEx, VirtualLock, VirtualUnlock, }; use windows_sys::Win32::System::SystemInformation::{GetNativeSystemInfo, SYSTEM_INFO}; use windows_sys::Win32::System::Threading::GetCurrentProcess; use super::MemoryLockError; /// Page size cached at first use. static PAGE_SIZE: OnceLock = OnceLock::new(); /// Per-page reference counts. Windows does not support nested VirtualLock /// calls on the same page, so we track them ourselves and only call /// VirtualLock/VirtualUnlock on the first lock / last unlock. static LOCKED_PAGES: Mutex> = Mutex::new(BTreeMap::new()); /// Maximum number of pages this library will lock. /// /// Crypto key material is small (typically 32-256 bytes per buffer). This /// cap prevents unbounded working set growth from exhausting physical RAM. /// 256 pages = 1 MiB on 4 KiB page systems. const MAX_LOCKED_PAGES: usize = 256; /// Win32 ERROR_WORKING_SET_QUOTA: the process minimum working set is too /// small for VirtualLock to pin the requested page. /// const ERROR_WORKING_SET_QUOTA: u32 = 0x5ad; /// Known flag bits for SetProcessWorkingSetSizeEx. Masked before passing /// to avoid forwarding undocumented bits from future Windows versions. /// const KNOWN_WS_FLAGS: u32 = 0x01 | 0x02 | 0x04 | 0x08; /// Lock memory pages so they cannot be paged to disk. pub fn mlock(ptr: *const u8, len: usize) -> Result<(), MemoryLockError> { let page_size = get_page_size(); let page_range = get_page_range(ptr, len, page_size); let mut locked_pages = LOCKED_PAGES .lock() .map_err(|e| MemoryLockError::new(format!("failed to acquire page lock table: {e}")))?; let mut newly_locked: Vec = Vec::new(); let mut refcount_bumped: Vec = Vec::new(); for page_idx in page_range { let count = locked_pages.len(); match locked_pages.entry(page_idx) { Entry::Occupied(mut entry) => { *entry.get_mut() += 1; refcount_bumped.push(page_idx); } Entry::Vacant(entry) => { if let Err(e) = lock_page(page_idx, count, page_size) { // Roll back: undo refcount bumps on already-locked pages. for &p in &refcount_bumped { if let Entry::Occupied(mut e) = locked_pages.entry(p) { *e.get_mut() -= 1; } } // Roll back: unlock and remove newly locked pages. for &p in &newly_locked { locked_pages.remove(&p); let _ = unlock_page(p, page_size); } return Err(e); } entry.insert(1); newly_locked.push(page_idx); } } } Ok(()) } /// Unlock previously locked memory pages. pub fn munlock(ptr: *const u8, len: usize) -> Result<(), MemoryLockError> { let page_size = get_page_size(); let page_range = get_page_range(ptr, len, page_size); let mut locked_pages = LOCKED_PAGES .lock() .map_err(|e| MemoryLockError::new(format!("failed to acquire page lock table: {e}")))?; for page_idx in page_range { match locked_pages.entry(page_idx) { Entry::Occupied(mut entry) => { *entry.get_mut() -= 1; if *entry.get() == 0 { entry.remove(); unlock_page(page_idx, page_size)?; } } Entry::Vacant(_) => { return Err(MemoryLockError::new( "attempted to unlock a page that is not locked".into(), )); } } } Ok(()) } // --------------------------------------------------------------------------- // Internal: per-page VirtualLock / VirtualUnlock // --------------------------------------------------------------------------- /// Lock a single page into physical memory. /// /// If VirtualLock fails with ERROR_WORKING_SET_QUOTA, grows the working /// set by one page and retries once. /// /// # Preconditions /// /// Called with `LOCKED_PAGES` held. Must not re-acquire it. fn lock_page( page_idx: usize, current_locked_count: usize, page_size: usize, ) -> Result<(), MemoryLockError> { if current_locked_count >= MAX_LOCKED_PAGES { return Err(MemoryLockError::new(format!( "VirtualLock: locked page limit reached ({MAX_LOCKED_PAGES} pages)" ))); } let addr = page_idx .checked_mul(page_size) .ok_or_else(|| MemoryLockError::new("VirtualLock: page address overflow".into()))? as *mut std::ffi::c_void; // First attempt. if unsafe { VirtualLock(addr, page_size) } != 0 { return Ok(()); } let err = unsafe { windows_sys::Win32::Foundation::GetLastError() }; if err != ERROR_WORKING_SET_QUOTA { return Err(MemoryLockError::new(format!( "VirtualLock failed: 0x{err:x}" ))); } // Working set too small -- grow it and retry. log::debug!("VirtualLock failed with ERROR_WORKING_SET_QUOTA, growing working set"); grow_working_set(page_size).map_err(|e| { MemoryLockError::new(format!( "VirtualLock failed: ERROR_WORKING_SET_QUOTA (0x{err:x}), working set growth also failed: {e}" )) })?; // Retry. if unsafe { VirtualLock(addr, page_size) } != 0 { return Ok(()); } let retry_err = unsafe { windows_sys::Win32::Foundation::GetLastError() }; Err(MemoryLockError::new(format!( "VirtualLock failed: 0x{retry_err:x} (after working set growth)" ))) } /// Unlock a single page. fn unlock_page(page_idx: usize, page_size: usize) -> Result<(), MemoryLockError> { let addr = page_idx .checked_mul(page_size) .ok_or_else(|| MemoryLockError::new("VirtualUnlock: page address overflow".into()))? as *mut std::ffi::c_void; if unsafe { VirtualUnlock(addr, page_size) } == 0 { let err = unsafe { windows_sys::Win32::Foundation::GetLastError() }; return Err(MemoryLockError::new(format!( "VirtualUnlock failed: 0x{err:x}" ))); } Ok(()) } // --------------------------------------------------------------------------- // Internal: working set management // --------------------------------------------------------------------------- /// Increase the process working set by one page. /// /// Called on demand when VirtualLock fails with ERROR_WORKING_SET_QUOTA. /// /// # Preconditions /// /// Called with `LOCKED_PAGES` held. Must not re-acquire it. fn grow_working_set(page_size: usize) -> Result<(), MemoryLockError> { let handle = unsafe { GetCurrentProcess() }; let mut min_ws: usize = 0; let mut max_ws: usize = 0; let mut flags: u32 = 0; unsafe { if GetProcessWorkingSetSizeEx(handle, &mut min_ws, &mut max_ws, &mut flags) == 0 { let err = windows_sys::Win32::Foundation::GetLastError(); return Err(MemoryLockError::new(format!( "GetProcessWorkingSetSizeEx failed: 0x{err:x}" ))); } } let new_min = min_ws.saturating_add(page_size); let new_max = max_ws.max(new_min.saturating_add(page_size)); let safe_flags = flags & KNOWN_WS_FLAGS; log::debug!( "growing process working set: min {min_ws} -> {new_min}, max {max_ws} -> {new_max}" ); unsafe { if SetProcessWorkingSetSizeEx(handle, new_min, new_max, safe_flags) == 0 { let err = windows_sys::Win32::Foundation::GetLastError(); return Err(MemoryLockError::new(format!( "SetProcessWorkingSetSizeEx failed: 0x{err:x}" ))); } } Ok(()) } // --------------------------------------------------------------------------- // Internal: page arithmetic // --------------------------------------------------------------------------- fn get_page_size() -> usize { *PAGE_SIZE.get_or_init(|| { let mut info: SYSTEM_INFO = unsafe { std::mem::zeroed() }; unsafe { GetNativeSystemInfo(&mut info) }; info.dwPageSize as usize }) } /// Compute the range of page indices spanned by `[ptr, ptr+len)`. fn get_page_range(ptr: *const u8, len: usize, page_size: usize) -> std::ops::Range { let start = ptr as usize / page_size; let end = if len == 0 { start } else { // Use the address of the last byte to find the last page, avoiding // overflow from `len + page_size - 1` when len is near usize::MAX. (ptr as usize + len - 1) / page_size + 1 }; start..end } #[cfg(test)] mod tests { use super::*; // -- Page range arithmetic ----------------------------------------------- #[test] fn page_range_zero_length() { let range = get_page_range(0x2000 as *const u8, 0, 4096); assert!(range.is_empty()); } #[test] fn page_range_single_byte() { let range = get_page_range(0x2000 as *const u8, 1, 4096); assert_eq!(range, 2..3); } #[test] fn page_range_within_one_page() { let range = get_page_range(0x2010 as *const u8, 100, 4096); assert_eq!(range, 2..3); } #[test] fn page_range_spans_two_pages() { // Buffer starts near end of page 2, extends into page 3. let range = get_page_range(0x2FF0 as *const u8, 32, 4096); assert_eq!(range, 2..4); } #[test] fn page_range_exact_page_boundary() { let range = get_page_range(0x3000 as *const u8, 4096, 4096); assert_eq!(range, 3..4); } #[test] fn page_range_one_byte_past_boundary() { let range = get_page_range(0x3000 as *const u8, 4097, 4096); assert_eq!(range, 3..5); } // -- mlock / munlock integration ----------------------------------------- #[test] fn mlock_munlock_roundtrip() { let buf = vec![0u8; 64]; mlock(buf.as_ptr(), buf.len()).expect("mlock should succeed"); munlock(buf.as_ptr(), buf.len()).expect("munlock should succeed"); } #[test] fn mlock_refcount_overlap() { // Two overlapping mlock calls on the same page should refcount. let buf = vec![0u8; 128]; mlock(buf.as_ptr(), 64).expect("first mlock"); mlock(buf.as_ptr(), 128).expect("second mlock (overlapping)"); munlock(buf.as_ptr(), 64).expect("first munlock"); munlock(buf.as_ptr(), 128).expect("second munlock"); } #[test] fn munlock_without_mlock_fails() { let buf = vec![0u8; 64]; assert!(munlock(buf.as_ptr(), buf.len()).is_err()); } #[test] fn mlock_zero_length_is_noop() { let buf = vec![0u8; 64]; mlock(buf.as_ptr(), 0).expect("zero-length mlock should succeed"); } #[test] fn mlock_multiple_distinct_buffers() { // Lock several distinct buffers to exercise working set growth. let buffers: Vec> = (0..8).map(|_| vec![0u8; 4096]).collect(); for buf in &buffers { mlock(buf.as_ptr(), buf.len()).expect("mlock should succeed"); } for buf in &buffers { munlock(buf.as_ptr(), buf.len()).expect("munlock should succeed"); } } } russh-cryptovec-0.59.0/src/ssh.rs000064400000000000000000000006211046102023000150300ustar 00000000000000use ssh_encoding::{Reader, Result, Writer}; use crate::CryptoVec; impl Reader for CryptoVec { fn read<'o>(&mut self, out: &'o mut [u8]) -> Result<&'o [u8]> { (&self[..]).read(out) } fn remaining_len(&self) -> usize { self.len() } } impl Writer for CryptoVec { fn write(&mut self, bytes: &[u8]) -> Result<()> { self.extend(bytes); Ok(()) } }