use rand_core::RngCore;
use crate::cert::CertRef;
use crate::crypto::{
self, canon, Aead, AeadNonceRef, CanonAeadKey, CanonAeadKeyRef, CanonPkcPublicKey,
CanonPkcPublicKeyRef, CanonPkcSharedSecret, CanonPkcSignature, CanonPkcSignatureRef, Crypto,
CryptoSensitive, Digest, Hash, HashRef, Kdf, PublicKey, SecretKey, SigningSecretKey,
AEAD_CANON_KEY_LEN, AEAD_KEY_ZEROED, AEAD_TAG_LEN, AEAD_TAG_ZEROED, HASH_LEN, HASH_ZEROED,
PKC_CANON_PUBLIC_KEY_LEN, PKC_PUBLIC_KEY_ZEROED, PKC_SHARED_SECRET_ZEROED,
};
use crate::dm::clusters::time_sync::UtcTime;
use crate::error::{Error, ErrorCode};
use crate::fabric::Fabric;
use crate::tlv::{Optional, TLVElement, TLVTag, TLVWrite};
use crate::utils::init::{init, Init};
use crate::utils::storage::WriteBuf;
pub const CASE_RANDOM_LEN: usize = 32;
pub const CASE_RESUMPTION_ID_LEN: usize = 16;
pub const CASE_SESSION_KEYS_LEN: usize = AEAD_CANON_KEY_LEN * 3;
const SIGMA2_NONCE: AeadNonceRef = AeadNonceRef::new(&[
0x4e, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x32, 0x4e,
]);
const SIGMA3_NONCE: AeadNonceRef = AeadNonceRef::new(&[
0x4e, 0x43, 0x41, 0x53, 0x45, 0x5f, 0x53, 0x69, 0x67, 0x6d, 0x61, 0x33, 0x4e,
]);
canon!(
CASE_RANDOM_LEN,
CASE_RANDOM_ZEROED,
CaseRandom,
CaseRandomRef
);
canon!(
CASE_RESUMPTION_ID_LEN,
CASE_RESUMPTION_ID_ZEROED,
CaseResumptionId,
CaseResumptionIdRef
);
canon!(
CASE_SESSION_KEYS_LEN,
CASE_SESSION_KEYS_ZEROED,
CaseSessionKeys,
CaseSessionKeysRef
);
pub struct CaseP<'a, C: Crypto + 'a> {
peer_sessid: u16,
local_sessid: u16,
local_fabric_idx: u8,
shared_secret: CanonPkcSharedSecret,
our_pub_key: CanonPkcPublicKey,
peer_pub_key: CanonPkcPublicKey,
tt: Optional<C::Hash<'a>>,
}
impl<'a, C: Crypto + 'a> CaseP<'a, C> {
#[inline(always)]
pub const fn new() -> Self {
Self {
peer_sessid: 0,
local_sessid: 0,
local_fabric_idx: 0,
shared_secret: PKC_SHARED_SECRET_ZEROED,
our_pub_key: PKC_PUBLIC_KEY_ZEROED,
peer_pub_key: PKC_PUBLIC_KEY_ZEROED,
tt: Optional::none(),
}
}
pub fn init() -> impl Init<Self> {
init!(Self {
peer_sessid: 0,
local_sessid: 0,
local_fabric_idx: 0,
shared_secret <- CanonPkcSharedSecret::init(),
our_pub_key <- CanonPkcPublicKey::init(),
peer_pub_key <- CanonPkcPublicKey::init(),
tt <- Optional::init_none(),
})
}
#[allow(clippy::too_many_arguments)]
pub fn start(
&mut self,
crypto: &'a C,
peer_sessid: u16,
local_sessid: u16,
local_fabric_idx: u8,
peer_pub_key: CanonPkcPublicKeyRef<'_>,
request: &[u8],
our_random_out: &mut CaseRandom,
resumption_id_out: &mut CaseResumptionId,
tt_hash_out: &mut Hash,
) -> Result<(), Error> {
self.peer_sessid = peer_sessid;
self.local_sessid = local_sessid;
self.local_fabric_idx = local_fabric_idx;
self.peer_pub_key.load(peer_pub_key);
let peer_pub_key = crypto.pub_key(peer_pub_key)?;
let secret_key = crypto.generate_secret_key()?;
secret_key.pub_key()?.write_canon(&mut self.our_pub_key)?;
secret_key.derive_shared_secret(&peer_pub_key, &mut self.shared_secret)?;
let mut rand = crypto.rand()?;
rand.fill_bytes(our_random_out.access_mut());
rand.fill_bytes(resumption_id_out.access_mut());
self.tt = Optional::some(crypto.hash()?);
self.update_tt(request)?;
self.current_tt_hash(tt_hash_out)?;
Ok(())
}
pub fn start_initiator(
&mut self,
crypto: &'a C,
fabric: &Fabric,
peer_node_id: u64,
local_sessid: u16,
initiator_random_out: &mut CaseRandom,
destination_id_out: &mut Hash,
) -> Result<C::SecretKey<'a>, Error> {
self.local_sessid = local_sessid;
self.local_fabric_idx = fabric.fab_idx().get();
let secret_key = crypto.generate_secret_key()?;
secret_key.pub_key()?.write_canon(&mut self.our_pub_key)?;
let mut rand = crypto.rand()?;
rand.fill_bytes(initiator_random_out.access_mut());
fabric.compute_dest_id(
crypto,
initiator_random_out.access(),
peer_node_id,
destination_id_out,
)?;
self.tt = Optional::some(crypto.hash()?);
Ok(secret_key)
}
#[allow(clippy::too_many_arguments)]
pub fn sigma2_decrypt(
&mut self,
crypto: &'a C,
fabric: &Fabric,
secret_key: &C::SecretKey<'a>,
raw_sigma2_payload: &[u8],
peer_random: CaseRandomRef<'_>,
peer_sessid: u16,
peer_eph_pub_key: CanonPkcPublicKeyRef<'_>,
encrypted2: &mut [u8],
) -> Result<usize, Error> {
self.peer_sessid = peer_sessid;
self.peer_pub_key.load(peer_eph_pub_key);
let peer_pub_key_obj = crypto.pub_key(peer_eph_pub_key)?;
secret_key.derive_shared_secret(&peer_pub_key_obj, &mut self.shared_secret)?;
let mut tt_hash = HASH_ZEROED;
self.current_tt_hash(&mut tt_hash)?;
let mut sigma2_key = AEAD_KEY_ZEROED;
self.compute_sigma2_key(
crypto,
fabric.ipk().op_key(),
peer_random,
peer_eph_pub_key,
tt_hash.reference(),
&mut sigma2_key,
)?;
self.update_tt(raw_sigma2_payload)?;
let encrypted_len = encrypted2.len();
let mut cypher = crypto.aead()?;
cypher.decrypt_in_place(sigma2_key.reference(), SIGMA2_NONCE, &[], encrypted2)?;
Ok(encrypted_len - crypto::AEAD_TAG_LEN)
}
pub fn local_fabric_idx(&self) -> u8 {
self.local_fabric_idx
}
pub fn peer_sessid(&self) -> u16 {
self.peer_sessid
}
pub fn local_sessid(&self) -> u16 {
self.local_sessid
}
pub fn our_pub_key(&self) -> CanonPkcPublicKeyRef<'_> {
self.our_pub_key.reference()
}
pub fn update_tt(&mut self, data: &[u8]) -> Result<(), Error> {
unwrap!(self.tt.as_opt_mut()).update(data)
}
pub fn current_tt_hash(&mut self, out: &mut Hash) -> Result<(), Error> {
unwrap!(self.tt.as_opt_mut()).finish_current(out)
}
#[allow(clippy::too_many_arguments)]
pub fn sigma2_encrypt(
&self,
crypto: &C,
fabric: &Fabric,
our_random: CaseRandomRef<'_>,
our_hash: HashRef<'_>,
signature: CanonPkcSignatureRef<'_>,
resumption_id: CaseResumptionIdRef<'_>,
out: &mut [u8],
) -> Result<usize, Error> {
let mut sigma2_key = AEAD_KEY_ZEROED;
self.compute_sigma2_key(
crypto,
fabric.ipk().op_key(),
our_random,
self.our_pub_key.reference(),
our_hash,
&mut sigma2_key,
)?;
let mut tw = WriteBuf::new(out);
tw.start_struct(&TLVTag::Anonymous)?;
tw.str(&TLVTag::Context(1), fabric.noc())?;
if !fabric.icac().is_empty() {
tw.str(&TLVTag::Context(2), fabric.icac())?
};
tw.str(&TLVTag::Context(3), signature.access())?;
tw.str(&TLVTag::Context(4), resumption_id.access())?;
tw.end_container()?;
tw.append(AEAD_TAG_ZEROED.access())?;
let cipher_text = tw.as_mut_slice();
let mut cypher = crypto.aead()?;
cypher.encrypt_in_place(
sigma2_key.reference(),
SIGMA2_NONCE,
&[],
cipher_text,
cipher_text.len() - AEAD_TAG_LEN,
)?;
Ok(tw.as_slice().len())
}
pub fn compute_sigma2_signature(
&self,
crypto: &C,
fabric: &Fabric,
tmp_buf: &mut [u8],
signature: &mut CanonPkcSignature,
) -> Result<(), Error> {
let mut tw = WriteBuf::new(tmp_buf);
tw.start_struct(&TLVTag::Anonymous)?;
tw.str(&TLVTag::Context(1), fabric.noc())?;
if !fabric.icac().is_empty() {
tw.str(&TLVTag::Context(2), fabric.icac())?;
}
tw.str(&TLVTag::Context(3), self.our_pub_key.access())?;
tw.str(&TLVTag::Context(4), self.peer_pub_key.access())?;
tw.end_container()?;
let fabric_secret = crypto.secret_key(fabric.secret_key())?;
fabric_secret.sign(tw.as_slice(), signature)?;
Ok(())
}
fn compute_sigma2_key(
&self,
crypto: &C,
ipk: CanonAeadKeyRef<'_>,
responder_random: CaseRandomRef<'_>,
responder_eph_pub_key: CanonPkcPublicKeyRef<'_>,
tt_hash: HashRef<'_>,
key: &mut CanonAeadKey,
) -> Result<(), Error> {
const S2K_INFO: [u8; 6] = [0x53, 0x69, 0x67, 0x6d, 0x61, 0x32];
let mut salt = CryptoSensitive::<
{ AEAD_CANON_KEY_LEN + 32 + PKC_CANON_PUBLIC_KEY_LEN + HASH_LEN },
>::new();
let salt_access: &mut [u8] = salt.access_mut();
salt_access[..AEAD_CANON_KEY_LEN].copy_from_slice(ipk.access());
salt_access[AEAD_CANON_KEY_LEN..][..32].copy_from_slice(responder_random.access());
salt_access[AEAD_CANON_KEY_LEN..][32..][..PKC_CANON_PUBLIC_KEY_LEN]
.copy_from_slice(responder_eph_pub_key.access());
salt_access[AEAD_CANON_KEY_LEN..][32..][PKC_CANON_PUBLIC_KEY_LEN..]
.copy_from_slice(tt_hash.access());
crypto
.kdf()?
.expand(
salt.access(),
self.shared_secret.reference(),
&S2K_INFO,
key,
)
.map_err(|_x| ErrorCode::InvalidData)?;
Ok(())
}
pub fn validate_certs(
&self,
crypto: &C,
time: UtcTime,
fabric: &Fabric,
noc: &CertRef,
icac: Option<&CertRef>,
tmp_buf: &mut [u8],
) -> Result<(), Error> {
let mut verifier = noc.verify_chain_start(crypto, time);
if fabric.fabric_id() != noc.get_fabric_id()? {
Err(ErrorCode::Invalid)?;
}
if let Some(icac) = icac {
if let Ok(fid) = icac.get_fabric_id() {
if fid != fabric.fabric_id() {
Err(ErrorCode::Invalid)?;
}
}
verifier = verifier.add_cert(icac, tmp_buf)?;
}
verifier
.add_cert(&CertRef::new(TLVElement::new(fabric.root_ca())), tmp_buf)?
.finalise(tmp_buf)?;
Ok(())
}
pub fn validate_peer_tbs_signature(
&self,
crypto: &C,
noc: &[u8],
icac: Option<&[u8]>,
noc_cert: &CertRef,
signature: CanonPkcSignatureRef<'_>,
tmp_buf: &mut [u8],
) -> Result<(), Error> {
let mut tw = WriteBuf::new(tmp_buf);
tw.start_struct(&TLVTag::Anonymous)?;
tw.str(&TLVTag::Context(1), noc)?;
if let Some(icac) = icac {
tw.str(&TLVTag::Context(2), icac)?;
}
tw.str(&TLVTag::Context(3), self.peer_pub_key.access())?;
tw.str(&TLVTag::Context(4), self.our_pub_key.access())?;
tw.end_container()?;
let pub_key = crypto.pub_key(CanonPkcPublicKeyRef::try_new(noc_cert.pubkey()?)?)?;
if !pub_key.verify(tw.as_slice(), signature)? {
Err(ErrorCode::Invalid)?;
}
Ok(())
}
pub fn compute_session_keys(
&mut self,
crypto: &C,
ipk: CanonAeadKeyRef<'_>,
keys: &mut CaseSessionKeys,
) -> Result<(), Error> {
const SEKEYS_INFO: [u8; 11] = [
0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73,
];
let mut tt_hash = HASH_ZEROED;
self.current_tt_hash(&mut tt_hash)?;
let mut salt = CryptoSensitive::<{ AEAD_CANON_KEY_LEN + HASH_LEN }>::new();
let salt_access: &mut [u8] = salt.access_mut();
salt_access[..AEAD_CANON_KEY_LEN].copy_from_slice(ipk.access());
salt_access[AEAD_CANON_KEY_LEN..].copy_from_slice(tt_hash.access());
crypto
.kdf()?
.expand(
salt.access(),
self.shared_secret.reference(),
&SEKEYS_INFO,
keys,
)
.map_err(|_x| ErrorCode::InvalidData)?;
Ok(())
}
pub fn compute_sigma3_signature(
&self,
crypto: &C,
fabric: &Fabric,
tmp_buf: &mut [u8],
signature: &mut CanonPkcSignature,
) -> Result<(), Error> {
let mut tw = WriteBuf::new(tmp_buf);
tw.start_struct(&TLVTag::Anonymous)?;
tw.str(&TLVTag::Context(1), fabric.noc())?;
if !fabric.icac().is_empty() {
tw.str(&TLVTag::Context(2), fabric.icac())?;
}
tw.str(&TLVTag::Context(3), self.our_pub_key.access())?;
tw.str(&TLVTag::Context(4), self.peer_pub_key.access())?;
tw.end_container()?;
let fabric_secret = crypto.secret_key(fabric.secret_key())?;
fabric_secret.sign(tw.as_slice(), signature)?;
Ok(())
}
pub fn sigma3_encrypt(
&mut self,
crypto: &C,
fabric: &Fabric,
signature: CanonPkcSignatureRef<'_>,
out: &mut [u8],
) -> Result<usize, Error> {
let mut sigma3_key = AEAD_KEY_ZEROED;
self.compute_sigma3_key(crypto, fabric.ipk().op_key(), &mut sigma3_key)?;
let mut tw = WriteBuf::new(out);
tw.start_struct(&TLVTag::Anonymous)?;
tw.str(&TLVTag::Context(1), fabric.noc())?;
if !fabric.icac().is_empty() {
tw.str(&TLVTag::Context(2), fabric.icac())?;
}
tw.str(&TLVTag::Context(3), signature.access())?;
tw.end_container()?;
tw.append(AEAD_TAG_ZEROED.access())?;
let cipher_text = tw.as_mut_slice();
let mut cypher = crypto.aead()?;
cypher.encrypt_in_place(
sigma3_key.reference(),
SIGMA3_NONCE,
&[],
cipher_text,
cipher_text.len() - AEAD_TAG_LEN,
)?;
Ok(tw.as_slice().len())
}
pub fn sigma3_decrypt(
&mut self,
crypto: &C,
ipk: CanonAeadKeyRef<'_>,
encrypted: &mut [u8],
) -> Result<usize, Error> {
let mut sigma3_key = AEAD_KEY_ZEROED;
self.compute_sigma3_key(crypto, ipk, &mut sigma3_key)?;
let encrypted_len = encrypted.len();
let mut cypher = crypto.aead()?;
cypher.decrypt_in_place(sigma3_key.reference(), SIGMA3_NONCE, &[], encrypted)?;
Ok(encrypted_len - crypto::AEAD_TAG_LEN)
}
fn compute_sigma3_key(
&mut self,
crypto: &C,
ipk: CanonAeadKeyRef<'_>,
key: &mut CanonAeadKey,
) -> Result<(), Error> {
const S3K_INFO: [u8; 6] = [0x53, 0x69, 0x67, 0x6d, 0x61, 0x33];
let mut tt_hash = HASH_ZEROED;
self.current_tt_hash(&mut tt_hash)?;
let mut salt = CryptoSensitive::<{ AEAD_CANON_KEY_LEN + HASH_LEN }>::new();
let salt_access: &mut [u8] = salt.access_mut();
salt_access[..AEAD_CANON_KEY_LEN].copy_from_slice(ipk.access());
salt_access[AEAD_CANON_KEY_LEN..].copy_from_slice(tt_hash.access());
crypto
.kdf()?
.expand(
salt.access(),
self.shared_secret.reference(),
&S3K_INFO,
key,
)
.map_err(|_x| ErrorCode::InvalidData)?;
Ok(())
}
}