use std::convert::TryInto;
use zeroize::Zeroizing;
use super::provider::{ContentKey, CONTENT_KEY_LEN};
use super::tier::HardwareKind;
use crate::cipher::{self, TAG_SIZE};
use crate::error::{KeystoreError, Result};
pub const ENVELOPE_MAGIC: &[u8; 6] = b"DIGHW1";
pub const ENVELOPE_VERSION_V1: u16 = 0x0001;
const CIPHER_AES_256_GCM: u8 = 0x01;
pub const HEADER_FIXED: usize = 6 + 2 + 1 + 1 + 12 + 2 + 4;
const FOOTER_SIZE: usize = 4;
pub fn is_envelope(bytes: &[u8]) -> bool {
bytes.len() >= ENVELOPE_MAGIC.len() && &bytes[..ENVELOPE_MAGIC.len()] == ENVELOPE_MAGIC
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Envelope {
pub(crate) hardware_wire_id: u8,
pub(crate) wrapped_key: Vec<u8>,
pub(crate) payload: Vec<u8>,
pub(crate) nonce: [u8; 12],
pub(crate) aad: Vec<u8>,
}
pub(crate) fn encode(
kind: HardwareKind,
content_key: &ContentKey,
wrapped_key: &[u8],
nonce: &[u8; 12],
inner: &[u8],
) -> Result<Vec<u8>> {
let wrapped_len: u16 =
wrapped_key
.len()
.try_into()
.map_err(|_| KeystoreError::HardwareWrapFailed {
detail: "wrapped key exceeds 65535 bytes".to_owned(),
})?;
let payload_len: u32 =
(inner.len() + TAG_SIZE)
.try_into()
.map_err(|_| KeystoreError::HardwareWrapFailed {
detail: "keystore blob too large to wrap".to_owned(),
})?;
let mut out = Vec::with_capacity(HEADER_FIXED + wrapped_key.len() + payload_len as usize + 4);
out.extend_from_slice(ENVELOPE_MAGIC);
out.extend_from_slice(&ENVELOPE_VERSION_V1.to_be_bytes());
out.push(kind.wire_id());
out.push(CIPHER_AES_256_GCM);
out.extend_from_slice(nonce);
out.extend_from_slice(&wrapped_len.to_be_bytes());
out.extend_from_slice(&payload_len.to_be_bytes());
out.extend_from_slice(wrapped_key);
let aad = out.clone();
let payload = cipher::encrypt(content_key, nonce, inner, &aad)?;
debug_assert_eq!(payload.len(), payload_len as usize);
out.extend_from_slice(&payload);
let crc = crc32fast::hash(&out);
out.extend_from_slice(&crc.to_be_bytes());
Ok(out)
}
pub(crate) fn decode(bytes: &[u8]) -> Result<Envelope> {
let floor = HEADER_FIXED + TAG_SIZE + FOOTER_SIZE;
if bytes.len() < floor {
return Err(KeystoreError::Truncated {
claimed: floor,
available: bytes.len(),
});
}
let (body, stored_crc_bytes) = bytes.split_at(bytes.len() - FOOTER_SIZE);
let stored = u32::from_be_bytes(stored_crc_bytes.try_into().expect("4 bytes"));
let computed = crc32fast::hash(body);
if stored != computed {
return Err(KeystoreError::CrcMismatch { stored, computed });
}
if &bytes[..6] != ENVELOPE_MAGIC {
return Err(KeystoreError::UnknownMagic {
saw: bytes[..6].try_into().expect("6 bytes"),
});
}
let version = u16::from_be_bytes([bytes[6], bytes[7]]);
if version != ENVELOPE_VERSION_V1 {
return Err(KeystoreError::UnsupportedFormat { found: version });
}
let hardware_wire_id = bytes[8];
let cipher_id = bytes[9];
if cipher_id != CIPHER_AES_256_GCM {
return Err(KeystoreError::UnsupportedCipher(cipher_id));
}
let nonce: [u8; 12] = bytes[10..22].try_into().expect("12 bytes");
let wrapped_len = u16::from_be_bytes([bytes[22], bytes[23]]) as usize;
let payload_len = u32::from_be_bytes(bytes[24..28].try_into().expect("4 bytes")) as usize;
if wrapped_len == 0 {
return Err(KeystoreError::MalformedEnvelope {
detail: "envelope carries no wrapped key".to_owned(),
});
}
if payload_len < TAG_SIZE {
return Err(KeystoreError::Truncated {
claimed: payload_len,
available: 0,
});
}
let declared = HEADER_FIXED
.checked_add(wrapped_len)
.and_then(|n| n.checked_add(payload_len))
.and_then(|n| n.checked_add(FOOTER_SIZE))
.ok_or(KeystoreError::Truncated {
claimed: usize::MAX,
available: bytes.len(),
})?;
if declared != bytes.len() {
return Err(KeystoreError::Truncated {
claimed: declared,
available: bytes.len(),
});
}
let key_end = HEADER_FIXED + wrapped_len;
Ok(Envelope {
hardware_wire_id,
wrapped_key: bytes[HEADER_FIXED..key_end].to_vec(),
payload: bytes[key_end..key_end + payload_len].to_vec(),
nonce,
aad: bytes[..key_end].to_vec(),
})
}
impl Envelope {
pub(crate) fn open(&self, content_key: &ContentKey) -> Result<Zeroizing<Vec<u8>>> {
cipher::decrypt(content_key, &self.nonce, &self.payload, &self.aad)
}
pub(crate) fn hardware_kind(&self) -> Option<HardwareKind> {
HardwareKind::from_wire_id(self.hardware_wire_id)
}
}
#[cfg(test)]
pub(crate) fn decode_for_test(bytes: &[u8]) -> Result<Envelope> {
decode(bytes)
}
pub(crate) fn random_content_key<R: rand_core::RngCore + rand_core::CryptoRng>(
rng: &mut R,
) -> ContentKey {
let mut key = Zeroizing::new(<[u8; CONTENT_KEY_LEN]>::default());
rng.fill_bytes(key.as_mut());
key
}
pub(crate) fn random_nonce<R: rand_core::RngCore + rand_core::CryptoRng>(rng: &mut R) -> [u8; 12] {
let mut nonce = <[u8; 12]>::default();
rng.fill_bytes(&mut nonce);
nonce
}