co-didcomm 0.8.0

COKIT fork of didcomm-rs — DIDComm messaging v2 implementation with modern crypto and WASM support.
Documentation
#[cfg(feature = "raw-crypto")]
extern crate chacha20poly1305;
#[cfg(feature = "raw-crypto")]
extern crate co_didcomm;

use co_didcomm::{Error, Message};
use serde_json::Value;

#[test]
fn sets_message_type_correctly_for_plain_messages() -> Result<(), Error> {
    let message = Message::new()
        .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
        .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"]);

    let jwm_string: String = serde_json::to_string(&message)?;
    let jwm_object: Value = serde_json::from_str(&jwm_string)?;

    assert!(jwm_object["typ"].as_str().is_some());
    assert_eq!(
        jwm_object["typ"]
            .as_str()
            .ok_or(Error::JwmHeaderParseError)?,
        "application/didcomm-plain+json",
    );

    Ok(())
}

#[cfg(feature = "raw-crypto")]
mod tests {
    use std::str::from_utf8;

    use co_didcomm::{
        crypto::{CryptoAlgorithm, SignatureAlgorithm, Signer},
        Error, JwmHeader, Message, MessageType,
    };
    use rand_core::OsRng;
    use serde_json::Value;
    use utilities::{get_keypair_set, KeyPairSet};

    #[test]
    #[cfg(feature = "raw-crypto")]
    fn sets_message_type_correctly_for_signed_messages() -> Result<(), Error> {
        let sign_keypair = ed25519_dalek::SigningKey::generate(&mut OsRng);
        let jws_string = Message::new()
            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
            .as_flat_jws(&SignatureAlgorithm::EdDsa)
            .sign(SignatureAlgorithm::EdDsa.signer(), &sign_keypair.to_bytes())?;

        let jws_object: Value = serde_json::from_str(&jws_string)?;
        assert!(jws_object["protected"].as_str().is_some());
        let protected_encoded = jws_object["protected"]
            .as_str()
            .ok_or(Error::JwmHeaderParseError)?;
        let protected_decoded_buffer = base64_url::decode(&protected_encoded.as_bytes())?;
        let protected_decoded_string =
            from_utf8(&protected_decoded_buffer).map_err(|_| Error::JwsParseError)?;
        let protected_object: Value = serde_json::from_str(protected_decoded_string)?;

        assert_eq!(
            protected_object["typ"]
                .as_str()
                .ok_or(Error::JwmHeaderParseError)?,
            "application/didcomm-signed+json",
        );

        Ok(())
    }

    #[test]
    #[cfg(feature = "raw-crypto")]
    fn sets_message_type_correctly_for_signed_and_encrypted_messages() -> Result<(), Error> {
        let KeyPairSet {
            alice_private,
            bobs_public,
            ..
        } = get_keypair_set();
        let sign_keypair = ed25519_dalek::SigningKey::generate(&mut OsRng);
        let message = Message::new()
            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
            .as_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()))
            .kid(&hex::encode(sign_keypair.verifying_key().to_bytes()));

        let jwe_string = message.seal_signed(
            &alice_private,
            Some(vec![Some(bobs_public.to_vec())]),
            SignatureAlgorithm::EdDsa,
            &sign_keypair.to_bytes(),
        )?;

        let jwe_object: Value = serde_json::from_str(&jwe_string)?;

        assert!(jwe_object["protected"].as_str().is_some());
        let protected_encoded = jwe_object["protected"]
            .as_str()
            .ok_or(Error::JwmHeaderParseError)?;
        let protected_decoded_buffer = base64_url::decode(&protected_encoded.as_bytes())?;
        let protected_decoded_string =
            from_utf8(&protected_decoded_buffer).map_err(|_| Error::JwsParseError)?;
        let protected_object: Value = serde_json::from_str(protected_decoded_string)?;

        assert_eq!(
            protected_object["typ"]
                .as_str()
                .ok_or(Error::JwmHeaderParseError)?,
            "application/didcomm-encrypted+json",
        );

        Ok(())
    }

    #[test]
    #[cfg(feature = "raw-crypto")]
    fn sets_message_type_correctly_for_forwarded_messages() -> Result<(), Error> {
        let KeyPairSet {
            alice_private,
            bobs_public,
            mediators_public,
            ..
        } = get_keypair_set();
        let message = Message::new()
            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
            .as_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()));

        let jwe_string = message.routed_by(
            &alice_private,
            Some(vec![Some(bobs_public.to_vec())]),
            "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
            Some(mediators_public.to_vec()),
        )?;

        let jwe_object: Value = serde_json::from_str(&jwe_string)?;

        assert!(jwe_object["protected"].as_str().is_some());
        let protected_encoded = jwe_object["protected"]
            .as_str()
            .ok_or(Error::JwmHeaderParseError)?;
        let protected_decoded_buffer = base64_url::decode(&protected_encoded.as_bytes())?;
        let protected_decoded_string =
            from_utf8(&protected_decoded_buffer).map_err(|_| Error::JwsParseError)?;
        let protected_object: Value = serde_json::from_str(protected_decoded_string)?;

        assert_eq!(
            protected_object["typ"]
                .as_str()
                .ok_or(Error::JwmHeaderParseError)?,
            "https://didcomm.org/routing/2.0/forward",
        );

        Ok(())
    }

    #[test]
    #[cfg(feature = "raw-crypto")]
    fn keeps_inner_message_type_as_plain_for_signed_messages() -> Result<(), Error> {
        let sign_keypair = ed25519_dalek::SigningKey::generate(&mut OsRng);
        let jws_string = Message::new()
            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
            .as_jws(&SignatureAlgorithm::EdDsa)
            .kid(&hex::encode(sign_keypair.verifying_key().to_bytes()))
            .sign(SignatureAlgorithm::EdDsa.signer(), &sign_keypair.to_bytes())?;

        let jws_object: Value = serde_json::from_str(&jws_string)?;
        let jws_protected_encoded = jws_object
            .get("signatures")
            .ok_or(Error::JwsParseError)?
            .as_array()
            .ok_or(Error::JwsParseError)?[0]
            .as_object()
            .ok_or(Error::JwsParseError)?
            .get("protected")
            .ok_or(Error::JwsParseError)?
            .as_str()
            .ok_or(Error::JwsParseError)?;
        let jws_protected_string_decoded = base64_url::decode(&jws_protected_encoded)?;
        let jws_jwm_header: JwmHeader = serde_json::from_slice(&jws_protected_string_decoded)?;

        let payload_string_encoded = jws_object
            .get("payload")
            .ok_or(Error::JwsParseError)?
            .as_str()
            .ok_or(Error::JwsParseError)?;
        let payload_string_decoded = base64_url::decode(&payload_string_encoded)?;
        let payload_jwm_header: JwmHeader = serde_json::from_slice(&payload_string_decoded)?;
        let received_message = Message::receive(&jws_string, None, None, None)?;

        assert_eq!(jws_jwm_header.typ, MessageType::DidCommJws);
        assert_eq!(payload_jwm_header.typ, MessageType::DidCommRaw);
        assert_eq!(
            received_message.get_jwm_header().typ,
            MessageType::DidCommRaw
        );

        Ok(())
    }
}

#[test]
fn serializes_missing_body_as_empty_object() -> Result<(), Error> {
    let message = Message::new()
        .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
        .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"]);

    let jwm_string: String = serde_json::to_string(&message)?;
    let jwm_object: Value = serde_json::from_str(&jwm_string)?;

    assert!(jwm_object["body"].as_object().is_some());
    assert_eq!(serde_json::to_string(&jwm_object["body"])?, "{}",);

    Ok(())
}

#[test]
fn serializes_existing_body_as_object() -> Result<(), Error> {
    let message = Message::new()
        .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
        .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
        .body(r#"{"foo":"bar"}"#)?;

    let jwm_string: String = serde_json::to_string(&message)?;
    let jwm_object: Value = serde_json::from_str(&jwm_string)?;

    assert!(jwm_object["body"].as_object().is_some());
    assert_eq!(
        serde_json::to_string(&jwm_object["body"])?,
        r#"{"foo":"bar"}"#,
    );

    Ok(())
}