Refactoring

pmeunier
Sep 12, 2025, 1:14 PM
5POTCWP5BFLZA7Z4KMTXMK44FGG7M3BEBKAO5GNCXMTKZCMDJDYAC

Dependencies

  • [2] LYRZREY7 Adding support for MACs and AES in CTR mode
  • [3] 32GIIFWR Fixing strict mode
  • [4] VJIXIN4T Fixing CVE-2023-48795
  • [5] D6H7OWTT Fixing the terrapin attack mitigation
  • [6] 2WEO7OZL Version updates: getting rid of anyhow + moving to Tokio 1.0
  • [7] XEKZBXNI Fixing a bug with tokio::select
  • [8] OBHPOIUH Modernising the API with async traits
  • [9] WX7UY5KK Adding support for aes256-gcm
  • [10] 7FRJYUI6 Reboot because of a bad change
  • [11] UHAEQPZU Support ecdsa-sha2-nistp256 keys for authentication
  • [12] WXZWQLGL Correct negotiation without OpenSSL

Change contents

  • replacement in thrussh/src/negotiation.rs at line 56
    [3.270058][3.270058:270075](),[3.4473][3.4473:4617]()
    impl Preferred {
    pub fn compressed(self) -> Self {
    Self {
    compression: &["zlib", "zlib@openssh.com"],
    ..self
    }
    }
    [3.270058]
    [3.4617]
    macro_rules! mac {
    () => {{ &[mac::hmac_sha256::NAME, mac::Name("none")] }};
    }
  • replacement in thrussh/src/negotiation.rs at line 60
    [3.4618][3.15271:15326](),[3.270075][3.15271:15326](),[3.15326][3.136:190](),[3.190][3.214:239](),[3.239][3.190:254](),[3.190][3.190:254]()
    #[cfg(all(feature = "openssl", feature = "p256"))]
    pub const DEFAULT_SERVER: Preferred = Preferred {
    is_server: true,
    kex: &[kex::CURVE25519, kex::STRICT_S],
    key: &[
    [3.4618]
    [3.254]
    #[cfg(all(feature = "openssl", feature = "p256"))]
    macro_rules! key {
    () => {
    &[
  • replacement in thrussh/src/negotiation.rs at line 68
    [3.380][3.380:391](),[3.391][3.0:77](),[3.77][2.1620:1655](),[2.1655][3.466:526](),[3.466][3.466:526]()
    ],
    cipher: &[cipher::chacha20poly1305::NAME, cipher::aes256_gcm::NAME],
    mac: &[mac::Name("none")],
    compression: &["none", "zlib", "zlib@openssh.com"],
    [3.380]
    [3.526]
    ]
  • replacement in thrussh/src/negotiation.rs at line 70
    [3.533][3.533:642](),[3.642][3.240:266](),[3.266][3.642:690](),[3.642][3.642:690](),[3.690][3.15406:15559](),[3.1101][3.15406:15559](),[3.15406][3.15406:15559](),[3.15559][3.78:155](),[3.155][2.1656:1715](),[2.1715][3.15634:15694](),[3.15634][3.15634:15694]()
    #[cfg(all(feature = "openssl", feature = "p256"))]
    pub const DEFAULT_CLIENT: Preferred = Preferred {
    is_server: false,
    kex: &[kex::CURVE25519, kex::STRICT_C],
    key: &[
    key::ED25519,
    key::ECDSA_SHA2_NISTP256,
    key::RSA_SHA2_256,
    key::RSA_SHA2_512,
    ],
    cipher: &[cipher::chacha20poly1305::NAME, cipher::aes256_gcm::NAME],
    mac: &[mac::hmac_sha256::NAME, mac::Name("none")],
    compression: &["none", "zlib", "zlib@openssh.com"],
    [3.533]
    [3.15694]
    }
    #[cfg(all(feature = "openssl", not(feature = "p256")))]
    macro_rules! key {
    () => {
    &[key::ED25519, key::RSA_SHA2_256, key::RSA_SHA2_512]
  • edit in thrussh/src/negotiation.rs at line 77
    [3.15701]
    [3.15701]
    }
  • replacement in thrussh/src/negotiation.rs at line 79
    [3.15702][3.15702:15762](),[3.15762][3.691:745](),[3.745][3.267:292](),[3.292][3.745:900](),[3.745][3.745:900](),[3.900][2.1716:1751](),[2.1751][3.924:984](),[3.924][3.924:984]()
    #[cfg(all(not(feature = "openssl"), feature = "p256"))]
    pub const DEFAULT_SERVER: Preferred = Preferred {
    is_server: true,
    kex: &[kex::CURVE25519, kex::STRICT_S],
    key: &[key::ED25519, key::ECDSA_SHA2_NISTP256],
    cipher: &[cipher::chacha20poly1305::NAME],
    mac: &[mac::Name("none")],
    compression: &["none", "zlib", "zlib@openssh.com"],
    [3.15702]
    [3.984]
    #[cfg(all(not(feature = "openssl"), feature = "p256"))]
    macro_rules! key {
    () => {
    &[key::ED25519, key::ECDSA_SHA2_NISTP256]
  • edit in thrussh/src/negotiation.rs at line 84
    [3.991]
    [3.991]
    }
  • replacement in thrussh/src/negotiation.rs at line 86
    [3.992][3.992:1106](),[3.1106][3.293:319](),[3.319][3.1106:1154](),[3.1106][3.1106:1154](),[3.1148][3.15842:15949](),[3.1154][3.15842:15949](),[3.15842][3.15842:15949](),[3.15949][2.1752:1787](),[2.1787][3.15973:16033](),[3.15973][3.15973:16033]()
    #[cfg(all(not(feature = "openssl"), feature = "p256"))]
    pub const DEFAULT_CLIENT: Preferred = Preferred {
    is_server: false,
    kex: &[kex::CURVE25519, kex::STRICT_C],
    key: &[key::ED25519, key::ECDSA_SHA2_NISTP256],
    cipher: &[cipher::chacha20poly1305::NAME],
    mac: &[mac::Name("none")],
    compression: &["none", "zlib", "zlib@openssh.com"],
    [3.992]
    [3.16033]
    #[cfg(all(not(feature = "openssl"), not(feature = "p256")))]
    macro_rules! key {
    () => {
    &[key::ED25519]
  • edit in thrussh/src/negotiation.rs at line 91
    [3.16040]
    [3.16040]
    }
  • replacement in thrussh/src/negotiation.rs at line 93
    [3.16041][3.16041:16101](),[3.16101][3.1155:1209](),[3.1209][3.320:345](),[3.345][3.1209:1325](),[3.1209][3.1209:1325](),[3.1325][3.156:233](),[3.233][2.1788:1847](),[2.1847][3.1400:1460](),[3.1400][3.1400:1460]()
    #[cfg(all(feature = "openssl", not(feature = "p256")))]
    pub const DEFAULT_SERVER: Preferred = Preferred {
    is_server: true,
    kex: &[kex::CURVE25519, kex::STRICT_S],
    key: &[key::ED25519, key::RSA_SHA2_256, key::RSA_SHA2_512],
    cipher: &[cipher::chacha20poly1305::NAME, cipher::aes256_gcm::NAME],
    mac: &[mac::hmac_sha256::NAME, mac::Name("none")],
    compression: &["none", "zlib", "zlib@openssh.com"],
    [3.16041]
    [3.1460]
    #[cfg(feature = "openssl")]
    macro_rules! cipher {
    () => {
    &[
    cipher::chacha20poly1305::NAME,
    cipher::aes256_gcm::NAME,
    cipher::aes256_ctr::NAME,
    ]
  • edit in thrussh/src/negotiation.rs at line 102
    [3.1467]
    [3.1467]
    }
  • replacement in thrussh/src/negotiation.rs at line 104
    [3.1468][3.1468:1582](),[3.1582][3.346:372](),[3.372][3.1582:1630](),[3.1582][3.1582:1630](),[3.1195][3.270155:270223](),[3.1630][3.270155:270223](),[3.270155][3.270155:270223](),[3.270223][3.234:311](),[3.311][2.1848:1907](),[2.1907][3.108:168](),[3.108][3.108:168]()
    #[cfg(all(feature = "openssl", not(feature = "p256")))]
    pub const DEFAULT_CLIENT: Preferred = Preferred {
    is_server: false,
    kex: &[kex::CURVE25519, kex::STRICT_C],
    key: &[key::ED25519, key::RSA_SHA2_256, key::RSA_SHA2_512],
    cipher: &[cipher::chacha20poly1305::NAME, cipher::aes256_gcm::NAME],
    mac: &[mac::hmac_sha256::NAME, mac::Name("none")],
    compression: &["none", "zlib", "zlib@openssh.com"],
    [3.1468]
    [3.168]
    #[cfg(not(feature = "openssl"))]
    macro_rules! cipher {
    () => {
    &[cipher::chacha20poly1305::NAME]
  • edit in thrussh/src/negotiation.rs at line 109
    [3.175]
    [3.175]
    }
  • replacement in thrussh/src/negotiation.rs at line 111
    [3.176][3.16102:16167]()
    #[cfg(all(not(feature = "openssl"), not(feature = "p256")))]
    [3.176]
    [3.1631]
    impl Preferred {
    pub fn compressed(self) -> Self {
    Self {
    compression: &["zlib", "zlib@openssh.com"],
    ..self
    }
    }
  • replacement in thrussh/src/negotiation.rs at line 122
    [3.1242][3.293:323](),[3.1733][3.293:323](),[3.293][3.293:323](),[3.323][3.270223:270274](),[3.270223][3.270223:270274](),[3.270274][2.1908:1943]()
    key: &[key::ED25519],
    cipher: &[cipher::chacha20poly1305::NAME],
    mac: &[mac::Name("none")],
    [3.1733]
    [3.270298]
    key: key!(),
    cipher: cipher!(),
    mac: mac!(),
  • edit in thrussh/src/negotiation.rs at line 128
    [3.270366][3.1734:1799]()
    #[cfg(all(not(feature = "openssl"), not(feature = "p256")))]
  • replacement in thrussh/src/negotiation.rs at line 129
    [3.1853][3.399:425](),[3.425][3.1853:1931](),[3.1853][3.1853:1931](),[3.1931][3.270517:270568](),[3.16321][3.270517:270568](),[3.270517][3.270517:270568](),[3.270568][2.1944:1979]()
    is_server: false,
    kex: &[kex::CURVE25519, kex::STRICT_C],
    key: &[key::ED25519],
    cipher: &[cipher::chacha20poly1305::NAME],
    mac: &[mac::Name("none")],
    [3.1853]
    [3.1932]
    is_server: true,
    kex: &[kex::CURVE25519, kex::STRICT_S],
    key: key!(),
    cipher: cipher!(),
    mac: mac!(),
  • edit in thrussh/src/mac/mod.rs at line 76
    [2.4751]
    [2.4751]
    debug!("out {:?} h {:?}", out, h);
  • edit in thrussh/src/cipher/mod.rs at line 18
    [3.9376][3.409737:409776](),[3.409737][3.409737:409776]()
    use byteorder::{BigEndian, ByteOrder};
  • replacement in thrussh/src/cipher/mod.rs at line 104
    [3.411340][3.411340:411433]()
    fn decrypt_packet_length(&self, seqn: u32, encrypted_packet_length: [u8; 4]) -> [u8; 4];
    [3.411340]
    [3.411433]
    fn length_block_size(&self) -> usize;
    fn decrypt_packet_length(&self, seqn: u32, encrypted_packet_length: &[u8]) -> u32;
  • edit in thrussh/src/cipher/mod.rs at line 127
    [3.411887]
    [3.411887]
    const MAX_BLOCK_SIZE: usize = 16;
  • replacement in thrussh/src/cipher/mod.rs at line 134
    [3.23216][3.50:149](),[3.412048][3.50:149]()
    if buffer.len == 0 {
    let mut len = [0; 4];
    stream.read_exact(&mut len).await?;
    [3.23216]
    [3.149]
    let len_block_size = if buffer.len == 0 {
    let mut len = [0; MAX_BLOCK_SIZE];
    let len = {
    let key = pair.remote_to_local.as_opening_key();
    &mut len[..key.length_block_size()]
    };
    stream.read_exact(len).await?;
  • replacement in thrussh/src/cipher/mod.rs at line 149
    [3.487][3.487:564]()
    buffer.len = BigEndian::read_u32(&len) as usize + key.tag_len();
    [3.487]
    [3.564]
    buffer.len = len as usize + key.tag_len();
  • edit in thrussh/src/cipher/mod.rs at line 151
    [3.625]
    [3.625]
    key.length_block_size()
  • replacement in thrussh/src/cipher/mod.rs at line 153
    [3.635][3.412515:412521](),[3.412515][3.412515:412521]()
    }
    [3.635]
    [3.636]
    } else {
    0
    };
  • replacement in thrussh/src/cipher/mod.rs at line 158
    [3.725][3.412521:412576](),[3.412521][3.412521:412576]()
    stream.read_exact(&mut buffer.buffer[4..]).await?;
    [3.725]
    [3.726]
    stream.read_exact(&mut buffer.buffer[len_block_size..]).await?;
  • edit in thrussh/src/cipher/clear.rs at line 17
    [3.415552]
    [3.415552]
    use byteorder::{BigEndian, ByteOrder};
  • replacement in thrussh/src/cipher/clear.rs at line 23
    [3.415620][3.415620:415727]()
    fn decrypt_packet_length(&self, _seqn: u32, packet_length: [u8; 4]) -> [u8; 4] {
    packet_length
    [3.415620]
    [3.415727]
    fn length_block_size(&self) -> usize {
    4
    }
    fn decrypt_packet_length(&self, _seqn: u32, packet_length: &[u8]) -> u32 {
    BigEndian::read_u32(packet_length)
  • edit in thrussh/src/cipher/chacha20poly1305.rs at line 67
    [3.418984]
    [3.418984]
    fn length_block_size(&self) -> usize {
    4
    }
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 74
    [3.419059][3.419059:419124]()
    mut encrypted_packet_length: [u8; 4],
    ) -> [u8; 4] {
    [3.419059]
    [3.419124]
    encrypted_packet_length: &[u8],
    ) -> u32 {
    let mut out = [0; 4];
    out.clone_from_slice(encrypted_packet_length);
  • replacement in thrussh/src/cipher/chacha20poly1305.rs at line 79
    [3.419175][3.419175:419277]()
    chacha20_xor(&mut encrypted_packet_length, &nonce, &self.k1);
    encrypted_packet_length
    [3.419175]
    [3.419277]
    chacha20_xor(&mut out, &nonce, &self.k1);
    BigEndian::read_u32(&out)
  • edit in thrussh/src/cipher/aes256_gcm.rs at line 65
    [3.5790]
    [3.5790]
    fn length_block_size(&self) -> usize {
    4
    }
  • replacement in thrussh/src/cipher/aes256_gcm.rs at line 71
    [3.5866][3.5866:5959]()
    encrypted_packet_length: [u8; 4],
    ) -> [u8; 4] {
    encrypted_packet_length
    [3.5866]
    [3.5959]
    encrypted_packet_length: &[u8],
    ) -> u32 {
    BigEndian::read_u32(&encrypted_packet_length[..4])
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 4
    [2.12229]
    [2.12229]
    use byteorder::{BigEndian, ByteOrder};
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 12
    [2.12401]
    [2.12401]
    length: Mutex<[u8; 16]>,
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 36
    [2.13013]
    [2.13013]
    length: Mutex::new([0; 16]),
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 53
    [2.13477]
    [2.13477]
    length: Mutex::new([0; 16]),
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 89
    [2.14680]
    [2.14680]
    fn length_block_size(&self) -> usize { 16 }
  • replacement in thrussh/src/cipher/aes256_ctr.rs at line 93
    [2.14756][2.14756:14847]()
    encrypted_packet_length: [u8; 4],
    ) -> [u8; 4] {
    let mut out = [0; 4];
    [2.14756]
    [2.14847]
    encrypted_packet_length: &[u8],
    ) -> u32 {
    let mut out = self.length.lock().unwrap();
  • replacement in thrussh/src/cipher/aes256_ctr.rs at line 97
    [2.14903][2.14903:14984]()
    let count = crypter.update(&encrypted_packet_length, &mut out).unwrap();
    [2.14903]
    [2.14984]
    let count = crypter.update(&encrypted_packet_length, &mut out[..]).unwrap();
  • replacement in thrussh/src/cipher/aes256_ctr.rs at line 99
    [2.15038][2.15038:15050]()
    out
    [2.15038]
    [2.15050]
    debug!("packet length {:?}", out);
    BigEndian::read_u32(&out[..4])
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 113
    [2.15275]
    [2.15275]
    debug!("payload = {:?}", payload);
  • replacement in thrussh/src/cipher/aes256_ctr.rs at line 118
    [2.15464][2.15464:15523]()
    let count = c.update(&payload, &mut out).unwrap();
    [2.15464]
    [2.15523]
    (&mut out[..16]).clone_from_slice(&self.length.lock().unwrap()[..]);
    let count = c.update(&payload[16..], &mut out[16..]).unwrap();
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 122
    [2.15572]
    [2.15572]
    debug!("out = {:?}", &out[..]);
    debug!("tag = {:?}", tag);
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 125
    [2.15630]
    [2.15630]
    error!("WRONG MAC");
  • edit in thrussh/src/cipher/aes256_ctr.rs at line 128
    [2.15688]
    [2.15688]