thrussh 0.40.5

A client and server SSH library.
Documentation
use openssl::symm::{Cipher, Crypter};
use rand::Rng;
use std::sync::Mutex;
use byteorder::{BigEndian, ByteOrder};

pub const NAME: super::Name = super::Name("aes256-ctr");

pub struct Key {
    mac: crate::mac::MacKey,
    crypter: Mutex<Crypter>,
    buf: Mutex<cryptovec::CryptoVec>,
    length: Mutex<[u8; 16]>,
}

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()),
        length: Mutex::new([0; 16]),
    })
}

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()),
        length: Mutex::new([0; 16]),
    })
}

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 length_block_size(&self) -> usize { 16 }
    fn decrypt_packet_length(
        &self,
        _sequence_number: u32,
        encrypted_packet_length: &[u8],
    ) -> u32 {
        let mut out = self.length.lock().unwrap();
        let mut crypter = self.crypter.lock().unwrap();
        let count = crypter.update(&encrypted_packet_length, &mut out[..]).unwrap();
        crypter.finalize(&mut out[count..]).unwrap();
        debug!("packet length {:?}", out);
        BigEndian::read_u32(&out[..4])
    }

    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> {
        debug!("payload = {:?}", payload);
        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();
        (&mut out[..16]).clone_from_slice(&self.length.lock().unwrap()[..]);
        let count = c.update(&payload[16..], &mut out[16..]).unwrap();
        c.finalize(&mut out[count..]).unwrap();

        debug!("out = {:?}", &out[..]);
        debug!("tag = {:?}", tag);
        if !self.mac.verify(sequence_number, &out, tag) {
            error!("WRONG MAC");
            return Err(crate::Error::WrongMAC);
        }

        payload.clone_from_slice(&out);
        Ok(payload)
    }
}