From cff58d922a2010f794fe2270d2247ba09337397e Mon Sep 17 00:00:00 2001 From: gram-signal <84339875+gram-signal@users.noreply.github.com> Date: Fri, 30 May 2025 10:46:26 -0700 Subject: [PATCH] Don't provide keys when we could version-negotiate to v0. --- benches/chain.rs | 10 +- benches/spqr.rs | 2 +- src/chain.rs | 19 +- src/lib.rs | 526 +++++++++++++++++++++++++++++++++++-- src/proto/pq_ratchet.proto | 1 + src/test/generic_dr.rs | 17 +- 6 files changed, 531 insertions(+), 44 deletions(-) diff --git a/benches/chain.rs b/benches/chain.rs index 50ffe27..62f14a9 100644 --- a/benches/chain.rs +++ b/benches/chain.rs @@ -12,7 +12,7 @@ mod tests { #[bench] fn add_epoch(b: &mut Bencher) { - let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default()) + let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()) .expect("should be valid"); let mut e: u64 = 0; b.iter(|| { @@ -28,7 +28,7 @@ mod tests { #[bench] fn send_key(b: &mut Bencher) { - let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default()) + let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()) .expect("should be valid"); b.iter(|| { // Inner closure, the actual test @@ -38,7 +38,7 @@ mod tests { #[bench] fn recv_key(b: &mut Bencher) { - let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default()) + let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()) .expect("should be valid"); let mut k: u32 = 0; b.iter(|| { @@ -50,7 +50,7 @@ mod tests { #[bench] fn recv_skip_key(b: &mut Bencher) { - let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default()) + let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()) .expect("should be valid"); let mut k: u32 = 0; b.iter(|| { @@ -63,7 +63,7 @@ mod tests { #[bench] fn recv_with_truncate(b: &mut Bencher) { - let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default()) + let mut c = chain::Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()) .expect("should be valid"); let mut k: u32 = 0; b.iter(|| { diff --git a/benches/spqr.rs b/benches/spqr.rs index e75455f..2bda3d4 100644 --- a/benches/spqr.rs +++ b/benches/spqr.rs @@ -58,7 +58,7 @@ mod tests { } = send(x, &mut rng).unwrap(); *x = state; let Recv { state, key: key_b } = recv(y, &msg).unwrap(); - assert_eq!(key_a.unwrap(), key_b.unwrap()); + assert_eq!(key_a, key_b); if drop_ctr == 0 { drop_ctr += 30; // We 'drop' a message by not replacing y's state. diff --git a/src/chain.rs b/src/chain.rs index 4ed0eea..20b729e 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -302,7 +302,7 @@ impl Chain { }) } - pub fn new(initial_key: &[u8], dir: Direction, params: ChainParams) -> Result { + pub fn new(initial_key: &[u8], dir: Direction, params: ChainParamsPB) -> Result { hax_lib::fstar!("admit ()"); let mut gen = [0u8; 96]; kdf::hkdf_to_slice( @@ -320,7 +320,7 @@ impl Chain { recv: Self::ced_for_direction(&gen, &dir.switch()), }]), next_root: gen[0..32].to_vec(), - params: params.into_pb(), + params, }) } @@ -429,8 +429,8 @@ mod test { #[test] fn directions_match() { - let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default()).unwrap(); - let mut b2a = Chain::new(b"1", Direction::B2A, ChainParams::default()).unwrap(); + let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()).unwrap(); + let mut b2a = Chain::new(b"1", Direction::B2A, ChainParams::default().into_pb()).unwrap(); let sk1 = a2b.send_key(0).unwrap(); assert_eq!(sk1.0, 1); assert_eq!(sk1.1, b2a.recv_key(0, 1).unwrap()); @@ -455,7 +455,7 @@ mod test { #[test] fn previously_returned_key() { - let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default()).unwrap(); + let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()).unwrap(); a2b.recv_key(0, 2).expect("should get key first time"); assert!(matches!( a2b.recv_key(0, 2), @@ -468,7 +468,8 @@ mod test { let params = ChainParams { max_jump: 10, max_ooo_keys: 10, - }; + } + .into_pb(); let mut a2b = Chain::new(b"1", Direction::A2B, params).unwrap(); a2b.recv_key(0, 10).expect("should allow this jump"); a2b.recv_key(0, 12).expect("should allow progression"); @@ -478,8 +479,8 @@ mod test { #[test] fn out_of_order_keys() { let max_ooo = DEFAULT_CHAIN_PARAMS.max_ooo_keys; - let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default()).unwrap(); - let mut b2a = Chain::new(b"1", Direction::B2A, ChainParams::default()).unwrap(); + let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()).unwrap(); + let mut b2a = Chain::new(b"1", Direction::B2A, ChainParams::default().into_pb()).unwrap(); let mut keys = Vec::with_capacity(max_ooo as usize); for _i in 0..(max_ooo as usize) { keys.push(a2b.send_key(0).unwrap()); @@ -493,7 +494,7 @@ mod test { #[test] fn clear_old_send_keys() { - let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default()).unwrap(); + let mut a2b = Chain::new(b"1", Direction::A2B, ChainParams::default().into_pb()).unwrap(); a2b.send_key(0).unwrap(); a2b.send_key(0).unwrap(); a2b.add_epoch(EpochSecret { diff --git a/src/lib.rs b/src/lib.rs index e2a1e39..120d7a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,15 @@ pub enum SecretOutput { Recv(Secret), } +#[derive(Debug)] +pub enum CurrentVersion { + StillNegotiating { + version: Version, + min_version: Version, + }, + NegotiationComplete(Version), +} + #[derive(Debug, thiserror::Error)] pub enum Error { #[error("state decode failed")] @@ -100,6 +109,8 @@ pub enum Error { SendKeyEpochDecreased(u64, u64), #[error("Invalid params: {0}")] InvalidParams(&'static str), + #[error("Chain not available")] + ChainNotAvailable, } impl From for Error { @@ -191,12 +202,11 @@ pub fn initial_state(params: Params) -> Result { auth_key: params.auth_key.to_vec(), direction: params.direction.into(), min_version: params.min_version.into(), + chain_params: Some(params.chain_params.into_pb()), }); - let chain = - Some(Chain::new(params.auth_key, params.direction, params.chain_params)?.into_pb()); Ok(pqrpb::PqRatchetState { inner: init_inner(params.version, params.direction, params.auth_key), - chain, + chain: None, version_negotiation, } .encode_to_vec()) @@ -215,6 +225,21 @@ pub struct Send { pub key: MessageKey, } +pub fn current_version(state: &SerializedState) -> Result { + let state_pb = decode_state(state)?; + let version = match state_pb.inner { + None => Version::V0, + Some(pqrpb::pq_ratchet_state::Inner::V1(_)) => Version::V1, + }; + Ok(match state_pb.version_negotiation { + None => CurrentVersion::NegotiationComplete(version), + Some(vn) => CurrentVersion::StillNegotiating { + version, + min_version: vn.min_version.try_into().map_err(|_| Error::StateDecode)?, + }, + }) +} + #[hax_lib::fstar::verification_status(lax)] pub fn send(state: &SerializedState, rng: &mut R) -> Result { let state_pb = decode_state(state)?; @@ -225,14 +250,35 @@ pub fn send(state: &SerializedState, rng: &mut R) -> Result< key: None, }), Some(pqrpb::pq_ratchet_state::Inner::V1(pb)) => { - let mut chain = Chain::from_pb(state_pb.chain.ok_or(Error::StateDecode)?)?; - let v1states::Send { msg, key, state } = v1states::States::from_pb(pb)?.send(rng)?; - - if let Some(epoch_secret) = key { - chain.add_epoch(epoch_secret); - } - let (index, msg_key) = chain.send_key(msg.epoch - 1)?; + let chain = match state_pb.chain { + None => match state_pb.version_negotiation.as_ref() { + Some(vn) => { + if vn.min_version > Version::V0 as i32 { + Some(chain_from_version_negotiation(vn)?) + } else { + None + } + } + None => { + return Err(Error::ChainNotAvailable); + } + }, + Some(pb) => Some(Chain::from_pb(pb)?), + }; + let (index, msg_key, chain_pb) = match chain { + None => { + assert!(key.is_none()); + (0, vec![], None) + } + Some(mut chain) => { + if let Some(epoch_secret) = key { + chain.add_epoch(epoch_secret); + } + let (index, msg_key) = chain.send_key(msg.epoch - 1)?; + (index, msg_key, Some(chain.into_pb())) + } + }; let msg = msg.serialize(index); assert!(!msg.is_empty()); @@ -242,11 +288,16 @@ pub fn send(state: &SerializedState, rng: &mut R) -> Result< inner: Some(pqrpb::pq_ratchet_state::Inner::V1(state.into_pb())), // Sending never changes our version negotiation. version_negotiation: state_pb.version_negotiation, - chain: Some(chain.into_pb()), + chain: chain_pb, } .encode_to_vec(), msg, - key: Some(msg_key), + // hax does not like `filter` + key: if msg_key.is_empty() { + None + } else { + Some(msg_key) + }, }) } } @@ -257,6 +308,29 @@ pub struct Recv { pub key: MessageKey, } +fn chain_from_version_negotiation( + vn: &pqrpb::pq_ratchet_state::VersionNegotiation, +) -> Result { + Chain::new( + &vn.auth_key, + vn.direction.try_into().map_err(|_| Error::StateDecode)?, + vn.chain_params.ok_or(Error::ChainNotAvailable)?, + ) +} + +fn chain_from( + pb: Option, + vn: Option<&pqrpb::pq_ratchet_state::VersionNegotiation>, +) -> Result { + match pb { + Some(pb) => Ok(Chain::from_pb(pb)?), + None => match vn { + None => Err(Error::ChainNotAvailable), + Some(vn) => chain_from_version_negotiation(vn), + }, + } +} + #[hax_lib::fstar::verification_status(lax)] pub fn recv(state: &SerializedState, msg: &SerializedMessage) -> Result { // Perform version negotiation. At the beginning of our interaction @@ -275,16 +349,10 @@ pub fn recv(state: &SerializedState, msg: &SerializedMessage) -> Result match (v as u8).cmp(&(state_version(&prenegotiated_state_pb) as u8)) { - Ordering::Equal => { + Ordering::Equal | Ordering::Greater => { // Our versions are equal; proceed with existing state prenegotiated_state_pb } - Ordering::Greater => { - // Their version is greater than ours, but still one we support. - // This should not happen, since we should use our highest supported - // version. - return Err(Error::VersionMismatch); - } Ordering::Less => { // Their version is less than ours. If we are allowed to negotiate, we // should. Otherwise, we should error out. @@ -306,7 +374,13 @@ pub fn recv(state: &SerializedState, msg: &SerializedMessage) -> Result Result { - let mut chain = Chain::from_pb(state_pb.chain.ok_or(Error::StateDecode)?)?; let (scka_msg, index, _) = v1states::Message::deserialize(msg)?; let v1states::Recv { key, state } = v1states::States::from_pb(pb)?.recv(&scka_msg)?; - + let msg_key_epoch = scka_msg.epoch - 1; + let mut chain = chain_from(state_pb.chain, state_pb.version_negotiation.as_ref())?; if let Some(epoch_secret) = key { chain.add_epoch(epoch_secret); } + let msg_key = if msg_key_epoch == 0 && index == 0 { + vec![] + } else { + chain.recv_key(msg_key_epoch, index)? + }; - let msg_key = chain.recv_key(scka_msg.epoch - 1, index)?; Ok(Recv { state: pqrpb::PqRatchetState { inner: Some(pqrpb::pq_ratchet_state::Inner::V1(state.into_pb())), @@ -341,7 +419,12 @@ pub fn recv(state: &SerializedState, msg: &SerializedMessage) -> Result Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let version = Version::V1; + + let alex_pq_state = initial_state(Params { + version, + min_version: Version::V0, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version, + min_version: Version::V0, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + + // Now let's send some messages + let Send { + state: alex_pq_state, + msg: msg_a1, + key: key_a1, + } = send(&alex_pq_state, &mut rng)?; + let Send { + state: alex_pq_state, + msg: msg_a2, + key: key_a2, + } = send(&alex_pq_state, &mut rng)?; + let Send { + state: alex_pq_state, + msg: msg_a3, + key: key_a3, + } = send(&alex_pq_state, &mut rng)?; + + let Send { + state: blake_pq_state, + msg: msg_b1, + key: key_b1, + } = send(&blake_pq_state, &mut rng)?; + let Send { + state: blake_pq_state, + msg: msg_b2, + key: key_b2, + } = send(&blake_pq_state, &mut rng)?; + let Send { + state: blake_pq_state, + msg: msg_b3, + key: key_b3, + } = send(&blake_pq_state, &mut rng)?; + + assert_eq!(key_a1, None); + assert_eq!(key_a2, None); + assert_eq!(key_a3, None); + assert_eq!(key_b1, None); + assert_eq!(key_b2, None); + assert_eq!(key_b3, None); + + let Recv { + state: alex_pq_state, + key: key_b2, + } = recv(&alex_pq_state, &msg_b2)?; + assert_eq!(key_b2, None); + // After our first Recv, keys are now non-empty. + let Send { + state: alex_pq_state, + msg: msg_a4, + key: key_a4, + } = send(&alex_pq_state, &mut rng)?; + assert!(key_a4.is_some()); + let Send { + state: mut alex_pq_state, + msg: msg_a5, + key: key_a5, + } = send(&alex_pq_state, &mut rng)?; + assert!(key_a5.is_some()); + + let Recv { + state: blake_pq_state, + key: key_a1, + } = recv(&blake_pq_state, &msg_a1)?; + assert_eq!(key_a1, None); + // After our first Recv, keys are now non-empty. + let Send { + state: blake_pq_state, + msg: msg_b4, + key: key_b4, + } = send(&blake_pq_state, &mut rng)?; + assert!(key_b4.is_some()); + let Send { + state: mut blake_pq_state, + msg: msg_b5, + key: key_b5, + } = send(&blake_pq_state, &mut rng)?; + assert!(key_b5.is_some()); + + for (msg, want_key) in [ + (msg_a3, key_a3), + (msg_a4, key_a4), + (msg_a2, key_a2), + (msg_a5, key_a5), + ] { + let Recv { state, key } = recv(&blake_pq_state, &msg)?; + assert_eq!(want_key, key); + blake_pq_state = state; + } + + for (msg, want_key) in [ + (msg_b1, key_b1), + (msg_b3, key_b3), + (msg_b4, key_b4), + (msg_b5, key_b5), + ] { + let Recv { state, key } = recv(&alex_pq_state, &msg)?; + assert_eq!(want_key, key); + alex_pq_state = state; + } + + Ok(()) + } + + #[test] + fn min_version_v1_always_creates_keys_a2b() -> Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let alex_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V1, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V0, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let Send { + msg: msg_a1, + key: key_a1, + .. + } = send(&alex_pq_state, &mut rng)?; + assert!(key_a1.is_some()); + let Send { + state: blake_pq_state, + key: key_b1, + .. + } = send(&blake_pq_state, &mut rng)?; + assert!(key_b1.is_none()); + let Recv { + state: blake_pq_state, + .. + } = recv(&blake_pq_state, &msg_a1)?; + // After our first Recv, keys are now non-empty. + let Send { key: key_b2, .. } = send(&blake_pq_state, &mut rng)?; + assert!(key_b2.is_some()); + Ok(()) + } + + #[test] + fn min_version_v1_always_creates_keys_b2a() -> Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let alex_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V0, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V1, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let Send { + msg: msg_b1, + key: key_b1, + .. + } = send(&blake_pq_state, &mut rng)?; + assert!(key_b1.is_some()); + let Send { + state: alex_pq_state, + key: key_a1, + .. + } = send(&alex_pq_state, &mut rng)?; + assert!(key_a1.is_none()); + let Recv { + state: alex_pq_state, + .. + } = recv(&alex_pq_state, &msg_b1)?; + // After our first Recv, keys are now non-empty. + let Send { key: key_a2, .. } = send(&alex_pq_state, &mut rng)?; + assert!(key_a2.is_some()); + Ok(()) + } + + #[test] + fn negotiate_to_v0_a2b() -> Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let alex_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V0, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version: Version::V0, + min_version: Version::V0, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::StillNegotiating { + version: Version::MAX, + min_version: Version::V0 + }, + )); + assert!(matches!( + current_version(&blake_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + let Send { + msg: msg_a1, + state: alex_pq_state, + .. + } = send(&alex_pq_state, &mut rng)?; + let Recv { + state: blake_pq_state, + .. + } = recv(&blake_pq_state, &msg_a1)?; + let Send { msg: msg_b1, .. } = send(&blake_pq_state, &mut rng)?; + let Recv { + state: alex_pq_state, + .. + } = recv(&alex_pq_state, &msg_b1)?; + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + Ok(()) + } + + #[test] + fn negotiate_to_v0_b2a() -> Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let alex_pq_state = initial_state(Params { + version: Version::V0, + min_version: Version::V0, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V0, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + assert!(matches!( + current_version(&blake_pq_state)?, + CurrentVersion::StillNegotiating { + version: Version::MAX, + min_version: Version::V0 + }, + )); + let Send { + msg: msg_a1, + state: alex_pq_state, + .. + } = send(&alex_pq_state, &mut rng)?; + let Recv { + state: blake_pq_state, + .. + } = recv(&blake_pq_state, &msg_a1)?; + let Send { msg: msg_b1, .. } = send(&blake_pq_state, &mut rng)?; + let Recv { + state: alex_pq_state, + .. + } = recv(&alex_pq_state, &msg_b1)?; + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + Ok(()) + } + + #[test] + fn negotiation_refused_a2b() -> Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let alex_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V1, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version: Version::V0, + min_version: Version::V0, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::StillNegotiating { + version: Version::MAX, + min_version: Version::V1 + }, + )); + assert!(matches!( + current_version(&blake_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + let Send { + msg: msg_a1, + state: alex_pq_state, + .. + } = send(&alex_pq_state, &mut rng)?; + let Recv { + state: blake_pq_state, + .. + } = recv(&blake_pq_state, &msg_a1)?; + let Send { msg: msg_b1, .. } = send(&blake_pq_state, &mut rng)?; + assert!(matches!( + recv(&alex_pq_state, &msg_b1), + Err(Error::MinimumVersion), + )); + Ok(()) + } + + #[test] + fn negotiation_refused_b2a() -> Result<(), Error> { + let mut rng = OsRng.unwrap_err(); + + let alex_pq_state = initial_state(Params { + version: Version::V0, + min_version: Version::V0, + direction: Direction::A2B, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + let blake_pq_state = initial_state(Params { + version: Version::MAX, + min_version: Version::V1, + direction: Direction::B2A, + auth_key: &[41u8; 32], + chain_params: ChainParams::default(), + })?; + assert!(matches!( + current_version(&alex_pq_state)?, + CurrentVersion::NegotiationComplete(Version::V0), + )); + assert!(matches!( + current_version(&blake_pq_state)?, + CurrentVersion::StillNegotiating { + version: Version::MAX, + min_version: Version::V1 + }, + )); + let Send { msg: msg_a1, .. } = send(&alex_pq_state, &mut rng)?; + assert!(matches!( + recv(&blake_pq_state, &msg_a1), + Err(Error::MinimumVersion) + )); + Ok(()) + } } diff --git a/src/proto/pq_ratchet.proto b/src/proto/pq_ratchet.proto index 3d1e72b..9df2d33 100644 --- a/src/proto/pq_ratchet.proto +++ b/src/proto/pq_ratchet.proto @@ -27,6 +27,7 @@ message PqRatchetState { bytes auth_key = 1; Direction direction = 2; Version min_version = 3; + ChainParams chain_params = 4; } VersionNegotiation version_negotiation = 1; Chain chain = 2; diff --git a/src/test/generic_dr.rs b/src/test/generic_dr.rs index 70aef38..3a4b1fb 100644 --- a/src/test/generic_dr.rs +++ b/src/test/generic_dr.rs @@ -116,7 +116,7 @@ mod test { let key = kdf::hkdf_to_vec( &[0u8; 32], - &[pq_send.key.unwrap(), ec_send.key.unwrap().to_vec()].concat(), + &[pq_send.key.unwrap_or(vec![]), ec_send.key.unwrap().to_vec()].concat(), b"hybrid ratchet merge", 32, ); @@ -141,7 +141,7 @@ mod test { let key = kdf::hkdf_to_vec( &[0u8; 32], - &[pq_recv.key.unwrap(), ec_recv.key.unwrap().to_vec()].concat(), + &[pq_recv.key.unwrap_or(vec![]), ec_recv.key.unwrap().to_vec()].concat(), b"hybrid ratchet merge", 32, ); @@ -151,7 +151,11 @@ mod test { #[test] fn hybrid_ratchet() -> Result<(), Error> { let alex_ec_ratchet = x25519_scka::states::States::init_a(); - let alex_ec_chain = chain::Chain::new(&[43u8; 32], Direction::A2B, ChainParams::default())?; + let alex_ec_chain = chain::Chain::new( + &[43u8; 32], + Direction::A2B, + ChainParams::default().into_pb(), + )?; let alex_ec_state = DoubleRatchet { asymratchet: alex_ec_ratchet, @@ -159,8 +163,11 @@ mod test { }; let blake_ec_ratchet = x25519_scka::states::States::init_b(); - let blake_ec_chain = - chain::Chain::new(&[43u8; 32], Direction::B2A, ChainParams::default())?; + let blake_ec_chain = chain::Chain::new( + &[43u8; 32], + Direction::B2A, + ChainParams::default().into_pb(), + )?; let blake_ec_state = DoubleRatchet { asymratchet: blake_ec_ratchet,