use ring;
use std::io::Write;
use msgs::codec;
use msgs::codec::Codec;
use msgs::message::{Message, MessagePayload};
use msgs::fragmenter::MAX_FRAGMENT_LEN;
use error::TLSError;
use session::SessionSecrets;
use suites::{SupportedCipherSuite, BulkAlgorithm};
fn xor(accum: &mut [u8], offset: &[u8]) {
for i in 0..accum.len() {
accum[i] ^= offset[i];
}
}
pub trait MessageCipher {
fn decrypt(&self, m: Message, seq: u64) -> Result<Message, TLSError>;
fn encrypt(&self, m: Message, seq: u64) -> Result<Message, TLSError>;
}
impl MessageCipher {
pub fn invalid() -> Box<MessageCipher + Send + Sync> {
Box::new(InvalidMessageCipher {})
}
pub fn new(scs: &'static SupportedCipherSuite, secrets: &SessionSecrets) -> Box<MessageCipher + Send + Sync> {
let key_block = secrets.make_key_block(scs.key_block_len());
let mut offs = 0;
let client_write_mac_key = &key_block[offs..offs+scs.mac_key_len]; offs += scs.mac_key_len;
let server_write_mac_key = &key_block[offs..offs+scs.mac_key_len]; offs += scs.mac_key_len;
let client_write_key = &key_block[offs..offs+scs.enc_key_len]; offs += scs.enc_key_len;
let server_write_key = &key_block[offs..offs+scs.enc_key_len]; offs += scs.enc_key_len;
let client_write_iv = &key_block[offs..offs+scs.fixed_iv_len]; offs += scs.fixed_iv_len;
let server_write_iv = &key_block[offs..offs+scs.fixed_iv_len]; offs += scs.fixed_iv_len;
let explicit_nonce_offs = &key_block[offs..offs+scs.explicit_nonce_len];
let (write_mac_key, write_key, write_iv) = if secrets.randoms.we_are_client {
(client_write_mac_key, client_write_key, client_write_iv)
} else {
(server_write_mac_key, server_write_key, server_write_iv)
};
let (read_mac_key, read_key, read_iv) = if secrets.randoms.we_are_client {
(server_write_mac_key, server_write_key, server_write_iv)
} else {
(client_write_mac_key, client_write_key, client_write_iv)
};
let aead_alg = scs.get_aead_alg();
if scs.bulk == BulkAlgorithm::CHACHA20_POLY1305 {
Box::new(ChaCha20Poly1305MessageCipher::new(aead_alg,
write_mac_key, write_key, write_iv,
read_mac_key, read_key, read_iv))
} else {
Box::new(GCMMessageCipher::new(aead_alg,
write_mac_key, write_key, write_iv,
read_mac_key, read_key, read_iv,
explicit_nonce_offs))
}
}
}
pub struct GCMMessageCipher {
alg: &'static ring::aead::Algorithm,
enc_key: ring::aead::SealingKey,
enc_salt: [u8; 4],
dec_key: ring::aead::OpeningKey,
dec_salt: [u8; 4],
nonce_offset: [u8; 8]
}
const GCM_EXPLICIT_NONCE_LEN: usize = 8;
const GCM_OVERHEAD: usize = GCM_EXPLICIT_NONCE_LEN + 16;
impl MessageCipher for GCMMessageCipher {
fn decrypt(&self, mut msg: Message, seq: u64) -> Result<Message, TLSError> {
let payload = try!(msg.take_opaque_payload().ok_or(TLSError::DecryptError));
let mut buf = payload.0;
if buf.len() < GCM_OVERHEAD {
return Err(TLSError::DecryptError);
}
let mut nonce = [0u8; 12];
nonce.as_mut().write(&self.dec_salt).unwrap();
nonce[4..].as_mut().write(&buf).unwrap();
let mut aad = Vec::new();
codec::encode_u64(seq, &mut aad);
msg.typ.encode(&mut aad);
msg.version.encode(&mut aad);
codec::encode_u16((buf.len() - GCM_OVERHEAD) as u16, &mut aad);
let plain_len = try!(
ring::aead::open_in_place(&self.dec_key,
&nonce,
GCM_EXPLICIT_NONCE_LEN,
&mut buf,
&aad)
.map_err(|_| TLSError::DecryptError)
);
if plain_len > MAX_FRAGMENT_LEN {
let msg = "peer sent oversized fragment".to_string();
return Err(TLSError::PeerMisbehavedError(msg));
}
buf.truncate(plain_len);
Ok(
Message {
typ: msg.typ,
version: msg.version,
payload: MessagePayload::opaque(buf)
}
)
}
fn encrypt(&self, msg: Message, seq: u64) -> Result<Message, TLSError> {
let mut nonce = [0u8; 12];
nonce.as_mut().write(&self.enc_salt).unwrap();
codec::put_u64(seq, &mut nonce[4..]);
xor(&mut nonce[4..], &self.nonce_offset);
let typ = msg.typ;
let version = msg.version;
let mut buf = msg.take_payload();
let payload_len = buf.len();
let tag_len = self.alg.max_overhead_len();
let want_len = buf.len() + tag_len;
buf.resize(want_len, 0u8);
let mut aad = Vec::new();
codec::encode_u64(seq, &mut aad);
typ.encode(&mut aad);
version.encode(&mut aad);
codec::encode_u16(payload_len as u16, &mut aad);
try!(
ring::aead::seal_in_place(&self.enc_key,
&nonce,
&mut buf,
tag_len,
&aad)
.map_err(|_| TLSError::General("encrypt failed".to_string()))
);
let mut result = Vec::new();
result.extend_from_slice(&nonce[4..]);
result.extend_from_slice(&buf);
Ok(Message {
typ: typ,
version: version,
payload: MessagePayload::opaque(result)
})
}
}
impl GCMMessageCipher {
fn new(alg: &'static ring::aead::Algorithm,
_enc_mac_key: &[u8], enc_key: &[u8], enc_iv: &[u8],
_dec_mac_key: &[u8], dec_key: &[u8], dec_iv: &[u8],
nonce_offset: &[u8]) -> GCMMessageCipher {
let mut ret = GCMMessageCipher {
alg: alg,
enc_key: ring::aead::SealingKey::new(alg, enc_key).unwrap(),
enc_salt: [0u8; 4],
dec_key: ring::aead::OpeningKey::new(alg, dec_key).unwrap(),
dec_salt: [0u8; 4],
nonce_offset: [0u8; 8]
};
debug_assert_eq!(enc_iv.len(), 4);
debug_assert_eq!(dec_iv.len(), 4);
debug_assert_eq!(nonce_offset.len(), 8);
ret.enc_salt.as_mut().write(enc_iv).unwrap();
ret.dec_salt.as_mut().write(dec_iv).unwrap();
ret.nonce_offset.as_mut().write(nonce_offset).unwrap();
ret
}
}
pub struct ChaCha20Poly1305MessageCipher {
alg: &'static ring::aead::Algorithm,
enc_key: ring::aead::SealingKey,
enc_offset: [u8; 12],
dec_key: ring::aead::OpeningKey,
dec_offset: [u8; 12]
}
impl ChaCha20Poly1305MessageCipher {
fn new(alg: &'static ring::aead::Algorithm,
_enc_mac_key: &[u8], enc_key: &[u8], enc_iv: &[u8],
_dec_mac_key: &[u8], dec_key: &[u8], dec_iv: &[u8]) -> ChaCha20Poly1305MessageCipher {
let mut ret = ChaCha20Poly1305MessageCipher {
alg: alg,
enc_key: ring::aead::SealingKey::new(alg, enc_key).unwrap(),
enc_offset: [0u8; 12],
dec_key: ring::aead::OpeningKey::new(alg, dec_key).unwrap(),
dec_offset: [0u8; 12]
};
ret.enc_offset.as_mut().write(enc_iv).unwrap();
ret.dec_offset.as_mut().write(dec_iv).unwrap();
ret
}
}
const CP_OVERHEAD: usize = 16;
impl MessageCipher for ChaCha20Poly1305MessageCipher {
fn decrypt(&self, mut msg: Message, seq: u64) -> Result<Message, TLSError> {
let payload = try!(msg.take_opaque_payload().ok_or(TLSError::DecryptError));
let mut buf = payload.0;
if buf.len() < CP_OVERHEAD {
return Err(TLSError::DecryptError);
}
let mut nonce = [0u8; 12];
codec::put_u64(seq, &mut nonce[4..]);
xor(&mut nonce, &self.dec_offset);
let mut aad = Vec::new();
codec::encode_u64(seq, &mut aad);
msg.typ.encode(&mut aad);
msg.version.encode(&mut aad);
codec::encode_u16((buf.len() - CP_OVERHEAD) as u16, &mut aad);
let plain_len = try!(
ring::aead::open_in_place(&self.dec_key,
&nonce,
0,
&mut buf,
&aad)
.map_err(|_| TLSError::DecryptError)
);
if plain_len > MAX_FRAGMENT_LEN {
let err_msg = "peer sent oversized fragment".to_string();
return Err(TLSError::PeerMisbehavedError(err_msg));
}
buf.truncate(plain_len);
Ok(
Message {
typ: msg.typ,
version: msg.version,
payload: MessagePayload::opaque(buf)
}
)
}
fn encrypt(&self, msg: Message, seq: u64) -> Result<Message, TLSError> {
let mut nonce = [0u8; 12];
codec::put_u64(seq, &mut nonce[4..]);
xor(&mut nonce, &self.enc_offset);
let typ = msg.typ;
let version = msg.version;
let mut buf = msg.take_payload();
let payload_len = buf.len();
let tag_len = self.alg.max_overhead_len();
let want_len = buf.len() + tag_len;
buf.resize(want_len, 0u8);
let mut aad = Vec::new();
codec::encode_u64(seq, &mut aad);
typ.encode(&mut aad);
version.encode(&mut aad);
codec::encode_u16(payload_len as u16, &mut aad);
try!(
ring::aead::seal_in_place(&self.enc_key,
&nonce,
&mut buf,
tag_len,
&aad)
.map_err(|_| TLSError::General("encrypt failed".to_string()))
);
Ok(Message {
typ: typ,
version: version,
payload: MessagePayload::opaque(buf)
})
}
}
pub struct InvalidMessageCipher {}
impl MessageCipher for InvalidMessageCipher {
fn decrypt(&self, _m: Message, _seq: u64) -> Result<Message, TLSError> {
Err(TLSError::DecryptError)
}
fn encrypt(&self, _m: Message, _seq: u64) -> Result<Message, TLSError> {
Err(TLSError::General("encrypt not yet available".to_string()))
}
}