use crate::endpoint_crypto_container::EndpointRatchetConstructor;
use crate::entropy_bank::{EntropyBank, SecurityLevel};
use crate::fcm::fcm_ratchet::ThinRatchet;
use crate::misc::CryptError;
use crate::scramble::crypt_splitter::calculate_nonce_version;
use crate::stacked_ratchet::constructor::StackedRatchetConstructor;
use bytes::BytesMut;
use citadel_pqcrypto::bytes_in_place::EzBuffer;
use citadel_pqcrypto::constructor_opts::{ConstructorOpts, RecursiveChain};
use citadel_pqcrypto::PostQuantumContainer;
use serde::{Deserialize, Serialize};
use sha3::Digest;
use std::convert::TryFrom;
use std::sync::Arc;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct StackedRatchet {
pub(crate) inner: Arc<StackedRatchetInner>,
}
pub trait Ratchet: Serialize + for<'a> Deserialize<'a> + Clone + Send + Sync + 'static {
type Constructor: EndpointRatchetConstructor<Self> + Serialize + for<'a> Deserialize<'a>;
fn get_cid(&self) -> u64;
fn version(&self) -> u32;
fn has_verified_packets(&self) -> bool;
fn reset_ara(&self);
fn get_default_security_level(&self) -> SecurityLevel;
fn message_pqc_drill(&self, idx: Option<usize>) -> (&PostQuantumContainer, &EntropyBank);
fn get_scramble_drill(&self) -> &EntropyBank;
fn get_next_constructor_opts(&self) -> Vec<ConstructorOpts>;
fn protect_message_packet<T: EzBuffer>(
&self,
security_level: Option<SecurityLevel>,
header_len_bytes: usize,
packet: &mut T,
) -> Result<(), CryptError<String>>;
fn validate_message_packet<H: AsRef<[u8]>, T: EzBuffer>(
&self,
security_level: Option<SecurityLevel>,
header: H,
packet: &mut T,
) -> Result<(), CryptError<String>>;
fn next_alice_constructor(&self) -> Option<Self::Constructor> {
Self::Constructor::new_alice(
self.get_next_constructor_opts(),
self.get_cid(),
self.version().wrapping_add(1),
Some(self.get_default_security_level()),
)
}
}
pub enum RatchetType<R: Ratchet = StackedRatchet, Fcm: Ratchet = ThinRatchet> {
Default(R),
Fcm(Fcm),
}
impl<R: Ratchet, Fcm: Ratchet> RatchetType<R, Fcm> {
pub fn assume_default(self) -> Option<R> {
if let RatchetType::Default(r) = self {
Some(r)
} else {
None
}
}
}
impl Ratchet for StackedRatchet {
type Constructor = StackedRatchetConstructor;
fn get_cid(&self) -> u64 {
self.get_cid()
}
fn version(&self) -> u32 {
self.version()
}
fn has_verified_packets(&self) -> bool {
self.has_verified_packets()
}
fn reset_ara(&self) {
for ratchet in self.inner.message.inner.iter() {
ratchet.pqc.reset_counters();
}
self.inner.scramble.pqc.reset_counters();
}
fn get_default_security_level(&self) -> SecurityLevel {
self.get_default_security_level()
}
fn message_pqc_drill(&self, idx: Option<usize>) -> (&PostQuantumContainer, &EntropyBank) {
self.message_pqc_drill(idx)
}
fn get_scramble_drill(&self) -> &EntropyBank {
self.get_scramble_drill()
}
fn get_next_constructor_opts(&self) -> Vec<ConstructorOpts> {
let mut meta_chain_hasher = sha3::Sha3_256::default();
for chain in self
.inner
.message
.inner
.iter()
.map(|r| r.pqc.get_chain().unwrap())
{
meta_chain_hasher.update(&chain.chain[..]);
}
let meta_chain = meta_chain_hasher.finalize();
self.inner
.message
.inner
.iter()
.map(|r| {
let prev_chain = r.pqc.get_chain().unwrap();
let next_chain =
RecursiveChain::new(&meta_chain[..], prev_chain.alice, prev_chain.bob, false)
.unwrap();
ConstructorOpts::new_from_previous(Some(r.pqc.params), next_chain)
})
.collect()
}
fn protect_message_packet<T: EzBuffer>(
&self,
security_level: Option<SecurityLevel>,
header_len_bytes: usize,
packet: &mut T,
) -> Result<(), CryptError<String>> {
self.protect_message_packet(security_level, header_len_bytes, packet)
}
fn validate_message_packet<H: AsRef<[u8]>, T: EzBuffer>(
&self,
security_level: Option<SecurityLevel>,
header: H,
packet: &mut T,
) -> Result<(), CryptError<String>> {
self.validate_message_packet(security_level, header, packet)
}
}
impl StackedRatchet {
pub fn has_verified_packets(&self) -> bool {
let max = self.inner.message.inner.len();
for n in 0..max {
if self.get_message_pqc(Some(n)).has_verified_packets() {
return true;
}
}
self.get_scramble_pqc().has_verified_packets()
}
pub fn get_scramble_pqc(&self) -> &PostQuantumContainer {
&self.inner.scramble.pqc
}
#[inline]
pub fn message_pqc_drill(&self, idx: Option<usize>) -> (&PostQuantumContainer, &EntropyBank) {
let idx = idx.unwrap_or(0);
(
&self.inner.message.inner[idx].pqc,
&self.inner.message.inner[idx].drill,
)
}
#[inline]
pub fn scramble_pqc_drill(&self) -> (&PostQuantumContainer, &EntropyBank) {
(&self.inner.scramble.pqc, &self.inner.scramble.drill)
}
pub fn verify_level(
&self,
security_level: Option<SecurityLevel>,
) -> Result<usize, CryptError<String>> {
let security_level = security_level.unwrap_or(SecurityLevel::Standard);
if security_level.value() as usize >= self.inner.message.inner.len() {
log::warn!(target: "citadel", "OOB: Security value: {}, max: {} (default: {:?})|| Version: {}", security_level.value() as usize, self.inner.message.inner.len() - 1, self.get_default_security_level(), self.version());
Err(CryptError::OutOfBoundsError)
} else {
Ok(security_level.value() as usize)
}
}
pub fn protect_message_packet<T: EzBuffer>(
&self,
security_level: Option<SecurityLevel>,
header_len_bytes: usize,
packet: &mut T,
) -> Result<(), CryptError<String>> {
let idx = self.verify_level(security_level)?;
for n in 0..=idx {
let (pqc, drill) = self.message_pqc_drill(Some(n));
drill.protect_packet(pqc, header_len_bytes, packet)?;
}
Ok(())
}
pub fn validate_message_packet<H: AsRef<[u8]>, T: EzBuffer>(
&self,
security_level: Option<SecurityLevel>,
header: H,
packet: &mut T,
) -> Result<(), CryptError<String>> {
let idx = self.verify_level(security_level)?;
for n in (0..=idx).rev() {
let (pqc, drill) = self.message_pqc_drill(Some(n));
drill.validate_packet_in_place_split(pqc, &header, packet)?;
}
Ok(())
}
pub fn validate_message_packet_in_place_split<H: AsRef<[u8]>>(
&self,
security_level: Option<SecurityLevel>,
header: H,
packet: &mut BytesMut,
) -> Result<(), CryptError<String>> {
let idx = self.verify_level(security_level)?;
for n in (0..=idx).rev() {
let (pqc, drill) = self.message_pqc_drill(Some(n));
drill.validate_packet_in_place_split(pqc, &header, packet)?;
}
Ok(())
}
pub fn encrypt<T: AsRef<[u8]>>(&self, contents: T) -> Result<Vec<u8>, CryptError<String>> {
self.encrypt_custom(0, 0, contents)
}
pub fn encrypt_custom<T: AsRef<[u8]>>(
&self,
wave_id: u32,
group: u64,
contents: T,
) -> Result<Vec<u8>, CryptError<String>> {
let (pqc, drill) = self.message_pqc_drill(None);
drill.encrypt(
calculate_nonce_version(wave_id as usize, group),
pqc,
contents,
)
}
pub fn encrypt_scrambler<T: AsRef<[u8]>>(
&self,
contents: T,
) -> Result<Vec<u8>, CryptError<String>> {
self.encrypt_custom_scrambler(0, 0, contents)
}
pub fn encrypt_custom_scrambler<T: AsRef<[u8]>>(
&self,
wave_id: u32,
group: u64,
contents: T,
) -> Result<Vec<u8>, CryptError<String>> {
let (pqc, drill) = self.scramble_pqc_drill();
drill.encrypt(
calculate_nonce_version(wave_id as usize, group),
pqc,
contents,
)
}
pub fn decrypt<T: AsRef<[u8]>>(&self, contents: T) -> Result<Vec<u8>, CryptError<String>> {
self.decrypt_custom(0, 0, contents)
}
pub fn decrypt_custom<T: AsRef<[u8]>>(
&self,
wave_id: u32,
group_id: u64,
contents: T,
) -> Result<Vec<u8>, CryptError<String>> {
let (pqc, drill) = self.message_pqc_drill(None);
drill.decrypt(
calculate_nonce_version(wave_id as usize, group_id),
pqc,
contents,
)
}
pub fn decrypt_scrambler<T: AsRef<[u8]>>(
&self,
contents: T,
) -> Result<Vec<u8>, CryptError<String>> {
self.decrypt_custom_scrambler(0, 0, contents)
}
pub fn decrypt_custom_scrambler<T: AsRef<[u8]>>(
&self,
wave_id: u32,
group_id: u64,
contents: T,
) -> Result<Vec<u8>, CryptError<String>> {
let (pqc, drill) = self.scramble_pqc_drill();
drill.decrypt(
calculate_nonce_version(wave_id as usize, group_id),
pqc,
contents,
)
}
pub fn get_message_drill(&self, idx: Option<usize>) -> &EntropyBank {
&self.inner.message.inner[idx.unwrap_or(0)].drill
}
pub fn get_message_pqc(&self, idx: Option<usize>) -> &PostQuantumContainer {
&self.inner.message.inner[idx.unwrap_or(0)].pqc
}
pub fn get_scramble_drill(&self) -> &EntropyBank {
&self.inner.scramble.drill
}
pub fn version(&self) -> u32 {
self.inner.message.inner[0].drill.version
}
pub fn get_cid(&self) -> u64 {
self.inner.message.inner[0].drill.cid
}
pub fn get_default_security_level(&self) -> SecurityLevel {
self.inner.default_security_level
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct StackedRatchetInner {
pub(crate) message: MessageRatchet,
pub(crate) scramble: ScrambleRatchet,
pub(crate) default_security_level: SecurityLevel,
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct MessageRatchet {
inner: Vec<MessageRatchetInner>,
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct MessageRatchetInner {
pub(crate) drill: EntropyBank,
pub(crate) pqc: PostQuantumContainer,
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct ScrambleRatchet {
pub(crate) drill: EntropyBank,
pub(crate) pqc: PostQuantumContainer,
}
impl From<StackedRatchetInner> for StackedRatchet {
fn from(inner: StackedRatchetInner) -> Self {
Self {
inner: Arc::new(inner),
}
}
}
pub mod constructor {
use crate::endpoint_crypto_container::EndpointRatchetConstructor;
use crate::entropy_bank::{EntropyBank, SecurityLevel};
use crate::fcm::fcm_ratchet::{FcmAliceToBobTransfer, FcmBobToAliceTransfer, ThinRatchet};
use crate::prelude::CryptError;
use crate::stacked_ratchet::{Ratchet, StackedRatchet};
use arrayvec::ArrayVec;
use bytes::BufMut;
use bytes::BytesMut;
use citadel_pqcrypto::algorithm_dictionary::CryptoParameters;
use citadel_pqcrypto::constructor_opts::ConstructorOpts;
use citadel_pqcrypto::wire::{AliceToBobTransferParameters, BobToAliceTransferParameters};
use citadel_pqcrypto::PostQuantumContainer;
use citadel_pqcrypto::LARGEST_NONCE_LEN;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
#[derive(Serialize, Deserialize)]
pub struct StackedRatchetConstructor {
pub(super) message: MessageRatchetConstructor,
pub(super) scramble: ScrambleRatchetConstructor,
nonce_message: ArrayVec<u8, LARGEST_NONCE_LEN>,
nonce_scramble: ArrayVec<u8, LARGEST_NONCE_LEN>,
cid: u64,
new_version: u32,
security_level: SecurityLevel,
params: CryptoParameters,
}
#[derive(Serialize, Deserialize)]
pub enum ConstructorType<R: Ratchet = StackedRatchet, Fcm: Ratchet = ThinRatchet> {
Default(R::Constructor),
Fcm(Fcm::Constructor),
}
impl<R: Ratchet, Fcm: Ratchet> ConstructorType<R, Fcm> {
pub fn stage1_alice(&mut self, transfer: BobToAliceTransferType) -> Result<(), CryptError> {
match self {
ConstructorType::Default(con) => con.stage1_alice(transfer),
ConstructorType::Fcm(con) => con.stage1_alice(transfer),
}
}
pub fn assume_default(self) -> Option<R::Constructor> {
if let ConstructorType::Default(c) = self {
Some(c)
} else {
None
}
}
pub fn assume_fcm(self) -> Option<Fcm::Constructor> {
if let ConstructorType::Fcm(c) = self {
Some(c)
} else {
None
}
}
pub fn assume_default_ref(&self) -> Option<&R::Constructor> {
if let ConstructorType::Default(c) = self {
Some(c)
} else {
None
}
}
pub fn assume_fcm_ref(&self) -> Option<&Fcm::Constructor> {
if let ConstructorType::Fcm(c) = self {
Some(c)
} else {
None
}
}
pub fn is_fcm(&self) -> bool {
matches!(self, Self::Fcm(..))
}
}
#[derive(Serialize, Deserialize)]
pub enum AliceToBobTransferType {
Default(AliceToBobTransfer),
Fcm(FcmAliceToBobTransfer),
}
impl AliceToBobTransferType {
pub fn get_declared_new_version(&self) -> u32 {
match self {
AliceToBobTransferType::Default(tx) => tx.new_version,
AliceToBobTransferType::Fcm(tx) => tx.version,
}
}
}
impl EndpointRatchetConstructor<StackedRatchet> for StackedRatchetConstructor {
fn new_alice(
opts: Vec<ConstructorOpts>,
cid: u64,
new_version: u32,
security_level: Option<SecurityLevel>,
) -> Option<Self> {
StackedRatchetConstructor::new_alice(opts, cid, new_version, security_level)
}
fn new_bob(
cid: u64,
new_drill_vers: u32,
opts: Vec<ConstructorOpts>,
transfer: AliceToBobTransferType,
) -> Option<Self> {
match transfer {
AliceToBobTransferType::Default(transfer) => {
StackedRatchetConstructor::new_bob(cid, new_drill_vers, opts, transfer)
}
_ => {
log::error!(target: "citadel", "Incompatible Ratchet Type passed! [X-22]");
None
}
}
}
fn stage0_alice(&self) -> Option<AliceToBobTransferType> {
Some(AliceToBobTransferType::Default(self.stage0_alice()?))
}
fn stage0_bob(&self) -> Option<BobToAliceTransferType> {
Some(BobToAliceTransferType::Default(self.stage0_bob()?))
}
fn stage1_alice(&mut self, transfer: BobToAliceTransferType) -> Result<(), CryptError> {
self.stage1_alice(transfer)
}
fn update_version(&mut self, version: u32) -> Option<()> {
self.update_version(version)
}
fn finish_with_custom_cid(self, cid: u64) -> Option<StackedRatchet> {
self.finish_with_custom_cid(cid)
}
fn finish(self) -> Option<StackedRatchet> {
self.finish()
}
}
#[derive(Serialize, Deserialize)]
pub struct AliceToBobTransfer {
pub params: CryptoParameters,
params_txs: Vec<AliceToBobTransferParameters>,
scramble_alice_params: AliceToBobTransferParameters,
scramble_nonce: ArrayVec<u8, LARGEST_NONCE_LEN>,
msg_nonce: ArrayVec<u8, LARGEST_NONCE_LEN>,
pub security_level: SecurityLevel,
cid: u64,
new_version: u32,
}
#[derive(Serialize, Deserialize)]
pub struct BobToAliceTransfer {
msg_bob_params_txs: Vec<BobToAliceTransferParameters>,
scramble_bob_params_tx: BobToAliceTransferParameters,
encrypted_msg_drills: Vec<Vec<u8>>,
encrypted_scramble_drill: Vec<u8>,
pub security_level: SecurityLevel,
}
#[derive(Serialize, Deserialize)]
pub enum BobToAliceTransferType {
Default(BobToAliceTransfer),
Fcm(FcmBobToAliceTransfer),
}
impl BobToAliceTransfer {
pub fn serialize_into(&self, buf: &mut BytesMut) -> Option<()> {
let len = bincode2::serialized_size(self).ok()?;
buf.reserve(len as usize);
bincode2::serialize_into(buf.writer(), self).ok()
}
pub fn deserialize_from<T: AsRef<[u8]>>(source: T) -> Option<BobToAliceTransfer> {
bincode2::deserialize(source.as_ref()).ok()
}
}
impl AliceToBobTransfer {
pub fn serialize_to_vec(&self) -> Option<Vec<u8>> {
bincode2::serialize(self).ok()
}
pub fn deserialize_from(source: &[u8]) -> Option<AliceToBobTransfer> {
bincode2::deserialize(source).ok()
}
pub fn get_declared_new_version(&self) -> u32 {
self.new_version
}
pub fn get_declared_cid(&self) -> u64 {
self.cid
}
}
impl StackedRatchetConstructor {
pub fn new_alice(
opts: Vec<ConstructorOpts>,
cid: u64,
new_version: u32,
security_level: Option<SecurityLevel>,
) -> Option<Self> {
let security_level = security_level.unwrap_or(SecurityLevel::Standard);
log::trace!(target: "citadel", "[ALICE] creating container with {:?} security level", security_level);
let len = opts.len();
let params = opts[0].cryptography.unwrap_or_default();
let keys = opts
.into_iter()
.filter_map(|opts| {
Some(MessageRatchetConstructorInner {
drill: None,
pqc: PostQuantumContainer::new_alice(opts).ok()?,
})
})
.collect::<Vec<MessageRatchetConstructorInner>>();
if keys.len() != len {
return None;
}
Some(Self {
params,
message: MessageRatchetConstructor { inner: keys },
scramble: ScrambleRatchetConstructor {
drill: None,
pqc: PostQuantumContainer::new_alice(ConstructorOpts::new_init(Some(params)))
.ok()?,
},
nonce_message: EntropyBank::generate_public_nonce(params.encryption_algorithm),
nonce_scramble: EntropyBank::generate_public_nonce(params.encryption_algorithm),
cid,
new_version,
security_level,
})
}
pub fn new_bob(
cid: u64,
new_drill_vers: u32,
opts: Vec<ConstructorOpts>,
transfer: AliceToBobTransfer,
) -> Option<Self> {
log::trace!(target: "citadel", "[BOB] creating container with {:?} security level", transfer.security_level);
let count = transfer.security_level.value() as usize + 1;
let params = transfer.params;
let keys: Vec<MessageRatchetConstructorInner> = transfer
.params_txs
.into_iter()
.zip(opts.into_iter())
.filter_map(|(params_tx, opts)| {
Some(MessageRatchetConstructorInner {
drill: Some(
EntropyBank::new(cid, new_drill_vers, params.encryption_algorithm)
.ok()?,
),
pqc: PostQuantumContainer::new_bob(opts, params_tx).ok()?,
})
})
.collect();
if keys.len() != count {
log::error!(target: "citadel", "[BOB] not all keys parsed correctly. {} != {}", keys.len(), count);
return None;
}
Some(Self {
params,
message: MessageRatchetConstructor { inner: keys },
scramble: ScrambleRatchetConstructor {
drill: Some(
EntropyBank::new(cid, new_drill_vers, params.encryption_algorithm).ok()?,
),
pqc: PostQuantumContainer::new_bob(
ConstructorOpts::new_init(Some(params)),
transfer.scramble_alice_params,
)
.ok()?,
},
nonce_message: transfer.msg_nonce,
nonce_scramble: transfer.scramble_nonce,
cid,
new_version: new_drill_vers,
security_level: transfer.security_level,
})
}
pub fn stage0_alice(&self) -> Option<AliceToBobTransfer> {
let pks = self
.message
.inner
.iter()
.filter_map(|inner| inner.pqc.generate_alice_to_bob_transfer().ok())
.collect::<Vec<AliceToBobTransferParameters>>();
if pks.len() != self.message.inner.len() {
return None;
}
let scramble_alice_pk = self.scramble.pqc.generate_alice_to_bob_transfer().ok()?;
let msg_nonce = self.nonce_message.clone();
let scramble_nonce = self.nonce_scramble.clone();
let cid = self.cid;
let new_version = self.new_version;
let params = self.params;
let security_level = self.security_level;
Some(AliceToBobTransfer {
params,
params_txs: pks,
scramble_alice_params: scramble_alice_pk,
msg_nonce,
scramble_nonce,
security_level,
cid,
new_version,
})
}
pub fn stage0_bob(&self) -> Option<BobToAliceTransfer> {
let expected_count = self.message.inner.len();
let security_level = self.security_level;
let msg_bob_cts: Vec<BobToAliceTransferParameters> = self
.message
.inner
.iter()
.filter_map(|inner| inner.pqc.generate_bob_to_alice_transfer().ok())
.collect();
if msg_bob_cts.len() != expected_count {
return None;
}
let scramble_bob_ct = self.scramble.pqc.generate_bob_to_alice_transfer().ok()?;
let nonce_msg = &self.nonce_message;
let nonce_scramble = &self.nonce_scramble;
let encrypted_msg_drills: Vec<Vec<u8>> = self
.message
.inner
.iter()
.filter_map(|inner| {
inner
.pqc
.encrypt(inner.drill.as_ref()?.serialize_to_vec().ok()?, nonce_msg)
.ok()
})
.collect();
if encrypted_msg_drills.len() != expected_count {
return None;
}
let encrypted_scramble_drill = self
.scramble
.pqc
.encrypt(
self.scramble.drill.as_ref()?.serialize_to_vec().ok()?,
nonce_scramble,
)
.ok()?;
let transfer = BobToAliceTransfer {
msg_bob_params_txs: msg_bob_cts,
scramble_bob_params_tx: scramble_bob_ct,
encrypted_msg_drills,
encrypted_scramble_drill,
security_level,
};
Some(transfer)
}
pub fn stage1_alice(&mut self, transfer: BobToAliceTransferType) -> Result<(), CryptError> {
if let BobToAliceTransferType::Default(transfer) = transfer {
let nonce_msg = &self.nonce_message;
for (container, bob_param_tx) in self
.message
.inner
.iter_mut()
.zip(transfer.msg_bob_params_txs)
{
container
.pqc
.alice_on_receive_ciphertext(bob_param_tx)
.map_err(|err| CryptError::DrillUpdateError(err.to_string()))?;
}
for (idx, container) in self.message.inner.iter_mut().enumerate() {
let decrypted_msg_drill = container
.pqc
.decrypt(
&transfer.encrypted_msg_drills.get(idx).ok_or_else(|| {
CryptError::DrillUpdateError(
"Unable to get encrypted_msg_drills".to_string(),
)
})?[..],
nonce_msg,
)
.map_err(|err| CryptError::DrillUpdateError(err.to_string()))?;
container.drill =
Some(EntropyBank::deserialize_from(&decrypted_msg_drill[..])?);
}
let nonce_scramble = &self.nonce_scramble;
self.scramble
.pqc
.alice_on_receive_ciphertext(transfer.scramble_bob_params_tx)
.map_err(|err| CryptError::DrillUpdateError(err.to_string()))?;
let decrypted_scramble_drill = self
.scramble
.pqc
.decrypt(&transfer.encrypted_scramble_drill[..], nonce_scramble)
.map_err(|err| CryptError::DrillUpdateError(err.to_string()))?;
self.scramble.drill = Some(EntropyBank::deserialize_from(
&decrypted_scramble_drill[..],
)?);
if self
.scramble
.drill
.as_ref()
.ok_or_else(|| {
CryptError::DrillUpdateError(
"Unable to get encrypted_msg_drills".to_string(),
)
})?
.version
!= self.message.inner[0]
.drill
.as_ref()
.ok_or_else(|| {
CryptError::DrillUpdateError(
"Unable to get encrypted_msg_drills".to_string(),
)
})?
.version
{
return Err(CryptError::DrillUpdateError(
"Message drill version != scramble drill version".to_string(),
));
}
if self
.scramble
.drill
.as_ref()
.ok_or_else(|| {
CryptError::DrillUpdateError(
"Unable to get encrypted_msg_drills".to_string(),
)
})?
.cid
!= self.message.inner[0]
.drill
.as_ref()
.ok_or_else(|| {
CryptError::DrillUpdateError(
"Unable to get encrypted_msg_drills".to_string(),
)
})?
.cid
{
return Err(CryptError::DrillUpdateError(
"Message drill cid != scramble drill cid".to_string(),
));
}
Ok(())
} else {
Err(CryptError::DrillUpdateError(
"Incompatible Ratchet Type passed! [X-40]".to_string(),
))
}
}
pub fn finish(self) -> Option<StackedRatchet> {
StackedRatchet::try_from(self).ok()
}
pub fn update_version(&mut self, proposed_version: u32) -> Option<()> {
self.new_version = proposed_version;
for container in self.message.inner.iter_mut() {
container.drill.as_mut()?.version = proposed_version;
}
self.scramble.drill.as_mut()?.version = proposed_version;
Some(())
}
pub fn finish_with_custom_cid(mut self, cid: u64) -> Option<StackedRatchet> {
for container in self.message.inner.iter_mut() {
container.drill.as_mut()?.cid = cid;
}
self.scramble.drill.as_mut()?.cid = cid;
self.finish()
}
}
#[derive(Serialize, Deserialize)]
pub(super) struct MessageRatchetConstructor {
pub(super) inner: Vec<MessageRatchetConstructorInner>,
}
#[derive(Serialize, Deserialize)]
pub(super) struct MessageRatchetConstructorInner {
pub(super) drill: Option<EntropyBank>,
pub(super) pqc: PostQuantumContainer,
}
#[derive(Serialize, Deserialize)]
pub(super) struct ScrambleRatchetConstructor {
pub(super) drill: Option<EntropyBank>,
pub(super) pqc: PostQuantumContainer,
}
}
impl TryFrom<StackedRatchetConstructor> for StackedRatchet {
type Error = ();
fn try_from(value: StackedRatchetConstructor) -> Result<Self, Self::Error> {
let StackedRatchetConstructor {
message, scramble, ..
} = value;
let default_security_level = SecurityLevel::for_value(message.inner.len() - 1).ok_or(())?;
let _ = scramble.pqc.get_shared_secret().map_err(|_| ())?;
let scramble_drill = scramble.drill.ok_or(())?;
let mut inner = Vec::with_capacity(message.inner.len());
for container in message.inner {
let _ = container.pqc.get_shared_secret().map_err(|_| ())?;
let message_drill = container.drill.ok_or(())?;
if message_drill.version != scramble_drill.version
|| message_drill.cid != scramble_drill.cid
{
return Err(());
}
inner.push(MessageRatchetInner {
drill: message_drill,
pqc: container.pqc,
});
}
let message = MessageRatchet { inner };
let scramble = ScrambleRatchet {
drill: scramble_drill,
pqc: scramble.pqc,
};
Ok(StackedRatchet::from(StackedRatchetInner {
message,
scramble,
default_security_level,
}))
}
}