use std::io::Cursor;
use std::sync::Arc;
use crypto::{AeadAlgorithm, AeadCipher, RTCCryptoProvider};
use super::*;
use crate::content::*;
use crate::record_layer::record_layer_header::*;
use shared::error::*;
const CRYPTO_CCM_NONCE_LENGTH: usize = 12;
#[derive(Clone)]
pub enum CryptoCcmTagLen {
CryptoCcm8TagLength,
CryptoCcmTagLength,
}
pub struct CryptoCcm {
provider: Arc<dyn RTCCryptoProvider>,
local_ccm: Box<dyn AeadCipher>,
remote_ccm: Box<dyn AeadCipher>,
local_write_iv: Vec<u8>,
remote_write_iv: Vec<u8>,
}
impl CryptoCcm {
pub fn new(
provider: Arc<dyn RTCCryptoProvider>,
tag_len: &CryptoCcmTagLen,
local_key: &[u8],
local_write_iv: &[u8],
remote_key: &[u8],
remote_write_iv: &[u8],
) -> Result<Self> {
let algorithm = match tag_len {
CryptoCcmTagLen::CryptoCcmTagLength => AeadAlgorithm::Aes128Ccm,
CryptoCcmTagLen::CryptoCcm8TagLength => AeadAlgorithm::Aes128Ccm8,
};
let local_ccm = provider
.crypto()
.new_aead(algorithm, local_key)
.map_err(crypto_error)?;
let remote_ccm = provider
.crypto()
.new_aead(algorithm, remote_key)
.map_err(crypto_error)?;
Ok(CryptoCcm {
provider,
local_ccm,
local_write_iv: local_write_iv.to_vec(),
remote_ccm,
remote_write_iv: remote_write_iv.to_vec(),
})
}
pub fn encrypt(&mut self, pkt_rlh: &RecordLayerHeader, raw: &[u8]) -> Result<Vec<u8>> {
let payload = &raw[RECORD_LAYER_HEADER_SIZE..];
let raw = &raw[..RECORD_LAYER_HEADER_SIZE];
let mut nonce = [0u8; CRYPTO_CCM_NONCE_LENGTH];
nonce[..4].copy_from_slice(&self.local_write_iv[..4]);
self.provider
.random()
.fill(&mut nonce[4..])
.map_err(crypto_error)?;
let additional_data = generate_aead_additional_data(pkt_rlh, payload.len());
let mut buffer = payload.to_vec();
let mut tag = vec![0; self.local_ccm.tag_len()];
self.local_ccm
.seal_in_place(&nonce, &additional_data, &mut buffer, &mut tag)
.map_err(crypto_error)?;
let mut r = Vec::with_capacity(raw.len() + 8 + buffer.len() + tag.len());
r.extend_from_slice(raw);
r.extend_from_slice(&nonce[4..]);
r.extend_from_slice(&buffer);
r.extend_from_slice(&tag);
let r_len = (r.len() - RECORD_LAYER_HEADER_SIZE) as u16;
r[RECORD_LAYER_HEADER_SIZE - 2..RECORD_LAYER_HEADER_SIZE]
.copy_from_slice(&r_len.to_be_bytes());
Ok(r)
}
pub fn decrypt(&mut self, r: &[u8]) -> Result<Vec<u8>> {
let mut reader = Cursor::new(r);
let h = RecordLayerHeader::unmarshal(&mut reader)?;
if h.content_type == ContentType::ChangeCipherSpec {
return Ok(r.to_vec());
}
if r.len() <= (RECORD_LAYER_HEADER_SIZE + 8) {
return Err(Error::ErrNotEnoughRoomForNonce);
}
let mut nonce = [0; CRYPTO_CCM_NONCE_LENGTH];
nonce[..4].copy_from_slice(&self.remote_write_iv[..4]);
nonce[4..].copy_from_slice(&r[RECORD_LAYER_HEADER_SIZE..RECORD_LAYER_HEADER_SIZE + 8]);
let out = &r[RECORD_LAYER_HEADER_SIZE + 8..];
let tag_len = self.remote_ccm.tag_len();
if out.len() < tag_len {
return Err(Error::ErrInvalidMac);
}
let tag_start = out.len() - tag_len;
let additional_data = generate_aead_additional_data(&h, tag_start);
let mut buffer = out[..tag_start].to_vec();
self.remote_ccm
.open_in_place(&nonce, &additional_data, &mut buffer, &out[tag_start..])
.map_err(authentication_error)?;
let mut d = Vec::with_capacity(RECORD_LAYER_HEADER_SIZE + buffer.len());
d.extend_from_slice(&r[..RECORD_LAYER_HEADER_SIZE]);
d.extend_from_slice(&buffer);
Ok(d)
}
}