thrussh 0.40.5

A client and server SSH library.
Documentation
// 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.
//

use super::super::Error;
use byteorder::{BigEndian, ByteOrder};
use openssl::symm::Cipher;
use rand::Rng;
use std::sync::atomic::{AtomicU64, Ordering};

pub const NAME: super::Name = super::Name("aes256-gcm@openssh.com");

pub struct Key {
    cipher: openssl::symm::Cipher,
    key: [u8; 32],
    iv: (u32, AtomicU64),
}

pub static CIPHER: super::Cipher = super::Cipher {
    _name: NAME,
    key_len: 32,
    iv_len: 12,
    make_sealing_cipher,
    make_opening_cipher,
};

fn make_sealing_cipher(k: &[u8], i: &[u8], _: crate::mac::MacKey) -> super::SealingCipher {
    let mut key = [0; 32];
    key.clone_from_slice(k);
    let (a, b) = i.split_at(4);
    let a = BigEndian::read_u32(&a);
    let b = BigEndian::read_u64(&b);
    super::SealingCipher::Aes256Gcm(Key {
        key,
        iv: (a, b.into()),
        cipher: Cipher::aes_256_gcm(),
    })
}

fn make_opening_cipher(k: &[u8], i: &[u8], _: crate::mac::MacKey) -> super::OpeningCipher {
    let mut key = [0; 32];
    key.clone_from_slice(k);
    let (a, b) = i.split_at(4);
    let a = BigEndian::read_u32(&a);
    let b = BigEndian::read_u64(&b);
    super::OpeningCipher::Aes256Gcm(Key {
        key,
        iv: (a, b.into()),
        cipher: Cipher::aes_256_gcm(),
    })
}

impl super::OpeningKey for Key {
    fn length_block_size(&self) -> usize {
        4
    }
    fn decrypt_packet_length(
        &self,
        _sequence_number: u32,
        encrypted_packet_length: &[u8],
    ) -> u32 {
        BigEndian::read_u32(&encrypted_packet_length[..4])
    }

    fn tag_len(&self) -> usize {
        16
    }

    fn open<'a>(
        &self,
        _sequence_number: u32,
        payload: &'a mut [u8],
        tag: usize,
    ) -> Result<&'a [u8], Error> {
        let (payload, tag) = payload.split_at_mut(tag);
        let (aad, data) = payload.split_at_mut(4);
        let mut iv = [0; 12];
        {
            let (a, b) = iv.split_at_mut(4);
            BigEndian::write_u32(a, self.iv.0);
            let counter = self.iv.1.fetch_add(1, Ordering::Relaxed);
            BigEndian::write_u64(b, counter);
        }
        let mut tag_ = [0; 16];
        tag_.clone_from_slice(tag);
        crypt_aead(
            self.cipher,
            openssl::symm::Mode::Decrypt,
            &self.key,
            Some(&iv),
            aad,
            data,
            &mut tag_,
        )
        .unwrap();
        Ok(payload)
    }
}

impl super::SealingKey for Key {
    fn padding_length(&self, payload: &[u8]) -> usize {
        let encrypted_len = payload.len() + 1;
        let padding = 16 - (encrypted_len % 16);
        let min_padding = if padding < 4 { padding + 16 } else { padding };
        let mut rng = rand::rng();
        (rng.random::<u8>() & 0xf0 - 16) as usize + min_padding
    }

    fn fill_padding(&self, padding_out: &mut [u8]) {
        openssl::rand::rand_bytes(padding_out).unwrap()
    }

    fn tag_len(&self) -> usize {
        16
    }

    /// Append an encrypted packet with contents `packet_content` at
    /// the end of `buffer`.
    fn seal(&self, _sequence_number: u32, payload: &mut [u8], tag_out: usize) {
        let (payload, tag_out) = payload.split_at_mut(tag_out);
        let (aad, data) = payload.split_at_mut(4);
        let mut iv = [0; 12];
        {
            let (a, b) = iv.split_at_mut(4);
            BigEndian::write_u32(a, self.iv.0);
            let counter = self.iv.1.fetch_add(1, Ordering::Relaxed);
            BigEndian::write_u64(b, counter);
        }
        crypt_aead(
            self.cipher,
            openssl::symm::Mode::Encrypt,
            &self.key,
            Some(&iv),
            aad,
            data,
            tag_out,
        )
        .unwrap()
    }
}

// The following is copied from the OpenSSL crate, with the addition
// of a thread-local buffer and less genericity on encryption modes.

use std::cell::RefCell;

thread_local! {
    static BUF: RefCell<cryptovec::CryptoVec> = RefCell::new(cryptovec::CryptoVec::new());
}

fn crypt_aead(
    t: openssl::symm::Cipher,
    mode: openssl::symm::Mode,
    key: &[u8],
    iv: Option<&[u8]>,
    aad: &[u8],
    data: &mut [u8],
    tag: &mut [u8],
) -> Result<(), openssl::error::ErrorStack> {
    let mut c = openssl::symm::Crypter::new(t, mode, key, iv)?;
    BUF.with(|buffer| {
        let mut buffer = buffer.borrow_mut();
        buffer.resize(data.len() + t.block_size());
        c.aad_update(aad)?;
        let count = c.update(data, &mut buffer)?;
        let rest = if let openssl::symm::Mode::Encrypt = mode {
            let rest = c.finalize(&mut buffer[count..])?;
            c.get_tag(tag)?;
            rest
        } else {
            c.set_tag(tag)?;
            c.finalize(&mut buffer[count..])?
        };
        data.clone_from_slice(&buffer[..count + rest]);
        buffer.clear();
        Ok(())
    })
}