1use std::convert::TryInto;
2
3use base64_url::{decode, encode};
4use serde_json::Value;
5
6use super::Message;
7use crate::{
8 crypto::{SignatureAlgorithm, Signer, SigningMethod, SymmetricCypherMethod},
9 Error,
10 Jwe,
11 JwmHeader,
12 Jws,
13 MessageType,
14 Signature,
15};
16
17#[cfg(feature = "raw-crypto")]
19impl Message {
20 pub fn encrypt(self, crypter: SymmetricCypherMethod, cek: &[u8]) -> Result<String, Error> {
33 let mut jwe_header = self.jwm_header.clone();
34 if jwe_header.typ != MessageType::DidCommForward {
35 jwe_header.typ = MessageType::DidCommJwe;
36 }
37 let d_header = self.get_didcomm_header();
38 let iv = Jwe::generate_iv();
39 let multi = self.recipients.is_some();
40 jwe_header.skid = Some(d_header.from.clone().unwrap_or_default());
41 if !multi {
42 jwe_header.kid = Some(d_header.to[0].clone());
43 }
44 jwe_header.skid = d_header.from.clone();
45 let aad_string = encode(&serde_json::to_string(&jwe_header)?.as_bytes());
46 let aad = aad_string.as_bytes();
47 let ciphertext_and_tag = crypter(
48 &decode(&iv)?,
49 cek,
50 serde_json::to_string(&self)?.as_bytes(),
51 aad,
52 )?;
53 let (ciphertext, tag) = ciphertext_and_tag.split_at(ciphertext_and_tag.len() - 16);
54 let jwe = if self.serialize_flat_jwe {
55 let recipients = self.recipients.ok_or_else(|| {
56 Error::Generic("flat JWE JSON serialization needs a recipient".to_string())
57 })?;
58 if recipients.len() != 1 {
59 return Err(Error::Generic(
60 "flat JWE JSON serialization needs exactly one recipient".to_string(),
61 ));
62 }
63
64 Jwe::new_flat(
65 None,
66 recipients[0].clone(),
67 ciphertext,
68 Some(jwe_header),
69 Some(tag),
70 Some(iv),
71 )
72 } else {
73 Jwe::new(
74 None,
75 self.recipients.clone(),
76 ciphertext,
77 Some(jwe_header),
78 Some(tag),
79 Some(iv),
80 )
81 };
82 Ok(serde_json::to_string(&jwe)?)
83 }
84
85 pub fn decrypt(
98 received_message: &[u8],
99 decrypter: SymmetricCypherMethod,
100 cek: &[u8],
101 ) -> Result<Self, Error> {
102 let jwe: Jwe = serde_json::from_slice(received_message)?;
103 let protected = jwe
104 .protected
105 .as_ref()
106 .ok_or_else(|| Error::Generic("jwe is missing protected header".to_string()))?;
107 let aad_string = encode(&serde_json::to_string(&protected)?.as_bytes());
108 let aad = aad_string.as_bytes();
109 let tag = jwe
110 .tag
111 .as_ref()
112 .ok_or("JWE is missing tag")
113 .map_err(|e| Error::Generic(e.to_string()))?;
114 let mut ciphertext_and_tag: Vec<u8> = vec![];
115 ciphertext_and_tag.extend(&jwe.get_payload());
116 ciphertext_and_tag.extend(&decode(&tag)?);
117
118 return match decrypter(jwe.get_iv().as_ref(), cek, &ciphertext_and_tag, aad) {
119 Ok(raw_message_bytes) => Ok(serde_json::from_slice(&raw_message_bytes)?),
120 Err(e) => {
121 error!("decryption failed; {}", &e);
122 Err(Error::PlugCryptoFailure)
123 }
124 };
125 }
126
127 pub fn sign(
131 mut self,
132 signer: SigningMethod,
133 signing_sender_private_key: &[u8],
134 ) -> Result<String, Error> {
135 let mut jws_header = self.jwm_header.clone();
136 jws_header.typ = MessageType::DidCommJws;
137 if jws_header.alg.is_none() {
138 return Err(Error::JwsParseError);
139 }
140
141 self.jwm_header = JwmHeader::default();
143
144 let jws_header_string_base64 = base64_url::encode(&serde_json::to_string(&jws_header)?);
145 let payload_json_string = serde_json::to_string(&self)?;
146 let payload_string_base64 = base64_url::encode(&payload_json_string);
147 let payload_to_sign = format!("{}.{}", &jws_header_string_base64, &payload_string_base64);
148 let signature = signer(signing_sender_private_key, payload_to_sign.as_bytes())?;
149 let signature_value = Signature::new(Some(jws_header), None, signature);
150
151 let jws: Jws = if self.serialize_flat_jws {
152 Jws::new_flat(payload_string_base64, signature_value)
153 } else {
154 let signature_values = self
155 .didcomm_header
156 .to
157 .iter()
158 .map(|_| signature_value.clone())
159 .collect();
160 Jws::new(payload_string_base64, signature_values)
161 };
162
163 Ok(serde_json::to_string(&jws)?)
164 }
165
166 pub fn verify(jws: &[u8], signing_sender_public_key: &[u8]) -> Result<Message, Error> {
170 let jws: Jws = serde_json::from_slice(jws)?;
171
172 let signatures_values_to_verify: Vec<Signature>;
173 if let Some(signatures) = &jws.signatures {
174 signatures_values_to_verify = signatures.clone();
175 } else if let Some(signature_value) = &jws.signature {
176 signatures_values_to_verify = vec![signature_value.clone()];
177 } else {
178 return Err(Error::JwsParseError);
179 }
180 let payload = &jws.payload;
181
182 let mut verified = false;
183 for signature_value in signatures_values_to_verify {
184 let alg = &signature_value.get_alg().ok_or(Error::JweParseError)?;
185 let signature = &signature_value.signature[..];
186 let verifier: SignatureAlgorithm = alg.try_into()?;
187 let protected_header = signature_value
188 .protected
189 .as_ref()
190 .ok_or(Error::JwsParseError)?;
191 let encoded_header = base64_url::encode(&serde_json::to_string(&protected_header)?);
192 let payload_to_verify = format!("{}.{}", &encoded_header, &payload);
193 if verifier.validator()(
194 signing_sender_public_key,
195 payload_to_verify.as_bytes(),
196 signature,
197 )? {
198 verified = true;
199 break;
200 }
201 }
202
203 if verified {
204 let message: Message = serde_json::from_slice(&base64_url::decode(&jws.payload)?)?;
206 Ok(message)
207 } else {
208 Err(Error::JwsParseError)
209 }
210 }
211
212 pub fn verify_value(jws: &Value, signing_sender_public_key: &[u8]) -> Result<Message, Error> {
222 let jws_string = serde_json::to_string(jws)?;
223 Message::verify(&jws_string.into_bytes(), signing_sender_public_key)
224 }
225}
226
227#[cfg(test)]
228mod raw_tests {
229 use chacha20poly1305::{
230 aead::{Aead, KeyInit},
231 Key,
232 XChaCha20Poly1305,
233 XNonce,
234 };
235 use sodiumoxide::crypto::secretbox;
236 use x25519_dalek::{EphemeralSecret, PublicKey};
237
238 use super::{Error, Message};
239 use crate::crypto::CryptoAlgorithm;
240
241 #[test]
242 #[allow(non_snake_case)]
243 #[cfg(feature = "raw-crypto")]
244 fn plugin_crypto_xChaCha20Paly1305_dummy_key() {
245 let key = Key::from_slice(b"an example very very secret key.");
247 let my_crypter = Box::new(
249 |n: &[u8], k: &[u8], m: &[u8], _a: &[u8]| -> Result<Vec<u8>, Error> {
250 let aead = XChaCha20Poly1305::new(k.into());
251 let nonce = XNonce::from_slice(n);
252 aead.encrypt(nonce, m)
253 .map_err(|e| Error::Generic(e.to_string()))
254 },
255 );
256 let my_decrypter = Box::new(
258 |n: &[u8], k: &[u8], m: &[u8], _a: &[u8]| -> Result<Vec<u8>, Error> {
259 let aead = XChaCha20Poly1305::new(k.into());
260 let nonce = XNonce::from_slice(n);
261 Ok(aead.decrypt(nonce, m).unwrap())
262 },
263 );
264 let m = Message::new().as_jwe(&CryptoAlgorithm::A256GCM, None);
265 let id = m.get_didcomm_header().id.to_owned();
266
267 let encrypted = m.encrypt(my_crypter, key);
269 assert!(&encrypted.is_ok()); let raw_m = Message::decrypt(encrypted.unwrap().as_bytes(), my_decrypter, key);
271 assert!(&raw_m.is_ok()); assert_eq!(id, raw_m.unwrap().get_didcomm_header().id); }
274
275 #[test]
276 #[cfg(feature = "raw-crypto")]
277 fn plugin_crypto_libsodium_box() {
278 let my_crypter = Box::new(
281 |n: &[u8], k: &[u8], m: &[u8], _a: &[u8]| -> Result<Vec<u8>, Error> {
282 let nonce = secretbox::Nonce::from_slice(n).unwrap();
283 Ok(secretbox::seal(
284 m,
285 &nonce,
286 &secretbox::Key::from_slice(k).unwrap(),
287 ))
288 },
289 );
290 let my_decrypter = Box::new(
292 |n: &[u8], k: &[u8], m: &[u8], _a: &[u8]| -> Result<Vec<u8>, Error> {
293 let nonce = secretbox::Nonce::from_slice(n).unwrap();
294 Ok(secretbox::open(m, &nonce, &secretbox::Key::from_slice(k).unwrap()).unwrap())
295 },
296 );
297 let m = Message::new().as_jwe(&CryptoAlgorithm::A256GCM, None);
298 let id = m.get_didcomm_header().id.to_owned();
299 let key = secretbox::gen_key();
300
301 let encrypted = m.encrypt(my_crypter, key.as_ref());
303 assert!(&encrypted.is_ok()); let encrypted = encrypted.unwrap();
305 println!("{}", &encrypted);
306 let raw_m = Message::decrypt(encrypted.as_bytes(), my_decrypter, key.as_ref());
307 assert!(&raw_m.is_ok()); assert_eq!(id, raw_m.unwrap().get_didcomm_header().id); }
310
311 #[test]
312 #[allow(non_snake_case)]
313 #[cfg(feature = "raw-crypto")]
314 fn plugin_crypto_xChaCha20Paly1305_x25519_dalek_shared_secret() {
315 let sender_sk = EphemeralSecret::random_from_rng(rand_core::OsRng);
317 let sender_pk = PublicKey::from(&sender_sk);
318 let recipient_sk = EphemeralSecret::random_from_rng(rand_core::OsRng);
319 let recipient_pk = PublicKey::from(&recipient_sk);
320 let sender_shared = sender_sk.diffie_hellman(&recipient_pk);
321 let recipient_shared = recipient_sk.diffie_hellman(&sender_pk);
322 let m = Message::new().as_jwe(&CryptoAlgorithm::XC20P, None);
323 let id = m.get_didcomm_header().id.to_owned();
324 let my_crypter = Box::new(
326 |n: &[u8], k: &[u8], m: &[u8], _a: &[u8]| -> Result<Vec<u8>, Error> {
327 let aead = XChaCha20Poly1305::new(k.into());
328 let nonce = XNonce::from_slice(n);
329 aead.encrypt(nonce, m)
330 .map_err(|e| Error::Generic(e.to_string()))
331 },
332 );
333 let my_decrypter = Box::new(
335 |n: &[u8], k: &[u8], m: &[u8], _a: &[u8]| -> Result<Vec<u8>, Error> {
336 let aead = XChaCha20Poly1305::new(k.into());
337 let nonce = XNonce::from_slice(n);
338 aead.decrypt(nonce, m)
339 .map_err(|e| Error::Generic(e.to_string()))
340 },
341 );
342
343 let encrypted = m.encrypt(my_crypter, sender_shared.as_bytes());
345 assert!(&encrypted.is_ok()); let raw_m = Message::decrypt(
347 encrypted.unwrap().as_bytes(),
348 my_decrypter,
349 recipient_shared.as_bytes(),
350 );
351 assert!(&raw_m.is_ok()); assert_eq!(id, raw_m.unwrap().get_didcomm_header().id); }
354}