use crate::types::ChannelId;
use derec_cryptography::pairing::PairingSecretKeyMaterial;
use derec_proto::ContactMessage;
#[cfg(any(feature = "serde", target_arch = "wasm32"))]
use serde::{Deserialize, Serialize};
use zeroize::Zeroizing;
#[derive(Debug, Clone)]
pub enum Target {
All,
Single(ChannelId),
Many(Vec<ChannelId>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[cfg_attr(
any(feature = "serde", target_arch = "wasm32"),
derive(Serialize, Deserialize)
)]
pub enum ChannelStatus {
Pending,
#[default]
Paired,
}
#[derive(Clone, Debug)]
#[cfg_attr(
any(feature = "serde", target_arch = "wasm32"),
derive(Serialize, Deserialize)
)]
pub struct Channel {
pub id: ChannelId,
pub transport: derec_proto::TransportProtocol,
#[cfg_attr(any(feature = "serde", target_arch = "wasm32"), serde(default))]
pub communication_info: std::collections::HashMap<String, String>,
#[cfg_attr(any(feature = "serde", target_arch = "wasm32"), serde(default))]
pub status: ChannelStatus,
#[cfg_attr(any(feature = "serde", target_arch = "wasm32"), serde(default))]
pub created_at: u64,
pub role: derec_proto::SenderKind,
#[cfg_attr(any(feature = "serde", target_arch = "wasm32"), serde(default))]
pub replica_id: Option<u64>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HelperInfo {
#[prost(uint64, tag = "1")]
pub channel_id: u64,
#[prost(string, tag = "2")]
pub transport_uri: ::prost::alloc::string::String,
#[prost(bytes = "vec", tag = "4")]
pub shared_key: ::prost::alloc::vec::Vec<u8>,
#[prost(map = "string, string", tag = "5")]
pub communication_info:
::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UserSecret {
#[prost(bytes = "vec", tag = "1")]
pub id: ::prost::alloc::vec::Vec<u8>,
#[prost(string, tag = "2")]
pub name: ::prost::alloc::string::String,
#[prost(bytes = "vec", tag = "3")]
pub data: ::prost::alloc::vec::Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct UserSecrets {
pub version: u32,
pub secrets: Vec<UserSecret>,
pub description: Option<String>,
pub replicas: Option<Replicas>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ReplicaInfo {
#[prost(uint64, tag = "1")]
pub channel_id: u64,
#[prost(string, tag = "2")]
pub transport_uri: ::prost::alloc::string::String,
#[prost(map = "string, string", tag = "4")]
pub communication_info:
::std::collections::HashMap<::prost::alloc::string::String, ::prost::alloc::string::String>,
#[prost(uint64, tag = "5")]
pub replica_id: u64,
#[prost(int32, tag = "6")]
pub sender_kind: i32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Secret {
#[prost(message, repeated, tag = "1")]
pub helpers: ::prost::alloc::vec::Vec<HelperInfo>,
#[prost(message, repeated, tag = "2")]
pub secrets: ::prost::alloc::vec::Vec<UserSecret>,
#[prost(message, optional, tag = "3")]
pub replicas: ::core::option::Option<Replicas>,
#[prost(uint64, tag = "4")]
pub owner_replica_id: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Replicas {
#[prost(message, repeated, tag = "1")]
pub replicas: ::prost::alloc::vec::Vec<ReplicaInfo>,
#[prost(bytes = "vec", tag = "2")]
pub shared_key: ::prost::alloc::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ChannelShare {
#[prost(uint64, tag = "1")]
pub channel_id: u64,
#[prost(bytes = "vec", tag = "2")]
pub committed_share: ::prost::alloc::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ReplicaSecretPayload {
#[prost(message, optional, tag = "1")]
pub secret: ::core::option::Option<Secret>,
#[prost(message, repeated, tag = "2")]
pub shares: ::prost::alloc::vec::Vec<ChannelShare>,
#[prost(bytes = "vec", tag = "3")]
pub shared_key: ::prost::alloc::vec::Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecretKind {
SharedKey = 0,
PairingSecret = 1,
PairingContact = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MissingPolicy {
Skip,
Fail,
}
#[derive(Clone)]
pub struct PairingKeyMaterial(Zeroizing<Vec<u8>>);
impl PairingKeyMaterial {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self(Zeroizing::new(bytes))
}
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
pub(crate) fn from_secret(material: &PairingSecretKeyMaterial) -> Self {
use ark_serialize::CanonicalSerialize as _;
let mut buf = Vec::with_capacity(material.compressed_size());
material
.serialize_compressed(&mut buf)
.expect("ark serialization of PairingSecretKeyMaterial is infallible");
Self(Zeroizing::new(buf))
}
pub(crate) fn to_secret(&self) -> crate::Result<PairingSecretKeyMaterial> {
use ark_serialize::CanonicalDeserialize as _;
PairingSecretKeyMaterial::deserialize_compressed(self.0.as_slice())
.map_err(|_| crate::Error::Invariant("stored PairingSecret bytes failed to decode"))
}
}
#[cfg(feature = "serde")]
impl Serialize for PairingKeyMaterial {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.as_slice().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for PairingKeyMaterial {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let bytes = Vec::<u8>::deserialize(deserializer)?;
Ok(Self(Zeroizing::new(bytes)))
}
}
#[cfg(feature = "serde")]
mod contact_serde {
use super::ContactMessage;
use prost::Message as _;
use serde::{Deserialize as _, Deserializer, Serialize as _, Serializer};
pub(super) fn serialize<S>(contact: &ContactMessage, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
contact.encode_to_vec().serialize(serializer)
}
pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<ContactMessage, D::Error>
where
D: Deserializer<'de>,
{
let bytes = Vec::<u8>::deserialize(deserializer)?;
ContactMessage::decode(bytes.as_slice()).map_err(serde::de::Error::custom)
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SecretValue {
SharedKey(crate::types::SharedKey),
PairingSecret(PairingKeyMaterial),
PairingContact(#[cfg_attr(feature = "serde", serde(with = "contact_serde"))] ContactMessage),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StateKind {
PendingVerification,
PendingRecovery,
PendingUnpair,
SharingRound,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum StateKey {
PendingVerification { channel_id: ChannelId },
PendingRecovery { version: u32 },
PendingUnpair { channel_id: ChannelId },
SharingRound,
}
impl StateKey {
pub fn kind(&self) -> StateKind {
match self {
StateKey::PendingVerification { .. } => StateKind::PendingVerification,
StateKey::PendingRecovery { .. } => StateKind::PendingRecovery,
StateKey::PendingUnpair { .. } => StateKind::PendingUnpair,
StateKey::SharingRound => StateKind::SharingRound,
}
}
}
#[derive(Debug, Clone)]
pub enum StateItem {
PendingVerification {
channel_id: ChannelId,
request: derec_proto::VerifyShareRequestMessage,
},
PendingRecovery {
version: u32,
shares: Vec<derec_proto::GetShareResponseMessage>,
},
PendingUnpair {
channel_id: ChannelId,
started_at: u64,
},
SharingRound {
version: u32,
pending: std::collections::HashSet<ChannelId>,
confirmed: std::collections::HashSet<ChannelId>,
failed: std::collections::HashSet<ChannelId>,
started_at: u64,
},
}
impl StateItem {
pub fn kind(&self) -> StateKind {
match self {
StateItem::PendingVerification { .. } => StateKind::PendingVerification,
StateItem::PendingRecovery { .. } => StateKind::PendingRecovery,
StateItem::PendingUnpair { .. } => StateKind::PendingUnpair,
StateItem::SharingRound { .. } => StateKind::SharingRound,
}
}
pub fn key(&self) -> StateKey {
match self {
StateItem::PendingVerification { channel_id, .. } => StateKey::PendingVerification {
channel_id: *channel_id,
},
StateItem::PendingRecovery { version, .. } => {
StateKey::PendingRecovery { version: *version }
}
StateItem::PendingUnpair { channel_id, .. } => StateKey::PendingUnpair {
channel_id: *channel_id,
},
StateItem::SharingRound { .. } => StateKey::SharingRound,
}
}
}
#[derive(Debug, Clone)]
pub struct Share {
pub secret_id: u64,
pub version: u32,
pub replica_id: Option<u64>,
pub bytes: Vec<u8>,
}
#[cfg(all(test, feature = "serde"))]
mod tests {
use super::*;
#[test]
fn secret_value_serde_round_trips_all_variants() {
let cases = [
SecretValue::SharedKey([7u8; 32]),
SecretValue::PairingSecret(PairingKeyMaterial::from_bytes(vec![1, 2, 3, 4, 5])),
SecretValue::PairingContact(ContactMessage {
nonce: 42,
..Default::default()
}),
];
for value in cases {
let json = serde_json::to_vec(&value).expect("serialize");
let decoded: SecretValue = serde_json::from_slice(&json).expect("deserialize");
match (&value, &decoded) {
(SecretValue::SharedKey(a), SecretValue::SharedKey(b)) => assert_eq!(a, b),
(SecretValue::PairingSecret(a), SecretValue::PairingSecret(b)) => {
assert_eq!(a.as_bytes(), b.as_bytes())
}
(SecretValue::PairingContact(a), SecretValue::PairingContact(b)) => {
assert_eq!(a, b)
}
_ => panic!("variant changed across serde round-trip"),
}
}
}
#[test]
fn pairing_key_material_serde_matches_byte_accessors() {
let material = PairingKeyMaterial::from_bytes(vec![9, 8, 7, 6]);
let json = serde_json::to_vec(&material).expect("serialize");
let decoded: PairingKeyMaterial = serde_json::from_slice(&json).expect("deserialize");
assert_eq!(material.as_bytes(), decoded.as_bytes());
}
}