Adding support for MACs and AES in CTR mode

pmeunier
Aug 28, 2025, 2:24 PM
LYRZREY75IOPQJ3DMJJB4SINAEVI5P5TE32SSFO7QKFBDYVXXKNAC

Dependencies

  • [2] 5EV2X6V4 Version bump/nix shell
  • [3] WX7UY5KK Adding support for aes256-gcm
  • [4] SKTE2HF6 Version 0.37
  • [5] Y67GNDVB use tokio::process::Command for proxy commands
  • [6] XEKZBXNI Fixing a bug with tokio::select
  • [7] G3FNNIIU Limiting the amount of messages that can be buffered while rekeying to two channel windows
  • [8] VYDCQWSF Version 0.30.6
  • [9] UT24SM2F Version bump
  • [10] 7FRJYUI6 Reboot because of a bad change
  • [11] CQSPFH4H Version 0.30.4
  • [12] 2VTUKRLJ Version
  • [13] 32GIIFWR Fixing strict mode
  • [14] PDTFLA4Y Version 0.30.7
  • [15] ASD7JVBE Do not read past the size of the buffer (after Tokio 0.3)
  • [16] 7Y2ROIVZ Version bump
  • [17] D6H7OWTT Fixing the terrapin attack mitigation
  • [18] DJT33BQE Version bump
  • [19] CWHVPLXN Version bump
  • [20] P5SAD7JF Version bump (+ formatting)
  • [21] E2SB74SV Version 0.30.3
  • [22] EUHO3DAZ Send a SSH_MSG_EXT_INFO with server-sig-algs when the client indicates they support extensions by sending ext-info-c. This allows modern clients that don't do ssh-rsa anymore because of sha1 to still use RSA keys with sha2.
  • [23] VJIXIN4T Fixing CVE-2023-48795
  • [24] 2WEO7OZL Version updates: getting rid of anyhow + moving to Tokio 1.0
  • [25] WXZWQLGL Correct negotiation without OpenSSL
  • [26] BRDS7STA Adding method `send_channel_msg` to client::Session, to make it easier to write handlers
  • [27] 662ZS5JF Version 0.33.2
  • [28] SMUTYV2C Fixing warnings
  • [29] OQZGSEWM Buffering non-kex packets received after issuing a KEXINIT
  • [30] MCS77Y4V Making OpenSSL optional
  • [31] Q323RFJS Version bump
  • [32] R5J3MB56 Client newkeys was not always resetting the sequence counter
  • [33] XCNFFN6Z Thrussh-keys, version 0.19.3
  • [34] EZTTZ6OW Fixing terrapin, again
  • [35] OBHPOIUH Modernising the API with async traits
  • [36] MFMCIUMJ Fixing authentication with RSA
  • [37] 634OYCNM Tokio 0.3
  • [38] NHOSLQGG Thrussh: making OpenSSL optional
  • [39] ORSEEVB5 Version bump
  • [40] UHAEQPZU Support ecdsa-sha2-nistp256 keys for authentication
  • [41] TFYJ3P2A Version 0.30.8/0.19.4, and solving conflicts
  • [42] BWU5BDAH Thrussh-keys 0.19.5
  • [43] 6TIVIM7M Version of thrussh-keys
  • [44] 2Q3SZY2C Version bump
  • [45] FT67GGO4 Version bump (Pijul and Thrussh)
  • [46] ELRPPXSG Fixing conflicts
  • [47] AWVLXGAW Removing anyhows on Windows

Change contents

  • edit in thrussh-libsodium/src/lib.rs at line 9
    [5.212]
    [5.212]
    }
    pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
    unsafe {
    sodium_memcmp(
    a.as_ptr() as *const libc::c_void,
    b.as_ptr() as *const libc::c_void,
    a.len().min(b.len()),
    ) == 0
    }
  • edit in thrussh-libsodium/src/lib.rs at line 51
    [5.1147]
    [5.1147]
    }
    }
    }
    pub mod hmac256 {
    pub const KEY_BYTES: usize = libsodium_sys::crypto_auth_hmacsha256_KEYBYTES as usize;
    pub const BYTES: usize = libsodium_sys::crypto_auth_hmacsha256_BYTES as usize;
    pub fn init_state(key: &[u8; KEY_BYTES]) -> libsodium_sys::crypto_auth_hmacsha256_state {
    unsafe {
    let mut state = std::mem::zeroed();
    libsodium_sys::crypto_auth_hmacsha256_init(&mut state, key.as_ptr(), KEY_BYTES);
    state
    }
    }
    pub fn update(s: &mut libsodium_sys::crypto_auth_hmacsha256_state, msg: &[u8]) {
    unsafe {
    libsodium_sys::crypto_auth_hmacsha256_update(s, msg.as_ptr(), msg.len() as u64);
  • edit in thrussh-libsodium/src/lib.rs at line 72
    [5.1163]
    [5.1163]
    pub fn finalize(s: &mut libsodium_sys::crypto_auth_hmacsha256_state, out: &mut [u8]) {
    assert_eq!(out.len(), BYTES);
    unsafe {
    libsodium_sys::crypto_auth_hmacsha256_final(s, out.as_mut_ptr());
    }
    }
  • replacement in thrussh-libsodium/Cargo.toml at line 3
    [5.5348][2.0:18]()
    version = "0.2.2"
    [5.5348]
    [5.5366]
    version = "0.4.0"
  • replacement in thrussh-keys/Cargo.toml at line 3
    [5.157057][5.283:302]()
    version = "0.23.1"
    [5.157057]
    [5.157076]
    version = "0.23.2"
  • replacement in thrussh-keys/Cargo.toml at line 37
    [5.157904][5.303:323]()
    cryptovec = "0.7.0"
    [5.157904]
    [5.255]
    cryptovec = "0.7.1"
  • replacement in thrussh-keys/Cargo.toml at line 49
    [5.158187][5.14033:14059]()
    thrussh-libsodium = "0.2"
    [5.158187]
    [5.14059]
    thrussh-libsodium = "0.4"
  • replacement in thrussh/src/session.rs at line 18
    [5.172339][5.361:411]()
    use crate::{auth, cipher, kex, msg, negotiation};
    [5.172339]
    [5.172339]
    use crate::{auth, cipher, kex, mac, msg, negotiation};
  • replacement in thrussh/src/session.rs at line 34
    [5.172775][5.172775:172810]()
    pub mac: Option<&'static str>,
    [5.172775]
    [5.0]
    pub mac: mac::Name,
  • replacement in thrussh/src/session.rs at line 485
    [5.187505][5.187505:187621]()
    let c = self
    .kex
    .compute_keys(&session_id, &hash, self.names.cipher, is_server)?;
    [5.187505]
    [5.187621]
    let c = self.kex.compute_keys(
    &session_id,
    &hash,
    self.names.cipher,
    self.names.mac,
    is_server,
    )?;
  • replacement in thrussh/src/negotiation.rs at line 15
    [5.268986][5.4433:4471]()
    use crate::{Error, cipher, kex, msg};
    [5.268986]
    [5.269024]
    use crate::{Error, cipher, kex, mac, msg};
  • replacement in thrussh/src/negotiation.rs at line 30
    [5.269388][5.269388:269423]()
    pub mac: Option<&'static str>,
    [5.269388]
    [5.269423]
    pub mac: mac::Name,
  • replacement in thrussh/src/negotiation.rs at line 51
    [5.269929][5.269929:269967]()
    pub mac: &'static [&'static str],
    [5.269929]
    [5.269967]
    pub mac: &'static [mac::Name],
  • replacement in thrussh/src/negotiation.rs at line 75
    [3.77][5.442:466](),[5.442][5.442:466]()
    mac: &["none"],
    [3.77]
    [5.466]
    mac: &[mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 89
    [3.155][5.15610:15634](),[5.15610][5.15610:15634]()
    mac: &["none"],
    [3.155]
    [5.15634]
    mac: &[mac::hmac_sha256::NAME, mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 99
    [5.900][5.900:924]()
    mac: &["none"],
    [5.900]
    [5.924]
    mac: &[mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 109
    [5.15949][5.15949:15973]()
    mac: &["none"],
    [5.15949]
    [5.15973]
    mac: &[mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 119
    [3.233][5.1376:1400](),[5.1376][5.1376:1400]()
    mac: &["none"],
    [3.233]
    [5.1400]
    mac: &[mac::hmac_sha256::NAME, mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 129
    [3.311][5.84:108](),[5.84][5.84:108]()
    mac: &["none"],
    [3.311]
    [5.108]
    mac: &[mac::hmac_sha256::NAME, mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 139
    [5.270274][5.270274:270298]()
    mac: &["none"],
    [5.270274]
    [5.270298]
    mac: &[mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 149
    [5.270568][5.270568:270592]()
    mac: &["none"],
    [5.270568]
    [5.1932]
    mac: &[mac::Name("none")],
  • replacement in thrussh/src/negotiation.rs at line 262
    [5.273024][5.273024:273134]()
    let mac = Self::select(pref.mac, r.read_string()?);
    let mac = mac.and_then(|(_, x)| Some(x));
    [5.273024]
    [5.273134]
    let mac_string = r.read_string()?;
    let mac = Self::select(pref.mac, mac_string);
    if mac.is_none() {
    debug!(
    "Could not find common mac, other side only supports {:?}, we only support {:?}",
    from_utf8(mac_string),
    pref.mac
    );
    return Err(Error::NoCommonMac.into());
    }
    let mac = mac.unwrap().1;
  • file addition: mac (d--r------)
    [5.165404]
  • file addition: mod.rs (----------)
    [0.2412]
    pub enum MacKey {
    None,
    HmacSha256(hmac_sha256::Key),
    }
    impl MacKey {
    pub fn len(&self) -> usize {
    match self {
    MacKey::None => 0,
    MacKey::HmacSha256(_) => thrussh_libsodium::hmac256::BYTES,
    }
    }
    pub fn authenticate(&self, number: u32, b: &[u8], out: &mut [u8]) {
    match self {
    MacKey::None => {}
    MacKey::HmacSha256(k) => hmac_sha256::authenticate(k, number, b, out),
    }
    }
    pub fn verify(&self, number: u32, b: &[u8], out: &[u8]) -> bool {
    match self {
    MacKey::None => true,
    MacKey::HmacSha256(k) => hmac_sha256::verify(k, number, b, out),
    }
    }
    }
    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
    pub struct Name(pub &'static str);
    impl AsRef<str> for Name {
    fn as_ref(&self) -> &str {
    self.0
    }
    }
    pub struct Mac {
    pub _name: Name,
    pub key_len: usize,
    pub make_key: fn(key: &[u8]) -> MacKey,
    }
    pub mod hmac_sha256 {
    use byteorder::{BigEndian, ByteOrder};
    pub const NAME: super::Name = super::Name("hmac-sha2-256");
    pub struct Key([u8; thrussh_libsodium::hmac256::KEY_BYTES]);
    pub const MAC: super::Mac = super::Mac {
    _name: NAME,
    key_len: 32,
    make_key,
    };
    fn make_key(b: &[u8]) -> super::MacKey {
    let mut k = Key([0; thrussh_libsodium::hmac256::KEY_BYTES]);
    k.0.clone_from_slice(b);
    super::MacKey::HmacSha256(k)
    }
    pub fn authenticate(k: &Key, number: u32, b: &[u8], out: &mut [u8]) {
    let mut s = thrussh_libsodium::hmac256::init_state(&k.0);
    let mut n = [0; 4];
    BigEndian::write_u32(&mut n, number);
    thrussh_libsodium::hmac256::update(&mut s, &n);
    thrussh_libsodium::hmac256::update(&mut s, b);
    thrussh_libsodium::hmac256::finalize(&mut s, out);
    }
    pub fn verify(k: &Key, number: u32, b: &[u8], h: &[u8]) -> bool {
    let mut s = thrussh_libsodium::hmac256::init_state(&k.0);
    let mut n = [0; 4];
    BigEndian::write_u32(&mut n, number);
    thrussh_libsodium::hmac256::update(&mut s, &n);
    thrussh_libsodium::hmac256::update(&mut s, b);
    let mut out = [0; thrussh_libsodium::hmac256::BYTES];
    thrussh_libsodium::hmac256::finalize(&mut s, &mut out);
    thrussh_libsodium::memcmp(&out, h)
    }
    }
  • edit in thrussh/src/lib.rs at line 268
    [5.289968]
    [5.289968]
    pub mod mac;
  • replacement in thrussh/src/lib.rs at line 322
    [5.291116][5.291116:291153]()
    #[error("No common key cipher")]
    [5.291116]
    [5.291153]
    #[error("No common cipher")]
  • edit in thrussh/src/lib.rs at line 325
    [5.291174]
    [5.291174]
    /// No common mac.
    #[error("No common mac")]
    NoCommonMac,
  • edit in thrussh/src/lib.rs at line 365
    [5.292082]
    [5.292082]
    /// The remote provided a wrong MAC.
    #[error("Wrong message authentication code")]
    WrongMAC,
  • replacement in thrussh/src/kex.rs at line 15
    [5.307516][5.307516:307547]()
    use crate::{cipher, key, msg};
    [5.307516]
    [5.307547]
    use crate::{cipher, key, mac, msg};
  • edit in thrussh/src/kex.rs at line 57
    [5.308508]
    [5.308508]
    static MAC_BUF: RefCell<CryptoVec> = RefCell::new(CryptoVec::new());
  • edit in thrussh/src/kex.rs at line 164
    [5.312120]
    [5.312120]
    mac: mac::Name,
  • edit in thrussh/src/kex.rs at line 171
    [3.677]
    [5.312336]
    #[cfg(feature = "openssl")]
    super::cipher::aes256_ctr::NAME => &super::cipher::aes256_ctr::CIPHER,
  • edit in thrussh/src/kex.rs at line 176
    [5.312381]
    [5.312381]
    let mac = match mac {
    super::mac::hmac_sha256::NAME => Some(&super::mac::hmac_sha256::MAC),
    super::mac::Name("none") => None,
    _ => unreachable!(),
    };
  • replacement in thrussh/src/kex.rs at line 186
    [3.713][3.713:950]()
    let compute_key = |c, key: &mut CryptoVec, len| -> Result<(), crate::Error> {
    let mut buffer = buffer.borrow_mut();
    buffer.clear();
    key.clear();
    [3.713]
    [5.312876]
    MAC_BUF.with(|mac_buf| {
    let compute_key =
    |c, key: &mut CryptoVec, len| -> Result<(), crate::Error> {
    let mut buffer = buffer.borrow_mut();
    buffer.clear();
    key.clear();
    if let Some(ref shared) = self.shared_secret {
    buffer.extend_ssh_mpint(&shared.0);
    }
    buffer.extend(exchange_hash.as_ref());
    buffer.push(c);
    buffer.extend(session_id.as_ref());
    let hash = {
    use sha2::Digest;
    let mut hasher = sha2::Sha256::new();
    hasher.update(&buffer[..]);
    hasher.finalize()
    };
    key.extend(hash.as_ref());
  • replacement in thrussh/src/kex.rs at line 208
    [5.313463][5.313463:313624]()
    if let Some(ref shared) = self.shared_secret {
    buffer.extend_ssh_mpint(&shared.0);
    }
    [5.312877]
    [3.951]
    while key.len() < len {
    // extend.
    buffer.clear();
    if let Some(ref shared) = self.shared_secret {
    buffer.extend_ssh_mpint(&shared.0);
    }
    buffer.extend(exchange_hash.as_ref());
    buffer.extend(key);
    let hash = {
    use sha2::Digest;
    let mut hasher = sha2::Sha256::new();
    hasher.update(&buffer[..]);
    hasher.finalize()
    };
    key.extend(&hash.as_ref());
    }
    key.resize(len);
    Ok(())
    };
  • replacement in thrussh/src/kex.rs at line 228
    [3.952][5.313624:313687](),[5.313624][5.313624:313687](),[5.313687][3.953:1053](),[3.1053][5.313731:313768](),[5.313731][5.313731:313768](),[5.313768][5.1662:1876]()
    buffer.extend(exchange_hash.as_ref());
    buffer.push(c);
    buffer.extend(session_id.as_ref());
    let hash = {
    use sha2::Digest;
    let mut hasher = sha2::Sha256::new();
    hasher.update(&buffer[..]);
    hasher.finalize()
    [3.952]
    [5.313950]
    let (
    local_to_remote,
    local_to_remote_iv,
    local_to_remote_mac,
    remote_to_local,
    remote_to_local_iv,
    remote_to_local_mac,
    ) = if is_server {
    (b'D', b'B', b'F', b'C', b'A', b'E')
    } else {
    (b'C', b'A', b'E', b'D', b'B', b'F')
  • edit in thrussh/src/kex.rs at line 240
    [5.313977][3.1054:1105]()
    key.extend(hash.as_ref());
  • replacement in thrussh/src/kex.rs at line 241
    [5.314098][3.1106:1883]()
    while key.len() < len {
    // extend.
    buffer.clear();
    if let Some(ref shared) = self.shared_secret {
    buffer.extend_ssh_mpint(&shared.0);
    }
    buffer.extend(exchange_hash.as_ref());
    buffer.extend(key);
    let hash = {
    use sha2::Digest;
    let mut hasher = sha2::Sha256::new();
    hasher.update(&buffer[..]);
    hasher.finalize()
    };
    key.extend(&hash.as_ref());
    [5.314098]
    [3.1883]
    let mut key = key.borrow_mut();
    compute_key(local_to_remote, &mut key, cipher.key_len)?;
    let mut iv = iv.borrow_mut();
    if cipher.iv_len > 0 {
    compute_key(local_to_remote_iv, &mut iv, cipher.iv_len)?;
  • edit in thrussh/src/kex.rs at line 247
    [3.1909][3.1909:2004]()
    key.resize(len);
    Ok(())
    };
  • replacement in thrussh/src/kex.rs at line 248
    [3.2005][3.2005:2198]()
    let (local_to_remote, local_to_remote_iv, remote_to_local, remote_to_local_iv) =
    if is_server {
    (b'D', b'B', b'C', b'A')
    [3.2005]
    [3.2198]
    let local_to_remote_mac = if let Some(mac) = mac {
    let mut mac_buf = mac_buf.borrow_mut();
    compute_key(local_to_remote_mac, &mut mac_buf, mac.key_len)?;
    (mac.make_key)(&mac_buf)
  • replacement in thrussh/src/kex.rs at line 253
    [3.2231][3.2231:2284]()
    (b'C', b'A', b'D', b'B')
    [3.2231]
    [3.2284]
    mac::MacKey::None
  • replacement in thrussh/src/kex.rs at line 256
    [5.314281][3.2312:2721]()
    let mut key = key.borrow_mut();
    compute_key(local_to_remote, &mut key, cipher.key_len)?;
    let mut iv = iv.borrow_mut();
    if cipher.iv_len > 0 {
    compute_key(local_to_remote_iv, &mut iv, cipher.iv_len)?;
    }
    let local_to_remote = (cipher.make_sealing_cipher)(&key, &iv);
    [5.314281]
    [5.314476]
    let local_to_remote =
    (cipher.make_sealing_cipher)(&key, &iv, local_to_remote_mac);
  • replacement in thrussh/src/kex.rs at line 259
    [5.314477][3.2722:3029]()
    compute_key(remote_to_local, &mut key, cipher.key_len)?;
    if cipher.iv_len > 0 {
    compute_key(remote_to_local_iv, &mut iv, cipher.iv_len)?;
    }
    let remote_to_local = (cipher.make_opening_cipher)(&key, &iv);
    [5.314477]
    [5.314624]
    compute_key(remote_to_local, &mut key, cipher.key_len)?;
    if cipher.iv_len > 0 {
    compute_key(remote_to_local_iv, &mut iv, cipher.iv_len)?;
    }
    let remote_to_local_mac = if let Some(mac) = mac {
    let mut mac_buf = mac_buf.borrow_mut();
    compute_key(remote_to_local_mac, &mut mac_buf, mac.key_len)?;
    (mac.make_key)(&mac_buf)
    } else {
    mac::MacKey::None
    };
    let remote_to_local =
    (cipher.make_opening_cipher)(&key, &iv, remote_to_local_mac);
  • replacement in thrussh/src/kex.rs at line 273
    [5.314625][3.3030:3197]()
    Ok(super::cipher::CipherPair {
    local_to_remote: local_to_remote,
    remote_to_local: remote_to_local,
    [5.314625]
    [3.3197]
    Ok(super::cipher::CipherPair {
    local_to_remote: local_to_remote,
    remote_to_local: remote_to_local,
    })
  • edit in thrussh/src/cipher/mod.rs at line 16
    [5.409737]
    [5.9343]
    use crate::mac;
  • edit in thrussh/src/cipher/mod.rs at line 21
    [5.23187]
    [3.3221]
    #[cfg(feature = "openssl")]
    pub mod aes256_ctr;
  • replacement in thrussh/src/cipher/mod.rs at line 32
    [3.3293][3.3293:3439]()
    pub make_opening_cipher: fn(key: &[u8], iv: &[u8]) -> OpeningCipher,
    pub make_sealing_cipher: fn(key: &[u8], iv: &[u8]) -> SealingCipher,
    [3.3293]
    [5.410079]
    pub make_opening_cipher: fn(key: &[u8], iv: &[u8], mac: mac::MacKey) -> OpeningCipher,
    pub make_sealing_cipher: fn(key: &[u8], iv: &[u8], mac: mac::MacKey) -> SealingCipher,
  • edit in thrussh/src/cipher/mod.rs at line 41
    [3.3504]
    [5.410182]
    #[cfg(feature = "openssl")]
    Aes256Ctr(aes256_ctr::Key),
  • edit in thrussh/src/cipher/mod.rs at line 52
    [3.3599]
    [5.410393]
    #[cfg(feature = "openssl")]
    OpeningCipher::Aes256Ctr(ref key) => key,
  • edit in thrussh/src/cipher/mod.rs at line 63
    [3.3664]
    [5.410512]
    #[cfg(feature = "openssl")]
    Aes256Ctr(aes256_ctr::Key),
  • edit in thrussh/src/cipher/mod.rs at line 74
    [3.3759]
    [5.410729]
    #[cfg(feature = "openssl")]
    SealingCipher::Aes256Ctr(ref key) => key,
  • replacement in thrussh/src/cipher/mod.rs at line 113
    [5.411569][5.411569:411589]()
    tag: &[u8],
    [5.411569]
    [5.411589]
    tag: usize,
  • replacement in thrussh/src/cipher/mod.rs at line 124
    [5.411793][5.411793:411884]()
    fn seal(&self, seqn: u32, plaintext_in_ciphertext_out: &mut [u8], tag_out: &mut [u8]);
    [5.411793]
    [5.411884]
    fn seal(&self, seqn: u32, plaintext_in_ciphertext_out: &mut [u8], plaintext_len: usize);
  • replacement in thrussh/src/cipher/mod.rs at line 154
    [5.412721][5.412721:412847]()
    let (ciphertext, tag) = buffer.buffer.split_at_mut(ciphertext_len);
    let plaintext = key.open(seqn, ciphertext, tag)?;
    [5.412721]
    [5.412847]
    let plaintext = key.open(seqn, &mut buffer.buffer, ciphertext_len)?;
  • replacement in thrussh/src/cipher/mod.rs at line 200
    [5.414441][5.414441:414557]()
    let (plaintext, tag) =
    buffer.buffer[offset..].split_at_mut(PACKET_LENGTH_LEN + packet_length);
    [5.414441]
    [5.414557]
    let plaintext = &mut buffer.buffer[offset..];
  • replacement in thrussh/src/cipher/mod.rs at line 202
    [5.414558][5.414558:414607]()
    key.seal(buffer.seqn.0, plaintext, tag);
    [5.414558]
    [5.414607]
    key.seal(buffer.seqn.0, plaintext, PACKET_LENGTH_LEN + packet_length);
  • replacement in thrussh/src/cipher/clear.rs at line 34
    [5.415887][5.415887:415907]()
    tag: &[u8],
    [5.415887]
    [5.415907]
    tag: usize,
  • replacement in thrussh/src/cipher/clear.rs at line 36
    [5.415942][5.415942:416002]()
    debug_assert_eq!(tag.len(), 0); // self.tag_len());
    [5.415942]
    [5.416002]
    debug_assert_eq!(tag, ciphertext_in_plaintext_out.len()); // self.tag_len());
  • replacement in thrussh/src/cipher/clear.rs at line 67
    [5.416838][5.416838:416989]()
    fn seal(&self, _seqn: u32, _plaintext_in_ciphertext_out: &mut [u8], tag_out: &mut [u8]) {
    debug_assert_eq!(tag_out.len(), self.tag_len());
    [5.416838]
    [5.416989]
    fn seal(&self, _seqn: u32, plaintext_in_ciphertext_out: &mut [u8], tag_out: usize) {
    debug_assert_eq!(tag_out, plaintext_in_ciphertext_out.len());
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 43
    [5.418160][3.3776:3845]()
    fn make_sealing_cipher(k: &[u8], _: &[u8]) -> super::SealingCipher {
    [5.418160]
    [5.418219]
    fn make_sealing_cipher(k: &[u8], _: &[u8], _: crate::mac::MacKey) -> super::SealingCipher {
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 51
    [5.418452][3.3846:3915]()
    fn make_opening_cipher(k: &[u8], _: &[u8]) -> super::OpeningCipher {
    [5.418452]
    [5.418511]
    fn make_opening_cipher(k: &[u8], _: &[u8], _: crate::mac::MacKey) -> super::OpeningCipher {
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 85
    [5.419453][5.419453:419473]()
    tag: &[u8],
    [5.419453]
    [5.419473]
    tag: usize,
  • edit in thrussh/src/cipher/chacha20poly1305.rs at line 87
    [5.419508]
    [5.419508]
    let (ciphertext_in_plaintext_out, tag) = ciphertext_in_plaintext_out.split_at_mut(tag);
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 135
    [5.421280][5.421280:421422]()
    fn seal(
    &self,
    sequence_number: u32,
    plaintext_in_ciphertext_out: &mut [u8],
    tag_out: &mut [u8],
    ) {
    [5.421280]
    [5.421422]
    fn seal(&self, sequence_number: u32, plaintext_in_ciphertext_out: &mut [u8], tag_out: usize) {
    let (payload, tag_out) = plaintext_in_ciphertext_out.split_at_mut(tag_out);
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 139
    [5.421487][5.421487:421557]()
    let (a, b) = plaintext_in_ciphertext_out.split_at_mut(4);
    [5.421487]
    [5.421557]
    let (a, b) = payload.split_at_mut(4);
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 147
    [5.421822][5.421822:421895]()
    let tag = poly1305_auth(plaintext_in_ciphertext_out, &poly_key);
    [5.421822]
    [5.421895]
    let tag = poly1305_auth(payload, &poly_key);
  • replacement in thrussh/src/cipher/aes256_gcm.rs at line 38
    [3.5033][3.5033:5102]()
    fn make_sealing_cipher(k: &[u8], i: &[u8]) -> super::SealingCipher {
    [3.5033]
    [3.5102]
    fn make_sealing_cipher(k: &[u8], i: &[u8], _: crate::mac::MacKey) -> super::SealingCipher {
  • replacement in thrussh/src/cipher/aes256_gcm.rs at line 51
    [3.5395][3.5395:5464]()
    fn make_opening_cipher(k: &[u8], i: &[u8]) -> super::OpeningCipher {
    [3.5395]
    [3.5464]
    fn make_opening_cipher(k: &[u8], i: &[u8], _: crate::mac::MacKey) -> super::OpeningCipher {
  • replacement in thrussh/src/cipher/aes256_gcm.rs at line 81
    [3.6111][3.6111:6131]()
    tag: &[u8],
    [3.6111]
    [3.6131]
    tag: usize,
  • edit in thrussh/src/cipher/aes256_gcm.rs at line 83
    [3.6166]
    [3.6166]
    let (payload, tag) = payload.split_at_mut(tag);
  • replacement in thrussh/src/cipher/aes256_gcm.rs at line 127
    [3.7424][3.7424:7508]()
    fn seal(&self, _sequence_number: u32, payload: &mut [u8], tag_out: &mut [u8]) {
    [3.7424]
    [3.7508]
    fn seal(&self, _sequence_number: u32, payload: &mut [u8], tag_out: usize) {
    let (payload, tag_out) = payload.split_at_mut(tag_out);
  • replacement in thrussh/src/cipher/aes256_gcm.rs at line 159
    [3.8317][3.8317:8336]()
    pub fn crypt_aead(
    [3.8317]
    [3.8336]
    fn crypt_aead(
  • file addition: aes256_ctr.rs (----------)
    [5.409076]
    use openssl::symm::{Cipher, Crypter};
    use rand::Rng;
    use std::sync::Mutex;
    pub const NAME: super::Name = super::Name("aes256-ctr");
    pub struct Key {
    mac: crate::mac::MacKey,
    crypter: Mutex<Crypter>,
    buf: Mutex<cryptovec::CryptoVec>,
    }
    pub static CIPHER: super::Cipher = super::Cipher {
    _name: NAME,
    key_len: 32,
    iv_len: 16,
    make_sealing_cipher,
    make_opening_cipher,
    };
    fn make_sealing_cipher(k: &[u8], iv: &[u8], mac: crate::mac::MacKey) -> super::SealingCipher {
    super::SealingCipher::Aes256Ctr(Key {
    crypter: Mutex::new(
    openssl::symm::Crypter::new(
    Cipher::aes_256_ctr(),
    openssl::symm::Mode::Encrypt,
    &k,
    Some(&iv),
    )
    .unwrap(),
    ),
    mac,
    buf: Mutex::new(cryptovec::CryptoVec::new()),
    })
    }
    fn make_opening_cipher(k: &[u8], iv: &[u8], mac: crate::mac::MacKey) -> super::OpeningCipher {
    super::OpeningCipher::Aes256Ctr(Key {
    crypter: Mutex::new(
    openssl::symm::Crypter::new(
    Cipher::aes_256_ctr(),
    openssl::symm::Mode::Encrypt,
    &k,
    Some(&iv),
    )
    .unwrap(),
    ),
    mac,
    buf: Mutex::new(cryptovec::CryptoVec::new()),
    })
    }
    impl super::SealingKey for Key {
    fn padding_length(&self, payload: &[u8]) -> usize {
    let encrypted_len = payload.len() + 5;
    let padding = 16 - (encrypted_len % 16);
    let min_padding = if padding < 4 { padding + 16 } else { padding };
    let mut rng = rand::rng();
    (rng.random::<u8>() & 0xe0) as usize + min_padding
    }
    fn fill_padding(&self, padding_out: &mut [u8]) {
    openssl::rand::rand_bytes(padding_out).unwrap()
    }
    fn tag_len(&self) -> usize {
    self.mac.len()
    }
    /// Append an encrypted packet with contents `packet_content` at
    /// the end of `buffer`.
    fn seal(&self, sequence_number: u32, payload_tag: &mut [u8], tag_out: usize) {
    let (payload, tag) = payload_tag.split_at_mut(tag_out);
    self.mac.authenticate(sequence_number, &payload[..], tag);
    let mut out = self.buf.lock().unwrap();
    out.resize(payload.len());
    let mut crypter = self.crypter.lock().unwrap();
    let count = crypter.update(&payload, &mut out).unwrap();
    crypter.finalize(&mut out[count..]).unwrap();
    payload.clone_from_slice(&out);
    }
    }
    impl super::OpeningKey for Key {
    fn decrypt_packet_length(
    &self,
    _sequence_number: u32,
    encrypted_packet_length: [u8; 4],
    ) -> [u8; 4] {
    let mut out = [0; 4];
    let mut crypter = self.crypter.lock().unwrap();
    let count = crypter.update(&encrypted_packet_length, &mut out).unwrap();
    crypter.finalize(&mut out[count..]).unwrap();
    out
    }
    fn tag_len(&self) -> usize {
    self.mac.len()
    }
    fn open<'a>(
    &self,
    sequence_number: u32,
    payload: &'a mut [u8],
    tag: usize,
    ) -> Result<&'a [u8], crate::Error> {
    let (payload, tag) = payload.split_at_mut(tag);
    let mut out = self.buf.lock().unwrap();
    out.resize(payload.len());
    let mut c = self.crypter.lock().unwrap();
    let count = c.update(&payload, &mut out).unwrap();
    c.finalize(&mut out[count..]).unwrap();
    if !self.mac.verify(sequence_number, &out, tag) {
    return Err(crate::Error::WrongMAC);
    }
    payload.clone_from_slice(&out);
    Ok(payload)
    }
    }
  • replacement in thrussh/Cargo.toml at line 5
    [5.426214][4.0:19]()
    version = "0.37.0"
    [5.426214]
    [5.426233]
    version = "0.38.0"
  • edit in thrussh/Cargo.toml at line 19
    [5.426551]
    [5.426551]
    "src/mac/mod.rs",
  • edit in thrussh/Cargo.toml at line 27
    [5.426692]
    [4.20]
    "src/cipher/aes256_ctr.rs",
  • replacement in thrussh/Cargo.toml at line 53
    [5.24259][5.9803:9867]()
    thrussh-keys = { version = "0.23.1", path = "../thrussh-keys" }
    [5.24259]
    [5.1877]
    thrussh-keys = { version = "0.23.2", path = "../thrussh-keys" }
  • replacement in thrussh/Cargo.toml at line 55
    [5.1925][5.427122:427148](),[5.427122][5.427122:427148](),[5.427148][5.9868:9888]()
    thrussh-libsodium = "0.2"
    cryptovec = "0.7.0"
    [5.1925]
    [5.1053]
    thrussh-libsodium = "0.4"
    cryptovec = "0.7.1"
  • replacement in cryptovec/src/lib.rs at line 301
    [5.436819][5.436819:436906]()
    let s = s.to_be();
    let x: [u8; 4] = unsafe { std::mem::transmute(s) };
    [5.436819]
    [5.436906]
    let x: [u8; 4] = s.to_be_bytes();
  • replacement in cryptovec/Cargo.toml at line 4
    [5.440768][5.10044:10062]()
    version = "0.7.0"
    [5.440768]
    [5.440786]
    version = "0.7.1"