LYRZREY75IOPQJ3DMJJB4SINAEVI5P5TE32SSFO7QKFBDYVXXKNAC
5EV2X6V42RKTOER62WQPTLMCES74WAIZI6IR7IJJPFYQ7Q4VMTTAC
WX7UY5KKUXOHBGPNWJC6ZQDQ3LFGNTKGF6ZQFIPYNI6H5IMWGOYAC
SKTE2HF665D2NL7TVZAADZDKHLKZKKEC7CLNKSYSJM36TRBWWVZQC
7FRJYUI62VW257VVFQXND6OKSAILVTHGEJCXFE6CG6FIOIUTDVYAC
OBHPOIUH2CPAB2ORVMOGAAQVN4NYP6GHGTIODEQ46VUFNZIUAIRQC
Q323RFJSTFYUJ5FPTKT4NI7DK3KDC3O4YLBDEYWKEYCA7G276SUQC
MCS77Y4VJGB6TU2HOLASGSRW4B6MT74XABD4KYALIRS54GGN2DDQC
NHOSLQGG4CIWBE7VKL5MB7PSY3RZ5IVDFENMGZG6X755GGZ6B3VQC
D6H7OWTTMHHX6BTB3B6MNBOBX2L66CBL4LGSEUSAI2MCRCJDQFRQC
UHAEQPZUODJ5YVBZJPPJVLO7EBW6DC2JXHQBN26ARELAVULG3JUQC
WXZWQLGLNQ3QRHMBH7YPTHKSPUST4HD34H3PW5E4LHQYH3KLNHYAC
2WEO7OZLWJJPUYK4WXLT5FD46G2MAEIHEYMDW5GASCBUNKOPXCVAC
Y67GNDVBCXX5V3SL3OAQCU3NX52OR34NXK7ARKHK7EQDGCLVTHTQC
}
}
}
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);
let mac = Self::select(pref.mac, r.read_string()?);
let mac = mac.and_then(|(_, x)| Some(x));
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;
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)
}
}
let compute_key = |c, key: &mut CryptoVec, len| -> Result<(), crate::Error> {
let mut buffer = buffer.borrow_mut();
buffer.clear();
key.clear();
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());
if let Some(ref shared) = self.shared_secret {
buffer.extend_ssh_mpint(&shared.0);
}
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(())
};
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()
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')
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());
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, local_to_remote_iv, remote_to_local, remote_to_local_iv) =
if is_server {
(b'D', b'B', b'C', b'A')
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)
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);
let local_to_remote =
(cipher.make_sealing_cipher)(&key, &iv, local_to_remote_mac);
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);
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);
pub make_opening_cipher: fn(key: &[u8], iv: &[u8]) -> OpeningCipher,
pub make_sealing_cipher: fn(key: &[u8], iv: &[u8]) -> SealingCipher,
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,
fn seal(&self, _seqn: u32, _plaintext_in_ciphertext_out: &mut [u8], tag_out: &mut [u8]) {
debug_assert_eq!(tag_out.len(), self.tag_len());
fn seal(&self, _seqn: u32, plaintext_in_ciphertext_out: &mut [u8], tag_out: usize) {
debug_assert_eq!(tag_out, plaintext_in_ciphertext_out.len());
fn seal(
&self,
sequence_number: u32,
plaintext_in_ciphertext_out: &mut [u8],
tag_out: &mut [u8],
) {
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);
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)
}
}