use didcomm_rs::{DdoParser, Jwe, Jwk, KeyAlgorithm, Message, crypto::{CryptoAlgorithm, SignatureAlgorithm, Signer}, resolve_any, try_resolve_any};
use crate::{prelude::*, Error, unlocked::UnlockedWallet, contents::Content};
impl UnlockedWallet {
pub fn create_message() -> String {
Message::new()
.as_raw_json()
.unwrap() }
pub fn create_jwe_message(from: &str, to: &[&str], alg: CryptoAlgorithm) -> String {
serde_json::to_string(&Message::new()
.to(to)
.from(from)
.as_jwe(&alg)).unwrap()
}
pub fn seal_signed(&self, message: &str)
-> Result<String, Error> {
let m: Message = serde_json::from_str(message)?;
let document = try_resolve_any(&m.get_didcomm_header().from.clone().unwrap_or_default())
.map_err(|e| Error::Other(e.into()))?;
let ekp: Option<(String, &Content)> =
if let Some(ka) = document.find_key_agreement("Ed25519") {
if let Some(kp) = self.contents.get_by_controller(&ka.controller) {
Some(kp)
} else { None }
} else { None };
if let Some(kp) = ekp {
match kp {
(_, Content::KeyPair(kp)) => {
let mut jws = Message::new()
.set_didcomm_header(m.get_didcomm_header().to_owned());
let mut key = Jwk::new();
let alg: SignatureAlgorithm;
match kp.public_key.key_type {
KeyType::JwsVerificationKey2020 => {
key.kty = Some(String::from("EC"));
key.crv = Some(String::from("P-256"));
key.kid = Some(calc_kid(&self.id, &kp.public_key.controller[0]));
key.add_other_header(String::from("x"), base64::encode(&kp.public_key.public_key));
alg = SignatureAlgorithm::Es256;
},
KeyType::Ed25519VerificationKey2018 => {
key.alg = KeyAlgorithm::EdDsa;
key.kty = Some(String::from("OKP"));
key.crv = Some(String::from("Ed25519"));
key.kid = Some(calc_kid(&self.id, &kp.public_key.controller[0]));
key.add_other_header(String::from("x"), base64::encode(&kp.public_key.public_key));
alg = SignatureAlgorithm::EdDsa;
},
KeyType::EcdsaSecp256k1VerificationKey2019 |
KeyType::EcdsaSecp256k1RecoveryMethod2020 => {
key.alg = KeyAlgorithm::EcdhEs;
key.kty = Some(String::from("EC"));
key.crv = Some(String::from("secp256k1"));
key.kid = Some(calc_kid(&self.id, &kp.public_key.controller[0]));
key.add_other_header(String::from("x"), base64::encode(&kp.public_key.public_key));
alg = SignatureAlgorithm::Es256k;
},
_ => return Err(Error::UnsupportedKeyType)
}
jws.jwm_header.kid = key.kid.clone();
jws.jwm_header.alg = Some(key.alg.to_string());
jws.jwm_header.cty = Some(String::from("JWM"));
jws.jwm_header.jwk = Some(key);
jws = jws.set_body(message.as_bytes());
self.seal_encrypted_str(&jws.sign(alg.signer(), &kp.private_key())?)
},
_ => Err(Error::KeyNotFound),
}
} else {
Err(Error::KeyNotFound)
}
}
pub fn seal_encrypted_str(&self, message: &str)
-> Result<String, Error> {
let m: Message = serde_json::from_str(message)?;
self.seal_encrypted(m)
}
pub fn seal_encrypted(&self, message: Message)
-> Result<String, Error> {
let document = try_resolve_any(&message.get_didcomm_header().from.clone().unwrap_or_default())
.map_err(|e| Error::Other(e.into()))?;
let ekp: Option<(String, &Content)> =
if let Some(controller) = document.find_public_key_controller_for_curve("X25519") {
if let Some(kp) = self.contents.get_by_controller(&controller) {
Some(kp)
} else { None }
} else { None };
if let Some(ekp) = ekp {
match ekp {
(_, Content::KeyPair(ekp)) => {
let mut e_key = Jwk::new();
match ekp.public_key.key_type {
KeyType::X25519KeyAgreementKey2019 => {
e_key.crv = Some(String::from("ECDH-ES+A256KW"));
e_key.kid = Some(calc_kid(&self.id, &ekp.public_key.controller[0]));
e_key.add_other_header(String::from("x"), base64::encode(&ekp.public_key.public_key));
},
_ => return Err(Error::UnsupportedKeyType)
}
let mut jwe = Message::new();
jwe.jwm_header.kid = e_key.kid.clone();
jwe.jwm_header.alg = Some(String::from("ECDH-ES+A256KW"));
jwe.jwm_header.cty = Some(String::from("JWM"));
jwe.jwm_header.jwk = Some(e_key);
jwe.set_didcomm_header(message.get_didcomm_header().to_owned())
.set_body(serde_json::to_string(&message)?.as_bytes())
.seal(&ekp.private_key())
.map_err(|e| Error::DidcommError(e))
},
_ => return Err(Error::UnsupportedKeyType)
}
} else {
Err(Error::KeyNotFound)
}
}
pub fn receive_message(&self, msg_bytes: &[u8]) -> Result<Message, Error> {
let jwe: Jwe = serde_json::from_slice(msg_bytes)?;
if let Some(kp) = self.get_content_from_header(&jwe.header.kid) {
match kp {
Content::KeyPair(unwrapped_kp) => {
Ok(Message::receive(
&String::from_utf8_lossy(msg_bytes),
&unwrapped_kp.private_key(),
)?)
},
_ => return Err(Error::ContentNotFound(jwe.header.kid.iter().map(|s| s.to_owned()).collect::<String>()))
}
} else {
Err(Error::DidcommError(didcomm_rs::Error::JweParseError))
}
}
fn get_content_from_header(&self, ids: &Option<String>) -> Option<Content> {
if let Some(id) = ids {
if let Some(document) = resolve_any(id) {
if let Some(controller) = document.find_public_key_controller_for_curve("X25519") {
match self.contents.get_by_controller(&controller) {
Some((_, cnt)) => Some(cnt.to_owned()),
None => None
}
} else { None }
} else {
None
}
} else { None }
}
}
fn calc_kid(id: &str, cntrlr: &str) -> String {
[id, cntrlr].join("#")
}
#[test]
fn send_receive_test() {
use base58::FromBase58;
let m = Message::new()
.from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
.to(&vec!("did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"))
.as_jwe(&didcomm_rs::crypto::CryptoAlgorithm::XC20P);
let mut alice_wallet = UnlockedWallet::new("alice");
let alice_didkey_content = Content::KeyPair(KeyPair {
public_key: PublicKeyInfo::new(KeyType::X25519KeyAgreementKey2019, &"7By6kV2t2d188odEM4ExAve1UithKT6dLva4dwsDT3ak".from_base58().unwrap()),
private_key: "6QN8DfuN9hjgHgPvLXqgzqYE3jRRGRrmJQZkd5tL8paR".from_base58().unwrap()
}.set_controller(vec!("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp".into())));
let alice_import = alice_wallet.import_content(&alice_didkey_content);
let mut bob_wallet = UnlockedWallet::new("bob");
let bob_didkey_content = Content::KeyPair(KeyPair {
public_key: PublicKeyInfo::new(KeyType::X25519KeyAgreementKey2019, &"FcoNC5NqP9CePWbhfz95iHaEsCjGkZUioK9Ck7Qiw286".from_base58().unwrap()),
private_key: "HBTcN2MrXNRj9xF9oi8QqYyuEPv3JLLjQKuEgW9oxVKP".from_base58().unwrap()
}.set_controller(vec!("did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG".into())));
let bob_import = bob_wallet.import_content(&bob_didkey_content);
let alice_encrypted_message = alice_wallet.seal_encrypted_str(&serde_json::to_string(&m).unwrap());
assert!(alice_encrypted_message.is_ok());
let bob_received_message = bob_wallet.receive_message(&alice_encrypted_message.unwrap().as_bytes());
assert!(alice_import.is_some());
assert!(bob_import.is_some());
assert!(bob_received_message.is_ok());
}