use crate::{
library::didcomm_receive::didcomm_receive, DidCommContext, DidCommHeader, Identity, IdentityResolver,
PrivateIdentity, PrivateIdentityResolver, ReceiveError,
};
use anyhow::anyhow;
use co_primitives::{from_json_string, Did};
use didcomm_rs::{Jwe, Jws, MessageType};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::value::RawValue;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Message {
PlainJson { header: DidCommHeader, body: String },
SignedJson { sender: Did, header: DidCommHeader, body: String },
AnonCryptJson { header: DidCommHeader, body: String },
AuthCryptJson { sender: Did, header: DidCommHeader, body: String },
}
impl Message {
pub async fn receive<I, P>(sender_resolver: I, recipent_resolver: P, data: &[u8]) -> Result<Message, ReceiveError>
where
I: IdentityResolver + Send + Sync + 'static,
P: PrivateIdentityResolver + Send + Sync + 'static,
{
let message = std::str::from_utf8(data).map_err(|e| ReceiveError::UnknownFormat(e.into()))?;
let message_type = get_message_type(message).map_err(ReceiveError::UnknownFormat)?;
if message_type == MessageType::DidCommJwe {
let jwe: Jwe = serde_json::from_str(message).map_err(|e| ReceiveError::UnknownFormat(e.into()))?;
let sender_identity = if let Some(sender_kid) = &jwe.get_skid() {
Some(
sender_resolver
.resolve(sender_kid)
.await
.map_err(|e| ReceiveError::BadDid(sender_kid.to_owned(), e.into()))?,
)
} else {
None
};
let recipents = jwe
.recipients
.unwrap_or_else(|| jwe.recipient.map(|item| vec![item]).unwrap_or_default());
let recipent_resolver_ref = &recipent_resolver;
for recipent in &recipents {
let recipent_did = match &recipent.header.kid {
Some(kid) => kid,
None => continue,
};
let recipent_identity = match recipent_resolver_ref.resolve_private(recipent_did).await {
Ok(i) => i,
Err(_) => continue,
};
let recipent_didcomm_context = match recipent_identity.didcomm_private() {
Some(i) => i,
None => continue,
};
let (header, body) = recipent_didcomm_context.receive(&sender_resolver, message).await?;
if let Some(from) = &header.from {
if let Some(sender_identity) = sender_identity {
if from == sender_identity.identity() {
return Ok(Message::AuthCryptJson {
sender: sender_identity.identity().to_owned(),
header,
body,
});
}
}
}
return Ok(Message::AnonCryptJson { header, body });
}
return Err(ReceiveError::NoRecipent);
}
if message_type == MessageType::DidCommJws {
let (header, body) = didcomm_receive(None, &sender_resolver, message).await?;
let sender = verify_signing_identity(&sender_resolver, &header, message).await?;
return Ok(Message::SignedJson { sender, header, body });
}
if message_type == MessageType::DidCommRaw {
let plain_message: DidCommMessage =
serde_json::from_str(message).map_err(|e| ReceiveError::UnknownFormat(e.into()))?;
return Ok(Message::PlainJson {
header: plain_message.header,
body: plain_message.body.map(|r| r.get()).unwrap_or("null").to_owned(),
});
}
Err(ReceiveError::UnknownFormat(anyhow!("Expected JSON as JWE, JWS or plain DIDComm")))
}
pub fn header(&self) -> &DidCommHeader {
match self {
Message::PlainJson { header, body: _ } => header,
Message::SignedJson { sender: _, header, body: _ } => header,
Message::AnonCryptJson { header, body: _ } => header,
Message::AuthCryptJson { sender: _, header, body: _ } => header,
}
}
pub fn body(&self) -> &str {
match self {
Message::PlainJson { header: _, body } => body,
Message::SignedJson { sender: _, header: _, body } => body,
Message::AnonCryptJson { header: _, body } => body,
Message::AuthCryptJson { sender: _, header: _, body } => body,
}
}
pub fn body_deserialize<T: DeserializeOwned>(&self) -> Result<T, anyhow::Error> {
Ok(from_json_string(self.body())?)
}
pub fn is_validated_sender(&self) -> bool {
self.sender().is_some()
}
pub fn sender(&self) -> Option<&Did> {
match self {
Message::AuthCryptJson { sender, header, body: _ } if Some(sender) == header.from.as_ref() => Some(sender),
Message::SignedJson { sender, header, body: _ } if Some(sender) == header.from.as_ref() => Some(sender),
_ => None,
}
}
pub fn into_inner(self) -> (DidCommHeader, String) {
match self {
Message::PlainJson { header, body } => (header, body),
Message::SignedJson { sender: _, header, body } => (header, body),
Message::AnonCryptJson { header, body } => (header, body),
Message::AuthCryptJson { sender: _, header, body } => (header, body),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
struct UnknownReceivedMessage<'a> {
#[serde(borrow)]
pub signature: Option<&'a RawValue>,
#[serde(borrow)]
pub signatures: Option<&'a RawValue>,
#[serde(borrow)]
pub iv: Option<&'a RawValue>,
}
fn get_message_type(message: &str) -> Result<MessageType, anyhow::Error> {
let to_check: UnknownReceivedMessage = serde_json::from_str(message)?;
if to_check.iv.is_some() {
return Ok(MessageType::DidCommJwe);
}
if to_check.signatures.is_some() || to_check.signature.is_some() {
return Ok(MessageType::DidCommJws);
}
let didcomm_message: Option<didcomm_rs::Message> = serde_json::from_str(message).ok();
if let Some(didcomm_message) = didcomm_message {
return Ok(didcomm_message.get_jwm_header().typ.clone());
}
let _plain_message: DidCommMessage = serde_json::from_str(message)?;
Ok(MessageType::DidCommRaw)
}
async fn verify_signing_identity<I>(
sender_resolver: &I,
header: &DidCommHeader,
message: &str,
) -> Result<Did, ReceiveError>
where
I: IdentityResolver + Send + Sync + 'static,
{
if let Some(from) = &header.from {
let sender_identity = sender_resolver
.resolve(from)
.await
.map_err(|e| ReceiveError::BadDid(from.to_owned(), e.into()))?;
let sender_context = sender_identity
.didcomm_public()
.ok_or_else(|| ReceiveError::BadDid(from.to_owned(), anyhow!("No didcomm public context.")))?;
let sender_public_key = sender_context
.verification_method()
.public_key_bytes()
.map_err(|err| ReceiveError::BadDid(from.to_owned(), err))?;
let sender_kid = hex::encode(&sender_public_key);
let jws: Jws = serde_json::from_str(message).map_err(|e| ReceiveError::UnknownFormat(e.into()))?;
let signatures = if let Some(signatures) = jws.signatures {
signatures
} else if let Some(signature) = jws.signature {
vec![signature]
} else {
vec![]
};
for signature in signatures {
if let Some(kid) = &signature.get_kid() {
if kid == &sender_kid || kid == sender_identity.identity() {
return Ok(from.clone());
}
}
}
Err(ReceiveError::InvalidSigningKeyId(anyhow!("Can not match from header")))
} else {
Err(ReceiveError::UnknownFormat(anyhow!("No from header")))
}
}
#[derive(Debug, Serialize, Deserialize)]
struct DidCommMessage<'a> {
#[serde(flatten)]
header: DidCommHeader,
#[serde(borrow)]
body: Option<&'a RawValue>,
}