Skip to main content

co_didcomm/messages/
message.rs

1#![allow(dead_code)]
2
3#[cfg(feature = "raw-crypto")]
4use crate::{
5    crypto::{CryptoAlgorithm, Cypher, SignatureAlgorithm, Signer},
6    helpers::{encrypt_cek, get_crypter_from_header, get_message_type, receive_jwe, receive_jws},
7    Jwe, Mediated,
8};
9use crate::{Attachment, DidCommHeader, Error, JwmHeader, MessageType, PriorClaims, Recipient};
10#[cfg(feature = "raw-crypto")]
11use base64_url::decode;
12#[cfg(all(feature = "resolve", feature = "raw-crypto"))]
13use ddoresolver_rs::*;
14#[cfg(feature = "raw-crypto")]
15use rand::{RngCore, SeedableRng};
16#[cfg(feature = "raw-crypto")]
17use rand_chacha::ChaCha20Rng;
18use serde::{Deserialize, Serialize};
19use serde_json::{json, Value};
20use crate::Result;
21
22/// DIDComm message structure.
23///
24/// `Message`s are used to construct new DIDComm messages.
25///
26/// A common flow is
27/// - [creating a message][Message::new()]
28/// - setting different properties with [chained setters](#impl-1)
29/// - serializing the message to one of the following formats:
30///   - a [plain][Message::as_raw_json()] DIDComm message
31///   - a [signed][Message::sign()] JWS envelope
32///   - an [encrypted][Message::seal()] JWE envelope
33///   - a [sealed and encrypted][Message::seal_signed()] JWE envelope
34///
35/// For examples have a look [here][`crate`].
36///
37/// [Specification](https://identity.foundation/didcomm-messaging/spec/#message-structure)
38#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
39pub struct Message {
40    /// JOSE header, which is sent as public part with JWE.
41    #[serde(flatten)]
42    pub(crate) jwm_header: JwmHeader,
43
44    /// DIDComm headers part, sent as part of encrypted message in JWE.
45    #[serde(flatten)]
46    pub(crate) didcomm_header: DidCommHeader,
47
48    /// single recipient of JWE `recipients` collection as used in JWE
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub(crate) recipients: Option<Vec<Recipient>>,
51
52    /// Message payload, which can be basically anything (JSON, text, file, etc.) represented
53    ///     as base64url String of raw bytes of data.
54    /// No direct access for encode/decode purposes! Use `get_body()` / `set_body()` methods instead.
55    pub(crate) body: Value,
56
57    /// Flag that toggles JWE serialization to flat JSON.
58    /// Not part of the serialized JSON and ignored when deserializing.
59    #[serde(skip)]
60    pub(crate) serialize_flat_jwe: bool,
61
62    /// Flag that toggles JWS serialization to flat JSON.
63    /// Not part of the serialized JSON and ignored when deserializing.
64    #[serde(skip)]
65    pub(crate) serialize_flat_jws: bool,
66
67    #[serde(skip_serializing_if = "Vec::is_empty", default)]
68    pub(crate) attachments: Vec<Attachment>,
69}
70
71impl Message {
72    /// Generates EMPTY default message.
73    /// Use extension messages to build final one before `send`ing.
74    pub fn new() -> Self {
75        Message {
76            jwm_header: JwmHeader::default(),
77            didcomm_header: DidCommHeader::new(),
78            recipients: None,
79            body: json!({}),
80            attachments: Vec::new(),
81            serialize_flat_jwe: false,
82            serialize_flat_jws: false,
83        }
84    }
85
86    /// Adds (or updates) custom unique header key-value pair to the header.
87    /// This portion of header is not sent as JOSE header.
88    pub fn add_header_field(mut self, key: String, value: String) -> Self {
89        if key.is_empty() {
90            return self;
91        }
92        self.didcomm_header.other.insert(key, value);
93        self
94    }
95
96    /// Sets message to be serialized as flat JWE JSON.
97    /// If this message has multiple targets, `seal`ing it will result in an Error.
98    #[cfg(feature = "raw-crypto")]
99    pub fn as_flat_jwe(
100        mut self,
101        alg: &CryptoAlgorithm,
102        recipient_public_key: Option<Vec<u8>>,
103    ) -> Self {
104        self.serialize_flat_jwe = true;
105        self.as_jwe(alg, recipient_public_key)
106    }
107
108    /// Sets message to be serialized as flat JWS JSON and then calls `as_jws`.
109    /// If this message has multiple targets, `seal`ing it will result in an Error.
110    #[cfg(feature = "raw-crypto")]
111    pub fn as_flat_jws(mut self, alg: &SignatureAlgorithm) -> Self {
112        self.serialize_flat_jws = true;
113        self.as_jws(alg)
114    }
115
116    /// Shortcut to `DidCommHeader::get_message_uri`
117    ///
118    pub fn get_message_uri(&self) -> String {
119        self.didcomm_header.get_message_uri()
120    }
121
122    /// Sets `thid` and `pthid` same as those in `replying_to`
123    /// Shortcut to `DidCommHeader::reply_to` method
124    ///
125    /// * `replying_to` - ref to message we're replying to
126    pub fn reply_to(mut self, replying_to: &Self) -> Self {
127        self.didcomm_header.reply_to(&replying_to.didcomm_header);
128        self
129    }
130
131    /// Sets `pthid` to the `parent`'s `thid`.
132    /// It defaults to `id` if `thid` is missing.
133    ///
134    /// # Parameters
135    ///
136    /// * `parent` - ref to a parent threaded `Message`
137    ///
138    pub fn with_parent(mut self, parent: &Self) -> Self {
139        self.didcomm_header.pthid = Some(
140            if let Some(thid_ref) = parent.didcomm_header.thid.as_ref() {
141                thid_ref.clone()
142            } else {
143                parent.didcomm_header.id.clone()
144            },
145        );
146
147        self
148    }
149
150    /// Setter of `from` header
151    /// Helper method.
152    ///
153    /// For `resolve` feature will set `kid` header automatically
154    ///     based on the did document resolved.
155    #[cfg(feature = "raw-crypto")]
156    pub fn as_jwe(mut self, alg: &CryptoAlgorithm, recipient_public_key: Option<Vec<u8>>) -> Self {
157        self.jwm_header.as_encrypted(alg);
158        if let Some(key) = recipient_public_key {
159            self.jwm_header.kid = Some(base64_url::encode(&key));
160        } else {
161            #[cfg(feature = "resolve")]
162            {
163                if let Some(from) = &self.didcomm_header.from {
164                    if let Some(document) = resolve_any(from) {
165                        match alg {
166                            CryptoAlgorithm::XC20P => {
167                                self.jwm_header.kid =
168                                    document.find_public_key_id_for_curve("X25519")
169                            }
170                            CryptoAlgorithm::A256GCM | CryptoAlgorithm::A256CBC => {
171                                self.jwm_header.kid = document.find_public_key_id_for_curve("P-256")
172                            }
173                        }
174                    }
175                }
176            }
177        }
178        self
179    }
180
181    /// Creates set of JWM related headers for the JWE
182    /// Modifies JWM related header portion to match
183    ///     encryption implementation and leaves other
184    ///     parts unchanged.  TODO + FIXME: complete implementation
185    #[cfg(feature = "raw-crypto")]
186    pub fn as_jws(mut self, alg: &SignatureAlgorithm) -> Self {
187        self.jwm_header.as_signed(alg);
188        self
189    }
190
191    /// Setter of the `body`.
192    /// Note, that given text has to be a valid JSON string to be a valid body value.
193    pub fn body(mut self, body: &str) -> Result<Self> {
194        self.body = serde_json::from_str(body)?;
195        Ok(self)
196    }
197
198    /// Setter of `didcomm_header`.
199    /// Replaces existing one with provided by consuming both values.
200    /// Returns modified instance of `Self`.
201    pub fn didcomm_header(mut self, h: DidCommHeader) -> Self {
202        self.didcomm_header = h;
203        self
204    }
205
206    /// Setter of `from` header.
207    pub fn from(mut self, from: &str) -> Self {
208        self.didcomm_header.from = Some(String::from(from));
209        self
210    }
211
212    /// Getter of the `body` as String.
213    pub fn get_body(&self) -> Result<String> {
214        Ok(serde_json::to_string(&self.body)?)
215    }
216
217    /// `&DidCommHeader` getter.
218    pub fn get_didcomm_header(&self) -> &DidCommHeader {
219        &self.didcomm_header
220    }
221
222    /// `&JwmCommHeader` getter.
223    pub fn get_jwm_header(&self) -> &JwmHeader {
224        &self.jwm_header
225    }
226
227    /// If message `is_rotation()` true - returns from_prion claims.
228    /// Errors otherwise with `Error::NoRotationData`
229    pub fn get_prior(&self) -> Result<PriorClaims> {
230        if self.is_rotation() {
231            Ok(self
232                .didcomm_header
233                .from_prior()
234                .ok_or(Error::NoRotationData)?
235                .clone())
236        } else {
237            Err(Error::NoRotationData)
238        }
239    }
240
241    /// Checks if message is rotation one.
242    /// Exposed for explicit checks on calling code level.
243    pub fn is_rotation(&self) -> bool {
244        self.didcomm_header.from_prior().is_some()
245    }
246
247    /// Setter of `jwm_header`.
248    /// Replaces existing one with provided by consuming both values.
249    /// Returns modified instance of `Self`.
250    pub fn jwm_header(mut self, h: JwmHeader) -> Self {
251        self.jwm_header = h;
252        self
253    }
254
255    /// Setter of `m_type` @type header
256    pub fn m_type(mut self, m_type: &str) -> Self {
257        self.didcomm_header.m_type = m_type.into();
258        self
259    }
260
261    /// Setter of `typ` header property.
262    ///
263    /// # Parameters
264    ///
265    /// * `typ` - `MessageType` to be set for `typ` property
266    pub fn typ(mut self, typ: MessageType) -> Self {
267        self.jwm_header.typ = typ;
268        self
269    }
270
271    // Setter of the `kid` header
272    pub fn kid(mut self, kid: &str) -> Self {
273        match &mut self.jwm_header.kid {
274            Some(h) => *h = kid.into(),
275            None => {
276                self.jwm_header.kid = Some(kid.into());
277            }
278        }
279        self
280    }
281
282    /// Sets times of creation as now and, optional, expires time.
283    ///
284    /// # Arguments
285    ///
286    /// * `expires` - time in seconds since Unix Epoch when message is
287    ///               considered to be invalid.
288    pub fn timed(mut self, expires: Option<u64>) -> Self {
289        self.didcomm_header.expires_time = expires;
290        self.didcomm_header.created_time = crate::helpers::now_epoch_secs();
291        self
292    }
293
294    /// Setter of `to` header
295    pub fn to(mut self, to: &[&str]) -> Self {
296        for s in to {
297            self.didcomm_header.to.push(s.to_string());
298        }
299        while let Some(a) = self
300            .didcomm_header
301            .to
302            .iter()
303            .position(|e| e == &String::default())
304        {
305            self.didcomm_header.to.remove(a);
306        }
307        self
308    }
309
310    /// Setter of `didcomm_header`.
311    /// Replaces existing one with provided by consuming both values.
312    /// Returns modified instance of `Self`.
313    pub fn set_didcomm_header(mut self, h: DidCommHeader) -> Self {
314        self.didcomm_header = h;
315        self
316    }
317
318    /// Gets `Iterator` over key-value pairs of application level headers
319    pub fn get_application_params(&self) -> impl Iterator<Item = (&String, &String)> {
320        self.didcomm_header.other.iter()
321    }
322
323    /// Setter of `thid` header
324    pub fn thid(mut self, thid: &str) -> Self {
325        self.didcomm_header.thid = Some(thid.to_string());
326        self
327    }
328
329    /// Setter of `pthid` header
330    pub fn pthid(mut self, pthid: &str) -> Self {
331        self.didcomm_header.pthid = Some(pthid.to_string());
332        self
333    }
334}
335
336// Interactions with messages (sending, receiving, etc.)
337#[cfg(feature = "raw-crypto")]
338impl Message {
339    /// Serializes current state of the message into json.
340    /// Consumes original message - use as raw sealing of envelope.
341    pub fn as_raw_json(self) -> Result<String> {
342        Ok(serde_json::to_string(&self)?)
343    }
344
345    /// Presents IV and Payload to be externally encrypted and then sealed with `seal_pre_encrypted` method.
346    ///
347    /// # Returns
348    /// Tuple of bytes where .0 is IV and .1 is payload for encryption
349    ///
350    pub fn export_for_encryption(&self) -> Result<(Vec<u8>, Vec<u8>)> {
351        Ok((
352            decode(&Jwe::generate_iv())?,
353            serde_json::to_string(&self)?.as_bytes().to_vec(),
354        ))
355    }
356
357    /// Builds JWE from current message and it's pre-encrypted payload:
358    ///  `expert_for_encryption` should be used prior to this call and it's output
359    ///  provided as payload.
360    ///
361    /// # Parameters
362    /// `ciphertext` - encrypted output of `export_for_encryption` as JWE payload
363    ///
364    /// Returns serialized JSON JWE message, which is ready to be sent to receipent
365    ///
366    pub fn seal_pre_encrypted(self, cyphertext: impl AsRef<[u8]>) -> Result<String> {
367        let d_header = self.get_didcomm_header();
368
369        let mut unprotected = JwmHeader {
370            skid: d_header.from.clone(),
371            ..Default::default()
372        };
373
374        if self.recipients.is_none() {
375            unprotected.kid = Some(d_header.to[0].clone());
376        }
377
378        let jwe = Jwe::new(
379            Some(unprotected),
380            self.recipients.clone(),
381            cyphertext,
382            Some(self.jwm_header.clone()),
383            None::<&[u8]>,
384            None,
385        );
386
387        Ok(serde_json::to_string(&jwe)?)
388    }
389
390    /// Construct a message from received data.
391    /// Raw, JWS or JWE payload is accepted.
392    ///
393    /// # Arguments
394    ///
395    /// * `incoming` - serialized message as `Message`/`Jws`/`Jws`
396    ///
397    /// * `encryption_recipient_private_key` - recipients private key, used to decrypt `kek` in JWE
398    ///
399    /// * `encryption_sender_public_key` - senders public key, used to decrypt `kek` in JWE
400    ///
401    /// * `signing_sender_public_key` - senders public key, the JWS envelope was signed with
402    pub fn receive(
403        incoming: &str,
404        encryption_recipient_private_key: Option<&[u8]>,
405        encryption_sender_public_key: Option<Vec<u8>>,
406        signing_sender_public_key: Option<&[u8]>,
407    ) -> Result<Self> {
408        let mut current_message: String = incoming.to_string();
409
410        if get_message_type(&current_message)? == MessageType::DidCommJwe {
411            let recipient_private_key = encryption_recipient_private_key.ok_or_else(|| {
412                Error::Generic("missing encryption recipient private key".to_string())
413            })?;
414            current_message = receive_jwe(
415                &current_message,
416                recipient_private_key,
417                encryption_sender_public_key,
418            )?;
419        }
420
421        if get_message_type(&current_message)? == MessageType::DidCommJws {
422            current_message = receive_jws(&current_message, signing_sender_public_key)?;
423        }
424
425        Ok(serde_json::from_str(&current_message)?)
426    }
427
428    /// Wrap self to be mediated by some mediator.
429    /// Warning: Should be called on a `Message` instance which is ready to be sent!
430    /// If message is not properly set up for crypto - this method will propagate error from
431    ///     called `.seal()` method.
432    /// Takes one mediator at a time to make sure that mediated chain preserves unchanged.
433    /// This method can be chained any number of times to match all the mediators in the chain.
434    ///
435    /// # Arguments
436    ///
437    /// * `sender_private_key` - encryption key for inner message payload JWE encryption
438    ///
439    /// * `recipient_public_keys` - keys used to encrypt content encryption key for recipient;
440    ///                             can be provided if key should not be resolved via recipients DID
441    ///
442    /// * `mediator_did` - DID of message mediator, will be `to` of mediated envelope
443    ///
444    /// * `mediator_public_key` - key used to encrypt content encryption key for mediator;
445    ///                           can be provided if key should not be resolved via mediators DID
446    pub fn routed_by(
447        self,
448        sender_private_key: &[u8],
449        recipient_public_keys: Option<Vec<Option<Vec<u8>>>>,
450        mediator_did: &str,
451        mediator_public_key: Option<Vec<u8>>,
452    ) -> Result<String> {
453        let from = &self.didcomm_header.from.clone().unwrap_or_default();
454        let alg = get_crypter_from_header(&self.jwm_header)?;
455        let body = Mediated::new(self.didcomm_header.to[0].clone()).with_payload(
456            self.seal(sender_private_key, recipient_public_keys)?
457                .as_bytes()
458                .to_vec(),
459        );
460        Message::new()
461            .to(&[mediator_did])
462            .from(from)
463            .as_jwe(&alg, mediator_public_key.clone())
464            .typ(MessageType::DidCommForward)
465            .body(&serde_json::to_string(&body)?)?
466            .seal(sender_private_key, Some(vec![mediator_public_key]))
467    }
468
469    /// Seals (encrypts) self and returns ready to send JWE
470    ///
471    /// # Arguments
472    ///
473    /// * `sender_private_key` - encryption key for inner message payload JWE encryption
474    ///
475    /// * `recipient_public_keys` - keys used to encrypt content encryption key for recipient;
476    ///                             can be provided if key should not be resolved via recipients DID
477    pub fn seal(
478        mut self,
479        sender_private_key: impl AsRef<[u8]>,
480        recipient_public_keys: Option<Vec<Option<Vec<u8>>>>,
481    ) -> Result<String> {
482        if sender_private_key.as_ref().len() != 32 {
483            return Err(Error::InvalidKeySize("!32".into()));
484        }
485        let to_len = self.didcomm_header.to.len();
486        let public_keys = if let Some(recipient_public_keys_value) = recipient_public_keys {
487            if recipient_public_keys_value.len() != to_len {
488                return Err(Error::Generic(
489                    "`to` and `recipient_public_keys` must have same length".to_string(),
490                ));
491            }
492            recipient_public_keys_value
493        } else {
494            vec![None; to_len]
495        };
496
497        // generate content encryption key
498        let mut cek = [0u8; 32];
499        let mut rng = ChaCha20Rng::from_seed(Default::default());
500        rng.fill_bytes(&mut cek);
501        trace!("sealing message with shared_key: {:?}", &cek.as_ref());
502
503        if to_len == 0_usize {
504            return Err(Error::NoJweRecipient);
505        } else if self.serialize_flat_jwe && self.didcomm_header.to.len() > 1 {
506            return Err(Error::Generic(
507                "flat JWE serialization only supports a single `to`".to_string(),
508            ));
509        }
510
511        let mut recipients: Vec<Recipient> = vec![];
512        // create jwk from static secret per recipient
513        for (i, public_key) in public_keys.iter().enumerate().take(to_len) {
514            let rv = encrypt_cek(
515                &self,
516                sender_private_key.as_ref(),
517                &self.didcomm_header.to[i],
518                &cek,
519                public_key.to_owned(),
520            )?;
521            recipients.push(Recipient::new(rv.header, rv.encrypted_key));
522        }
523        self.recipients = Some(recipients);
524        // encrypt original message with static secret
525        let alg = get_crypter_from_header(&self.jwm_header)?;
526        self.encrypt(alg.encryptor(), cek.as_ref())
527    }
528}
529
530/// Associated functions implementations.
531/// Possibly not required as Jwe serialization covers this.
532impl Message {
533    /// Parses `iv` value as `Vec<u8>` from public header.
534    /// Both regular JSON and Compact representations are accepted.
535    /// Returns `Error` on failure.
536    /// TODO: Add examples
537    pub fn get_iv(received: &[u8]) -> Result<Vec<u8>> {
538        // parse from compact
539        let as_str = String::from_utf8(received.to_vec())?;
540        let json: serde_json::Value = if let Some(header_end) = as_str.find('.') {
541            serde_json::from_str(&String::from_utf8(base64_url::decode(
542                &as_str[..header_end],
543            )?)?)?
544        } else {
545            serde_json::from_str(&as_str)?
546        };
547        if let Some(iv) = json.get("iv") {
548            if let Some(t) = iv.as_str() {
549                if t.len() != 24 {
550                    Err(Error::Generic(format!(
551                        "IV [nonce] size is incorrect: {}",
552                        t.len()
553                    )))
554                } else {
555                    Ok(t.as_bytes().to_vec())
556                }
557            } else {
558                Err(Error::Generic("wrong nonce format".into()))
559            }
560        } else {
561            Err(Error::Generic("iv is not found in JOSE header".into()))
562        }
563    }
564
565    /// Transforms incomming into `Jwe` if it is one
566    /// Also checks if `skid` field is present or returns `None` othervise
567    /// Key resolution and validation fall onto caller of this method
568    ///
569    /// # Parameters
570    ///
571    /// * `incomming` - incomming message
572    ///
573    /// Returns `Option<Jwe>` where `.header.skid` is skid and `.payload()` is cyphertext
574    ///
575    #[cfg(feature = "raw-crypto")]
576    pub fn received_as_jwe(incomming: impl AsRef<[u8]>) -> Option<Jwe> {
577        if let Ok(jwe) = serde_json::from_slice::<Jwe>(incomming.as_ref()) {
578            if jwe.get_skid().is_some() {
579                Some(jwe)
580            } else {
581                None
582            }
583        } else {
584            None
585        }
586    }
587    /// Transforms decrypted `Jwe` into `Message`
588    ///
589    /// # Parameters
590    ///
591    /// * `decrypted` - result of decrypting of Jwe payload retreived after
592    ///  decrypting content of `as_jwe` function call output.
593    ///
594    pub fn receive_external_crypto(decrypted: impl AsRef<[u8]>) -> Result<Self> {
595        Ok(serde_json::from_slice(decrypted.as_ref())?)
596    }
597
598    /// Signs raw message and then packs it to encrypted envelope
599    /// [Spec](https://identity.foundation/didcomm-messaging/spec/#message-signing)
600    ///
601    /// # Arguments
602    ///
603    /// * `encryption_sender_private_key` - encryption key for inner message payload JWE encryption
604    ///
605    /// * `encryption_recipient_public_keys` - keys used to encrypt content encryption key for
606    ///                                        recipient with; can be provided if key should not be
607    ///                                        resolved via recipients DID
608    ///
609    /// * `signing_algorithm` - encryption algorithm used
610    ///
611    /// * `signing_sender_private_key` - signing key for enveloped message JWS encryption
612    #[cfg(feature = "raw-crypto")]
613    pub fn seal_signed(
614        self,
615        encryption_sender_private_key: &[u8],
616        encryption_recipient_public_keys: Option<Vec<Option<Vec<u8>>>>,
617        signing_algorithm: SignatureAlgorithm,
618        signing_sender_private_key: &[u8],
619    ) -> Result<String> {
620        let mut to = self.clone();
621        let signed = self
622            .as_jws(&signing_algorithm)
623            .sign(signing_algorithm.signer(), signing_sender_private_key)?;
624        to.body = serde_json::from_str(&signed)?;
625        to.typ(MessageType::DidCommJws).seal(
626            encryption_sender_private_key,
627            encryption_recipient_public_keys,
628        )
629    }
630}
631
632impl Default for Message {
633    fn default() -> Self {
634        Self::new()
635    }
636}
637
638#[cfg(test)]
639mod parse_tests {
640    use super::*;
641
642    #[test]
643    fn iv_from_json_test() {
644        // Arrange
645        // Example JWM from RFC: https://tools.ietf.org/html/draft-looker-jwm-01#section-2.3
646        // Extendet twice to be 192bit (24byte) nonce.
647        let raw_json = r#" { "protected": "eyJ0eXAiOiJKV00iLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiUEdvWHpzME5XYVJfbWVLZ1RaTGJFdURvU1ZUYUZ1eXJiV0k3VjlkcGpDZyIsImFsZyI6IkVDREgtRVMrQTI1NktXIiwiZXBrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiLU5oN1NoUkJfeGFDQlpSZElpVkN1bDNTb1IwWXc0VEdFUXFxR2lqMXZKcyIsInkiOiI5dEx4ODFQTWZRa3JPdzh5dUkyWXdJMG83TXROemFDR2ZDQmJaQlc1WXJNIn19",
648                "recipients": [
649                  {
650                    "encrypted_key": "J1Fs9JaDjOT_5481ORQWfEZmHy7OjE3pTNKccnK7hlqjxbPalQWWLg"
651                  }
652                ],
653                "iv": "u5kIzo0m_d2PjI4mu5kIzo0m",
654                "ciphertext": "qGuFFoHy7HBmkf2BaY6eREwzEjn6O_FnRoXj2H-DAXo1PgQdfON-_1QbxtnT8e8z_M6Gown7s8fLtYNmIHAuixqFQnSA4fdMcMSi02z1MYEn2JC-1EkVbWr4TqQgFP1EyymB6XjCWDiwTYd2xpKoUshu8WW601HLSgFIRUG3-cK_ZSdFaoWosIgAH5EQ2ayJkRB_7dXuo9Bi1MK6TYGZKezc6rpCK_VRSnLXhFwa1C3T0QBes",
655                "tag": "doeAoagwJe9BwKayfcduiw"
656            }"#;
657        // Act
658        let iv = Message::get_iv(raw_json.as_bytes());
659        // Assert
660        assert!(iv.is_ok());
661        assert_eq!(
662            "u5kIzo0m_d2PjI4mu5kIzo0m",
663            &String::from_utf8(iv.unwrap()).unwrap()
664        );
665    }
666
667    #[test]
668    fn iv_from_compact_json_test() {
669        // Arrange
670        // Example JWM from RFC: https://tools.ietf.org/html/draft-looker-jwm-01#section-2.3
671        let compact = r#"eyJ0eXAiOiJKV00iLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiUEdvWHpzME5XYVJfbWVLZ1RaTGJFdURvU1ZUYUZ1eXJiV0k3VjlkcGpDZyIsImFsZyI6IkVDREgtRVMrQTI1NktXIiwiaXYiOiAidTVrSXpvMG1fZDJQakk0bXU1a0l6bzBtIn0."#;
672        // Act
673        let iv = Message::get_iv(compact.as_bytes());
674        // Assert
675        assert!(iv.is_ok());
676        assert_eq!(
677            "u5kIzo0m_d2PjI4mu5kIzo0m",
678            &String::from_utf8(iv.unwrap()).unwrap()
679        );
680    }
681}
682
683#[cfg(all(test, feature = "raw-crypto"))]
684mod crypto_tests {
685    extern crate chacha20poly1305;
686    extern crate sodiumoxide;
687
688    #[cfg(feature = "resolve")]
689    use base58::FromBase58;
690    use rand_core::OsRng;
691    use utilities::{get_keypair_set, KeyPairSet};
692
693    use super::*;
694    #[cfg(feature = "resolve")]
695    use crate::{Jwe, Mediated};
696
697    #[test]
698    #[cfg(not(feature = "resolve"))]
699    fn create_and_send() {
700        let KeyPairSet {
701            alice_private,
702            bobs_public,
703            ..
704        } = get_keypair_set();
705        let m = Message::new().as_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()));
706        let p = m.seal(&alice_private, Some(vec![Some(bobs_public.to_vec())]));
707        assert!(p.is_ok());
708    }
709
710    #[test]
711    #[cfg(feature = "resolve")]
712    fn create_and_send() {
713        let KeyPairSet { alice_private, .. } = get_keypair_set();
714        let m = Message::new()
715            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
716            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
717            .as_jwe(&CryptoAlgorithm::XC20P, None);
718        let p = m.seal(&alice_private, None);
719        assert!(p.is_ok());
720    }
721
722    #[test]
723    fn create_and_send_without_resolving_dids() {
724        let KeyPairSet {
725            alice_private,
726            bobs_public,
727            ..
728        } = get_keypair_set();
729        let m = Message::new().as_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()));
730        let p = m.seal(&alice_private, Some(vec![Some(bobs_public.to_vec())]));
731        assert!(p.is_ok());
732    }
733
734    #[test]
735    #[cfg(feature = "resolve")]
736    fn receive_test() {
737        // Arrange
738        let KeyPairSet {
739            alice_public,
740            alice_private,
741            bobs_private,
742            ..
743        } = get_keypair_set();
744        // alice seals JWE
745        let m = Message::new()
746            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
747            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
748            .as_jwe(&CryptoAlgorithm::XC20P, None);
749        let jwe = m.seal(&alice_private, None).unwrap();
750
751        // Act
752        // bob receives JWE
753        let received =
754            Message::receive(&jwe, Some(&bobs_private), Some(alice_public.to_vec()), None);
755
756        // Assert
757        assert!(received.is_ok());
758    }
759
760    #[test]
761    fn receive_test_without_resolving_dids() {
762        // Arrange
763        let KeyPairSet {
764            alice_public,
765            alice_private,
766            bobs_private,
767            bobs_public,
768            ..
769        } = get_keypair_set();
770        // alice seals JWE
771        let m = Message::new()
772            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
773            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
774            .as_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()));
775        let jwe = m
776            .seal(&alice_private, Some(vec![Some(bobs_public.to_vec())]))
777            .unwrap();
778
779        // Act
780        // bob receives JWE
781        let received =
782            Message::receive(&jwe, Some(&bobs_private), Some(alice_public.to_vec()), None);
783
784        // Assert
785        assert!(received.is_ok());
786    }
787
788    #[test]
789    #[cfg(feature = "resolve")]
790    fn send_receive_didkey_test() {
791        let m = Message::new()
792            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
793            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
794            .as_jwe(&CryptoAlgorithm::XC20P, None);
795        // TODO: validate derived pub from priv key <<<
796        let KeyPairSet {
797            alice_private,
798            bobs_private,
799            ..
800        } = get_keypair_set();
801        let jwe = m.seal(&alice_private, None);
802        assert!(jwe.is_ok());
803
804        let received = Message::receive(&jwe.unwrap(), Some(&bobs_private), None, None);
805        assert!(received.is_ok());
806    }
807
808    #[test]
809    fn send_receive_didkey_explicit_pubkey_test() {
810        let KeyPairSet {
811            alice_public,
812            alice_private,
813            bobs_private,
814            bobs_public,
815            ..
816        } = get_keypair_set();
817        let m = Message::new()
818            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
819            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
820            .as_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()));
821
822        let jwe = m.seal(&alice_private, Some(vec![Some(bobs_public.to_vec())]));
823        assert!(jwe.is_ok());
824
825        let received = Message::receive(
826            &jwe.unwrap(),
827            Some(&bobs_private),
828            Some(alice_public.to_vec()),
829            None,
830        );
831        assert!(received.is_ok());
832    }
833
834    #[test]
835    #[cfg(feature = "resolve")]
836    fn send_receive_didkey_test_1pu_aes256() {
837        let m = Message::new()
838            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
839            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
840            .as_jwe(&CryptoAlgorithm::A256GCM, None);
841        // TODO: validate derived pub from priv key <<<
842        let KeyPairSet {
843            alice_private,
844            bobs_private,
845            ..
846        } = get_keypair_set();
847        let jwe = m.seal(&alice_private, None);
848        assert!(jwe.is_ok());
849
850        let received = Message::receive(&jwe.unwrap(), Some(&bobs_private), None, None);
851        assert!(received.is_ok());
852    }
853
854    #[test]
855    #[cfg(feature = "resolve")]
856    fn send_receive_didkey_test_1pu_aes256_explicit_pubkey() {
857        let m = Message::new()
858            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
859            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
860            .as_jwe(&CryptoAlgorithm::A256GCM, None);
861        // TODO: validate derived pub from priv key <<<
862        let KeyPairSet {
863            alice_private,
864            bobs_private,
865            ..
866        } = get_keypair_set();
867        let jwe = m.seal(&alice_private, None);
868        assert!(jwe.is_ok());
869
870        let KeyPairSet { alice_public, .. } = get_keypair_set();
871
872        let received = Message::receive(
873            &jwe.unwrap(),
874            Some(&bobs_private),
875            Some(alice_public.to_vec()),
876            None,
877        );
878        assert!(received.is_ok());
879    }
880
881    #[test]
882    #[cfg(feature = "resolve")]
883    fn send_receive_didkey_multiple_recipients_test() {
884        let m = Message::new()
885            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
886            .to(&[
887                "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
888                "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
889            ])
890            .as_jwe(&CryptoAlgorithm::XC20P, None);
891        let KeyPairSet {
892            alice_private,
893            bobs_private,
894            ..
895        } = get_keypair_set();
896        let third_private = "ACa4PPJ1LnPNq1iwS33V3Akh7WtnC71WkKFZ9ccM6sX2"
897            .from_base58()
898            .unwrap();
899        let jwe = m.seal(&alice_private, None);
900        assert!(jwe.is_ok());
901
902        let jwe = jwe.unwrap();
903        let received_bob = Message::receive(&jwe, Some(&bobs_private), None, None);
904        let received_third = Message::receive(&jwe, Some(&third_private), None, None);
905        assert!(received_bob.is_ok());
906        assert!(received_third.is_ok());
907    }
908
909    #[test]
910    #[cfg(feature = "resolve")]
911    fn mediated_didkey_test() {
912        let mediator_private = "ACa4PPJ1LnPNq1iwS33V3Akh7WtnC71WkKFZ9ccM6sX2"
913            .from_base58()
914            .unwrap();
915        let KeyPairSet {
916            alice_private,
917            bobs_private,
918            ..
919        } = get_keypair_set();
920        let sealed = Message::new()
921            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
922            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
923            .as_jwe(&CryptoAlgorithm::XC20P, None)
924            .routed_by(
925                &alice_private,
926                None,
927                "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
928                None,
929            );
930        assert!(sealed.is_ok());
931
932        let mediator_received =
933            Message::receive(&sealed.unwrap(), Some(&mediator_private), None, None);
934        assert!(mediator_received.is_ok());
935
936        let mediator_received_unwrapped = mediator_received.unwrap().get_body().unwrap();
937        let pl_string = String::from_utf8_lossy(mediator_received_unwrapped.as_ref());
938        let message_to_forward: Mediated = serde_json::from_str(&pl_string).unwrap();
939        let attached_jwe = serde_json::from_slice::<Jwe>(&message_to_forward.payload);
940        assert!(attached_jwe.is_ok());
941        let str_jwe = serde_json::to_string(&attached_jwe.unwrap());
942        assert!(str_jwe.is_ok());
943
944        let bob_received = Message::receive(
945            &String::from_utf8_lossy(&message_to_forward.payload),
946            Some(&bobs_private),
947            None,
948            None,
949        );
950        assert!(bob_received.is_ok());
951    }
952
953    #[test]
954    fn can_pass_explicit_signing_verification_keys() -> Result<()> {
955        let KeyPairSet {
956            alice_private,
957            alice_public,
958            bobs_private,
959            bobs_public,
960            ..
961        } = get_keypair_set();
962        let sign_keypair = ed25519_dalek::SigningKey::generate(&mut OsRng);
963        let body = r#"{"foo":"bar"}"#;
964        let message = Message::new()
965            .from("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
966            .to(&["did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"])
967            .body(body)? // packing in some payload
968            .as_flat_jwe(&CryptoAlgorithm::XC20P, Some(bobs_public.to_vec()))
969            .kid(&hex::encode(vec![1; 32])); // invalid key, passing no key will not succeed
970
971        let jwe_string = message.seal_signed(
972            &alice_private,
973            Some(vec![Some(bobs_public.to_vec())]),
974            SignatureAlgorithm::EdDsa,
975            &sign_keypair.to_bytes(),
976        )?;
977
978        let received_failure_no_key = Message::receive(
979            &jwe_string,
980            Some(&bobs_private),
981            Some(alice_public.to_vec()),
982            None,
983        );
984        let received_failure_wrong_key = Message::receive(
985            &jwe_string,
986            Some(&bobs_private),
987            Some(alice_public.to_vec()),
988            Some(&[0; 32]),
989        );
990        let received_success = Message::receive(
991            &jwe_string,
992            Some(&bobs_private),
993            Some(alice_public.to_vec()),
994            Some(&sign_keypair.verifying_key().to_bytes()),
995        );
996
997        // Assert
998        assert!(&received_failure_no_key.is_err());
999        assert!(&received_failure_wrong_key.is_err());
1000        assert!(&received_success.is_ok());
1001        let received = received_success.unwrap();
1002        let sample_body: Value = serde_json::from_str(body).unwrap();
1003        let received_body: Value = serde_json::from_str(&received.get_body().unwrap()).unwrap();
1004        assert_eq!(sample_body.to_string(), received_body.to_string(),);
1005
1006        Ok(())
1007    }
1008}