1#![cfg_attr(docsrs, doc(cfg(feature = "v2")))]
2
3use crate::common::{encode_b64, validate_footer_untrusted_token};
4use crate::errors::Error;
5use crate::keys::{
6 AsymmetricKeyPair, AsymmetricPublicKey, AsymmetricSecretKey, Generate, SymmetricKey,
7};
8use crate::pae;
9use crate::token::{Local, Public, TrustedToken, UntrustedToken};
10use crate::version::private::Version;
11use alloc::string::String;
12use alloc::vec::Vec;
13use core::convert::TryFrom;
14use core::marker::PhantomData;
15use ed25519_compact::{KeyPair, PublicKey, SecretKey as SigningKey, Seed, Signature};
16use orion::hazardous::aead::xchacha20poly1305::*;
17use orion::hazardous::mac::blake2b;
18use orion::hazardous::mac::poly1305::POLY1305_OUTSIZE;
19use orion::hazardous::stream::xchacha20::XCHACHA_NONCESIZE;
20use subtle::ConstantTimeEq;
21
22#[derive(Debug, PartialEq, Eq, Clone)]
23pub struct V2;
25
26impl Version for V2 {
27 const LOCAL_KEY: usize = 32;
28 const SECRET_KEY: usize = 32 + Self::PUBLIC_KEY; const PUBLIC_KEY: usize = 32;
30 const PUBLIC_SIG: usize = 64;
31 const LOCAL_NONCE: usize = 24;
32 const LOCAL_TAG: usize = 16;
33 const PUBLIC_HEADER: &'static str = "v2.public.";
34 const LOCAL_HEADER: &'static str = "v2.local.";
35 #[cfg(feature = "paserk")]
36 const PASERK_ID: usize = 44;
37
38 fn validate_local_key(key_bytes: &[u8]) -> Result<(), Error> {
39 if key_bytes.len() != Self::LOCAL_KEY {
40 return Err(Error::Key);
41 }
42
43 Ok(())
44 }
45
46 fn validate_secret_key(key_bytes: &[u8]) -> Result<(), Error> {
47 if key_bytes.len() != Self::SECRET_KEY {
48 return Err(Error::Key);
49 }
50
51 let seed = Seed::from_slice(&key_bytes[..32]).map_err(|_| Error::Key)?;
52 let kp = KeyPair::from_seed(seed);
53
54 if !bool::from(kp.pk.as_slice().ct_eq(&key_bytes[32..])) {
55 return Err(Error::Key);
56 }
57
58 Ok(())
59 }
60
61 fn validate_public_key(key_bytes: &[u8]) -> Result<(), Error> {
62 if key_bytes.len() != Self::PUBLIC_KEY {
63 return Err(Error::Key);
64 }
65
66 Ok(())
67 }
68}
69
70impl TryFrom<&AsymmetricSecretKey<V2>> for AsymmetricPublicKey<V2> {
71 type Error = Error;
72
73 fn try_from(value: &AsymmetricSecretKey<V2>) -> Result<Self, Self::Error> {
74 AsymmetricPublicKey::<V2>::from(&value.as_bytes()[32..])
75 }
76}
77
78impl Generate<AsymmetricKeyPair<V2>, V2> for AsymmetricKeyPair<V2> {
79 fn generate() -> Result<AsymmetricKeyPair<V2>, Error> {
80 let key_pair = KeyPair::generate();
81
82 let secret = AsymmetricSecretKey::<V2>::from(key_pair.sk.as_ref())
83 .map_err(|_| Error::KeyGeneration)?;
84 let public = AsymmetricPublicKey::<V2>::from(key_pair.pk.as_ref())
85 .map_err(|_| Error::KeyGeneration)?;
86
87 Ok(Self { public, secret })
88 }
89}
90
91impl Generate<SymmetricKey<V2>, V2> for SymmetricKey<V2> {
92 fn generate() -> Result<SymmetricKey<V2>, Error> {
93 let mut rng_bytes = vec![0u8; V2::LOCAL_KEY];
94 V2::validate_local_key(&rng_bytes)?;
95 getrandom::fill(&mut rng_bytes)?;
96
97 Ok(Self {
98 bytes: rng_bytes,
99 phantom: PhantomData,
100 })
101 }
102}
103
104pub struct PublicToken;
106
107impl PublicToken {
108 pub const HEADER: &'static str = "v2.public.";
110
111 pub fn sign(
113 secret_key: &AsymmetricSecretKey<V2>,
114 message: &[u8],
115 footer: Option<&[u8]>,
116 ) -> Result<String, Error> {
117 if message.is_empty() {
118 return Err(Error::EmptyPayload);
119 }
120
121 let sk = SigningKey::from_slice(secret_key.as_bytes()).map_err(|_| Error::Key)?;
122 let f = footer.unwrap_or(&[]);
123 let m2 = pae::pae(&[Self::HEADER.as_bytes(), message, f])?;
124 let sig = sk.sign(m2, None);
125
126 let mut m_sig: Vec<u8> = Vec::from(message);
127 m_sig.extend_from_slice(sig.as_ref());
128
129 let token_no_footer = format!("{}{}", Self::HEADER, encode_b64(m_sig)?);
130
131 if f.is_empty() {
132 Ok(token_no_footer)
133 } else {
134 Ok(format!("{}.{}", token_no_footer, encode_b64(f)?))
135 }
136 }
137
138 pub fn verify(
143 public_key: &AsymmetricPublicKey<V2>,
144 token: &UntrustedToken<Public, V2>,
145 footer: Option<&[u8]>,
146 ) -> Result<TrustedToken, Error> {
147 validate_footer_untrusted_token(token, footer)?;
148
149 let f = token.untrusted_footer();
150 let sm = token.untrusted_message();
151 let m = token.untrusted_payload();
152 let s = sm[m.len()..m.len() + V2::PUBLIC_SIG].as_ref();
153
154 let m2 = pae::pae(&[Self::HEADER.as_bytes(), m, f])?;
155 let pk: PublicKey = PublicKey::from_slice(public_key.as_bytes()).map_err(|_| Error::Key)?;
156
157 debug_assert!(s.len() == V2::PUBLIC_SIG);
158 let sig = Signature::from_slice(s).map_err(|_| Error::TokenValidation)?;
160
161 if pk.verify(m2, &sig).is_ok() {
162 TrustedToken::_new(Self::HEADER, m, f, &[])
163 } else {
164 Err(Error::TokenValidation)
165 }
166 }
167}
168
169pub struct LocalToken;
171
172impl LocalToken {
173 pub const HEADER: &'static str = "v2.local.";
175
176 pub(crate) fn encrypt_with_derived_nonce(
179 secret_key: &SymmetricKey<V2>,
180 nonce_key_bytes: &[u8],
181 message: &[u8],
182 footer: Option<&[u8]>,
183 ) -> Result<String, Error> {
184 debug_assert!(nonce_key_bytes.len() == XCHACHA_NONCESIZE);
185
186 let nonce_key = blake2b::SecretKey::from_slice(nonce_key_bytes).unwrap();
188 let mut blake2b = blake2b::Blake2b::new(&nonce_key, XCHACHA_NONCESIZE).unwrap();
189 blake2b.update(message.as_ref()).unwrap();
190 let nonce = Nonce::from_slice(blake2b.finalize().unwrap().unprotected_as_bytes()).unwrap();
191
192 let f = footer.unwrap_or(&[]);
193
194 let pre_auth = pae::pae(&[Self::HEADER.as_bytes(), nonce.as_ref(), f])?;
195 let mut out = vec![0u8; message.len() + POLY1305_OUTSIZE + nonce.len()];
196 let sk = match SecretKey::from_slice(secret_key.as_bytes()) {
197 Ok(val) => val,
198 Err(orion::errors::UnknownCryptoError) => return Err(Error::Key),
199 };
200
201 match seal(
202 &sk,
203 &nonce,
204 message,
205 Some(&pre_auth),
206 &mut out[nonce.len()..],
207 ) {
208 Ok(()) => (),
209 Err(orion::errors::UnknownCryptoError) => return Err(Error::Encryption),
210 }
211
212 out[..nonce.len()].copy_from_slice(nonce.as_ref());
213 let token_no_footer = format!("{}{}", Self::HEADER, encode_b64(out)?);
214
215 if f.is_empty() {
216 Ok(token_no_footer)
217 } else {
218 Ok(format!("{}.{}", token_no_footer, encode_b64(f)?))
219 }
220 }
221
222 pub fn encrypt(
224 secret_key: &SymmetricKey<V2>,
225 message: &[u8],
226 footer: Option<&[u8]>,
227 ) -> Result<String, Error> {
228 if message.is_empty() {
229 return Err(Error::EmptyPayload);
230 }
231
232 let mut rng_bytes = [0u8; XCHACHA_NONCESIZE];
233 getrandom::fill(&mut rng_bytes)?;
234
235 Self::encrypt_with_derived_nonce(secret_key, &rng_bytes, message, footer)
236 }
237
238 pub fn decrypt(
243 secret_key: &SymmetricKey<V2>,
244 token: &UntrustedToken<Local, V2>,
245 footer: Option<&[u8]>,
246 ) -> Result<TrustedToken, Error> {
247 validate_footer_untrusted_token(token, footer)?;
248
249 let f = token.untrusted_footer();
250 let nc = token.untrusted_message();
251 let n = nc[..XCHACHA_NONCESIZE].as_ref();
252 let c = nc[n.len()..].as_ref();
253
254 let pre_auth = pae::pae(&[Self::HEADER.as_bytes(), n, f])?;
255 let mut out = vec![0u8; c.len() - POLY1305_OUTSIZE];
256
257 let sk = match SecretKey::from_slice(secret_key.as_bytes()) {
258 Ok(val) => val,
259 Err(orion::errors::UnknownCryptoError) => return Err(Error::Key),
260 };
261
262 match open(
263 &sk,
264 &Nonce::from_slice(n).unwrap(),
265 c,
266 Some(pre_auth.as_ref()),
267 &mut out,
268 ) {
269 Ok(()) => TrustedToken::_new(Self::HEADER, &out, f, &[]),
270 Err(orion::errors::UnknownCryptoError) => Err(Error::TokenValidation),
271 }
272 }
273}
274
275#[cfg(test)]
276#[cfg(feature = "std")]
277mod test_vectors {
278
279 use hex;
280
281 use super::*;
282 use core::convert::TryFrom;
283 use std::fs::File;
284 use std::io::BufReader;
285
286 use crate::claims::Claims;
287 use crate::common::tests::*;
288
289 fn test_local(test: &PasetoTest) {
290 debug_assert!(test.nonce.is_some());
291 debug_assert!(test.key.is_some());
292
293 let sk =
294 SymmetricKey::<V2>::from(&hex::decode(test.key.as_ref().unwrap()).unwrap()).unwrap();
295
296 let nonce = hex::decode(test.nonce.as_ref().unwrap()).unwrap();
297 let footer: Option<&[u8]> = if test.footer.is_empty() {
298 None
299 } else {
300 Some(test.footer.as_bytes())
301 };
302
303 if test.expect_fail {
305 if let Ok(ut) = UntrustedToken::<Local, V2>::try_from(&test.token) {
306 assert!(LocalToken::decrypt(&sk, &ut, footer).is_err());
307 }
308
309 return;
310 }
311
312 let message = test.payload.as_ref().unwrap().as_str().unwrap();
313
314 let actual =
315 LocalToken::encrypt_with_derived_nonce(&sk, &nonce, message.as_bytes(), footer)
316 .unwrap();
317 assert_eq!(actual, test.token, "Failed {:?}", test.name);
318
319 let ut = UntrustedToken::<Local, V2>::try_from(&test.token).unwrap();
320 let trusted = LocalToken::decrypt(&sk, &ut, footer).unwrap();
321 assert_eq!(trusted.payload(), message, "Failed {:?}", test.name);
322 assert_eq!(trusted.footer(), test.footer.as_bytes());
323 assert_eq!(trusted.header(), LocalToken::HEADER);
324 assert!(trusted.implicit_assert().is_empty());
325
326 let parsed_claims = Claims::from_bytes(trusted.payload().as_bytes()).unwrap();
327 let test_vector_claims = serde_json::from_str::<Payload>(message).unwrap();
328
329 assert_eq!(
330 parsed_claims.get_claim("data").unwrap().as_str().unwrap(),
331 test_vector_claims.data,
332 );
333 assert_eq!(
334 parsed_claims.get_claim("exp").unwrap().as_str().unwrap(),
335 test_vector_claims.exp,
336 );
337 }
338
339 fn test_public(test: &PasetoTest) {
340 debug_assert!(test.public_key.is_some());
341 debug_assert!(test.secret_key.is_some());
342
343 let sk = AsymmetricSecretKey::<V2>::from(
344 &hex::decode(test.secret_key.as_ref().unwrap()).unwrap(),
345 )
346 .unwrap();
347 let pk = AsymmetricPublicKey::<V2>::from(
348 &hex::decode(test.public_key.as_ref().unwrap()).unwrap(),
349 )
350 .unwrap();
351 let footer: Option<&[u8]> = if test.footer.is_empty() {
352 None
353 } else {
354 Some(test.footer.as_bytes())
355 };
356
357 if test.expect_fail {
359 if let Ok(ut) = UntrustedToken::<Public, V2>::try_from(&test.token) {
360 assert!(PublicToken::verify(&pk, &ut, footer).is_err());
361 }
362
363 return;
364 }
365
366 let message = test.payload.as_ref().unwrap().as_str().unwrap();
367
368 let actual = PublicToken::sign(&sk, message.as_bytes(), footer).unwrap();
369 assert_eq!(actual, test.token, "Failed {:?}", test.name);
370 let ut = UntrustedToken::<Public, V2>::try_from(&test.token).unwrap();
371
372 let trusted = PublicToken::verify(&pk, &ut, footer).unwrap();
373 assert_eq!(trusted.payload(), message);
374 assert_eq!(trusted.footer(), test.footer.as_bytes());
375 assert_eq!(trusted.header(), PublicToken::HEADER);
376 assert!(trusted.implicit_assert().is_empty());
377 }
378
379 #[test]
380 fn run_test_vectors() {
381 let path = "./test_vectors/v2.json";
382 let file = File::open(path).unwrap();
383 let reader = BufReader::new(file);
384 let tests: TestFile = serde_json::from_reader(reader).unwrap();
385
386 for t in tests.tests {
387 if t.public_key.is_some() {
389 test_public(&t);
390 }
391 if t.nonce.is_some() {
393 test_local(&t);
394 }
395 }
396 }
397}
398
399#[cfg(test)]
400mod test_tokens {
401 use super::*;
402 use crate::common::decode_b64;
403 use crate::keys::{AsymmetricKeyPair, Generate};
404 use crate::token::UntrustedToken;
405 use core::convert::TryFrom;
406
407 const TEST_LOCAL_SK_BYTES: [u8; 32] = [
408 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
409 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
410 ];
411
412 pub(crate) const TEST_SK_BYTES: [u8; 64] = [
413 180, 203, 251, 67, 223, 76, 226, 16, 114, 125, 149, 62, 74, 113, 51, 7, 250, 25, 187, 125,
414 159, 133, 4, 20, 56, 217, 225, 27, 148, 42, 55, 116, 30, 185, 219, 187, 188, 4, 124, 3,
415 253, 112, 96, 78, 0, 113, 240, 152, 126, 22, 178, 139, 117, 114, 37, 193, 31, 0, 65, 93,
416 14, 32, 177, 162,
417 ];
418
419 const TEST_PK_BYTES: [u8; 32] = [
420 30, 185, 219, 187, 188, 4, 124, 3, 253, 112, 96, 78, 0, 113, 240, 152, 126, 22, 178, 139,
421 117, 114, 37, 193, 31, 0, 65, 93, 14, 32, 177, 162,
422 ];
423
424 const MESSAGE: &str =
425 "{\"data\":\"this is a signed message\",\"exp\":\"2019-01-01T00:00:00+00:00\"}";
426 const FOOTER: &str = "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}";
427 const VALID_PUBLIC_TOKEN: &str = "v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOS0wMS0wMVQwMDowMDowMCswMDowMCJ9flsZsx_gYCR0N_Ec2QxJFFpvQAs7h9HtKwbVK2n1MJ3Rz-hwe8KUqjnd8FAnIJZ601tp7lGkguU63oGbomhoBw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9";
428 const VALID_LOCAL_TOKEN: &str = "v2.local.5K4SCXNhItIhyNuVIZcwrdtaDKiyF81-eWHScuE0idiVqCo72bbjo07W05mqQkhLZdVbxEa5I_u5sgVk1QLkcWEcOSlLHwNpCkvmGGlbCdNExn6Qclw3qTKIIl5-zSLIrxZqOLwcFLYbVK1SrQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9";
429
430 #[test]
431 fn test_gen_keypair() {
432 let kp = AsymmetricKeyPair::<V2>::generate().unwrap();
433
434 let token = PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), None).unwrap();
435
436 let ut = UntrustedToken::<Public, V2>::try_from(&token).unwrap();
437 assert!(PublicToken::verify(&kp.public, &ut, None).is_ok());
438 }
439
440 #[test]
441 fn test_untrusted_token_usage() {
442 let sk = SymmetricKey::<V2>::generate().unwrap();
444 let token = LocalToken::encrypt(&sk, MESSAGE.as_bytes(), Some(FOOTER.as_bytes())).unwrap();
445
446 let untrusted_token = UntrustedToken::<Local, V2>::try_from(token.as_str()).unwrap();
447 let _ = LocalToken::decrypt(
448 &sk,
449 &untrusted_token,
450 Some(untrusted_token.untrusted_footer()),
451 )
452 .unwrap();
453
454 let kp = AsymmetricKeyPair::<V2>::generate().unwrap();
456 let token =
457 PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), Some(FOOTER.as_bytes())).unwrap();
458
459 let untrusted_token = UntrustedToken::<Public, V2>::try_from(token.as_str()).unwrap();
460 assert!(PublicToken::verify(&kp.public, &untrusted_token, Some(FOOTER.as_bytes())).is_ok());
461 }
462
463 #[test]
464 fn test_roundtrip_local() {
465 let sk = SymmetricKey::<V2>::generate().unwrap();
466 let message = "token payload";
467
468 let token = LocalToken::encrypt(&sk, message.as_bytes(), None).unwrap();
469 let ut = UntrustedToken::<Local, V2>::try_from(&token).unwrap();
470 let trusted_token = LocalToken::decrypt(&sk, &ut, None).unwrap();
471
472 assert_eq!(trusted_token.payload(), message);
473 }
474
475 #[test]
476 fn test_roundtrip_public() {
477 let test_sk = AsymmetricSecretKey::<V2>::from(&TEST_SK_BYTES).unwrap();
478 let test_pk = AsymmetricPublicKey::<V2>::from(&TEST_PK_BYTES).unwrap();
479
480 let token = PublicToken::sign(&test_sk, MESSAGE.as_bytes(), None).unwrap();
481 let ut = UntrustedToken::<Public, V2>::try_from(&token).unwrap();
482
483 assert!(PublicToken::verify(&test_pk, &ut, None).is_ok());
484 }
485
486 #[test]
487 fn footer_logic() {
488 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
489 let test_sk = AsymmetricSecretKey::<V2>::from(&TEST_SK_BYTES).unwrap();
490 let test_pk = AsymmetricPublicKey::<V2>::from(&TEST_PK_BYTES).unwrap();
491 let message =
492 b"{\"data\":\"this is a signed message\",\"exp\":\"2019-01-01T00:00:00+00:00\"}";
493
494 let actual_some = UntrustedToken::<Public, V2>::try_from(
496 &PublicToken::sign(&test_sk, message, Some(FOOTER.as_bytes())).unwrap(),
497 )
498 .unwrap();
499 let actual_none = UntrustedToken::<Public, V2>::try_from(
500 &PublicToken::sign(&test_sk, message, None).unwrap(),
501 )
502 .unwrap();
503
504 assert!(PublicToken::verify(&test_pk, &actual_some, None).is_ok());
510 assert!(PublicToken::verify(&test_pk, &actual_some, Some(FOOTER.as_bytes())).is_ok());
512 assert!(PublicToken::verify(&test_pk, &actual_none, Some(FOOTER.as_bytes())).is_err());
514
515 let actual_some = UntrustedToken::<Local, V2>::try_from(
516 &LocalToken::encrypt(&test_local_sk, message, Some(FOOTER.as_bytes())).unwrap(),
517 )
518 .unwrap();
519 let actual_none = UntrustedToken::<Local, V2>::try_from(
520 &LocalToken::encrypt(&test_local_sk, message, None).unwrap(),
521 )
522 .unwrap();
523
524 assert!(LocalToken::decrypt(&test_local_sk, &actual_some, None).is_ok());
526 assert!(LocalToken::decrypt(&test_local_sk, &actual_some, Some(FOOTER.as_bytes())).is_ok());
527 assert!(
528 LocalToken::decrypt(&test_local_sk, &actual_none, Some(FOOTER.as_bytes())).is_err()
529 );
530 }
531
532 #[test]
533 fn empty_payload() {
535 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
536 let test_sk = AsymmetricSecretKey::<V2>::from(&TEST_SK_BYTES).unwrap();
537
538 assert_eq!(
539 PublicToken::sign(&test_sk, b"", None).unwrap_err(),
540 Error::EmptyPayload
541 );
542 assert_eq!(
543 LocalToken::encrypt(&test_local_sk, b"", None).unwrap_err(),
544 Error::EmptyPayload
545 );
546 }
547
548 #[test]
549 fn err_on_modified_footer() {
550 let test_pk = AsymmetricPublicKey::<V2>::from(&TEST_PK_BYTES).unwrap();
551 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
552
553 assert_eq!(
554 PublicToken::verify(
555 &test_pk,
556 &UntrustedToken::<Public, V2>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
557 Some(FOOTER.replace("kid", "mid").as_bytes())
558 )
559 .unwrap_err(),
560 Error::TokenValidation
561 );
562 assert_eq!(
563 LocalToken::decrypt(
564 &test_local_sk,
565 &UntrustedToken::<Local, V2>::try_from(VALID_LOCAL_TOKEN).unwrap(),
566 Some(FOOTER.replace("kid", "mid").as_bytes())
567 )
568 .unwrap_err(),
569 Error::TokenValidation
570 );
571 }
572
573 #[test]
574 fn err_on_footer_in_token_none_supplied() {
575 let test_pk = AsymmetricPublicKey::<V2>::from(&TEST_PK_BYTES).unwrap();
576 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
577
578 assert_eq!(
579 PublicToken::verify(
580 &test_pk,
581 &UntrustedToken::<Public, V2>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
582 Some(b"")
583 )
584 .unwrap_err(),
585 Error::TokenValidation
586 );
587 assert_eq!(
588 LocalToken::decrypt(
589 &test_local_sk,
590 &UntrustedToken::<Local, V2>::try_from(VALID_LOCAL_TOKEN).unwrap(),
591 Some(b"")
592 )
593 .unwrap_err(),
594 Error::TokenValidation
595 );
596 }
597
598 #[test]
599 fn err_on_no_footer_in_token_some_supplied() {
600 let test_pk = AsymmetricPublicKey::<V2>::from(&TEST_PK_BYTES).unwrap();
601 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
602
603 let split_public = VALID_PUBLIC_TOKEN.split('.').collect::<Vec<&str>>();
604 let invalid_public: String = format!(
605 "{}.{}.{}",
606 split_public[0], split_public[1], split_public[2]
607 );
608
609 let split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
610 let invalid_local: String =
611 format!("{}.{}.{}", split_local[0], split_local[1], split_local[2]);
612
613 assert_eq!(
614 PublicToken::verify(
615 &test_pk,
616 &UntrustedToken::<Public, V2>::try_from(&invalid_public).unwrap(),
617 Some(FOOTER.as_bytes())
618 )
619 .unwrap_err(),
620 Error::TokenValidation
621 );
622 assert_eq!(
623 LocalToken::decrypt(
624 &test_local_sk,
625 &UntrustedToken::<Local, V2>::try_from(&invalid_local).unwrap(),
626 Some(FOOTER.as_bytes())
627 )
628 .unwrap_err(),
629 Error::TokenValidation
630 );
631 }
632
633 #[test]
634 fn err_on_modified_signature() {
635 let test_pk = AsymmetricPublicKey::<V2>::from(&TEST_PK_BYTES).unwrap();
636
637 let mut split_public = VALID_PUBLIC_TOKEN.split('.').collect::<Vec<&str>>();
638 let mut bad_sig = decode_b64(split_public[2]).unwrap();
639 bad_sig.copy_within(0..32, 32);
640 let tmp = encode_b64(bad_sig).unwrap();
641 split_public[2] = &tmp;
642 let invalid_public: String = format!(
643 "{}.{}.{}.{}",
644 split_public[0], split_public[1], split_public[2], split_public[3]
645 );
646
647 assert_eq!(
648 PublicToken::verify(
649 &test_pk,
650 &UntrustedToken::<Public, V2>::try_from(&invalid_public).unwrap(),
651 Some(FOOTER.as_bytes())
652 )
653 .unwrap_err(),
654 Error::TokenValidation
655 );
656 }
657
658 #[test]
659 fn err_on_modified_tag() {
660 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
661
662 let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
663 let mut bad_tag = decode_b64(split_local[2]).unwrap();
664 let tlen = bad_tag.len();
665 bad_tag.copy_within(0..16, tlen - 16);
666 let tmp = encode_b64(bad_tag).unwrap();
667 split_local[2] = &tmp;
668 let invalid_local: String = format!(
669 "{}.{}.{}.{}",
670 split_local[0], split_local[1], split_local[2], split_local[3]
671 );
672
673 assert_eq!(
674 LocalToken::decrypt(
675 &test_local_sk,
676 &UntrustedToken::<Local, V2>::try_from(&invalid_local).unwrap(),
677 Some(FOOTER.as_bytes())
678 )
679 .unwrap_err(),
680 Error::TokenValidation
681 );
682 }
683
684 #[test]
685 fn err_on_modified_ciphertext() {
686 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
687
688 let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
689 let mut bad_ct = decode_b64(split_local[2]).unwrap();
690 let ctlen = bad_ct.len();
691 bad_ct.copy_within((ctlen - 16)..ctlen, 24);
692 let tmp = encode_b64(bad_ct).unwrap();
693 split_local[2] = &tmp;
694 let invalid_local: String = format!(
695 "{}.{}.{}.{}",
696 split_local[0], split_local[1], split_local[2], split_local[3]
697 );
698
699 assert_eq!(
700 LocalToken::decrypt(
701 &test_local_sk,
702 &UntrustedToken::<Local, V2>::try_from(&invalid_local).unwrap(),
703 Some(FOOTER.as_bytes())
704 )
705 .unwrap_err(),
706 Error::TokenValidation
707 );
708 }
709
710 #[test]
711 fn err_on_modified_nonce() {
712 let test_local_sk = SymmetricKey::<V2>::from(&TEST_LOCAL_SK_BYTES).unwrap();
713
714 let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
715 let mut bad_nonce = decode_b64(split_local[2]).unwrap();
716 let nlen = bad_nonce.len();
717 bad_nonce.copy_within((nlen - 24)..nlen, 0);
718 let tmp = encode_b64(bad_nonce).unwrap();
719 split_local[2] = &tmp;
720 let invalid_local: String = format!(
721 "{}.{}.{}.{}",
722 split_local[0], split_local[1], split_local[2], split_local[3]
723 );
724
725 assert_eq!(
726 LocalToken::decrypt(
727 &test_local_sk,
728 &UntrustedToken::<Local, V2>::try_from(&invalid_local).unwrap(),
729 Some(FOOTER.as_bytes())
730 )
731 .unwrap_err(),
732 Error::TokenValidation
733 );
734 }
735
736 #[test]
737 fn err_on_invalid_public_secret_key() {
738 let bad_pk = AsymmetricPublicKey::<V2>::from(&[0u8; 32]).unwrap();
739
740 assert_eq!(
741 PublicToken::verify(
742 &bad_pk,
743 &UntrustedToken::<Public, V2>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
744 Some(FOOTER.as_bytes())
745 )
746 .unwrap_err(),
747 Error::TokenValidation
748 );
749 }
750
751 #[test]
752 fn err_on_invalid_shared_secret_key() {
753 let bad_local_sk = SymmetricKey::<V2>::from(&[0u8; 32]).unwrap();
754
755 assert_eq!(
756 LocalToken::decrypt(
757 &bad_local_sk,
758 &UntrustedToken::<Local, V2>::try_from(VALID_LOCAL_TOKEN).unwrap(),
759 Some(FOOTER.as_bytes())
760 )
761 .unwrap_err(),
762 Error::TokenValidation
763 );
764 }
765}
766
767#[cfg(test)]
768mod test_keys {
769 use super::*;
770 use crate::version2::test_tokens::TEST_SK_BYTES;
771
772 #[test]
773 fn test_symmetric_gen() {
774 let randomv = SymmetricKey::<V2>::generate().unwrap();
775 assert_ne!(randomv.as_bytes(), &[0u8; 32]);
776 }
777
778 #[test]
779 fn test_invalid_sizes() {
780 assert!(AsymmetricSecretKey::<V2>::from(&[1u8; 63]).is_err());
781 assert!(AsymmetricSecretKey::<V2>::from(&TEST_SK_BYTES).is_ok());
782 assert!(AsymmetricSecretKey::<V2>::from(&[1u8; 65]).is_err());
783
784 assert!(AsymmetricPublicKey::<V2>::from(&[1u8; 31]).is_err());
785 assert!(AsymmetricPublicKey::<V2>::from(&[1u8; 32]).is_ok());
786 assert!(AsymmetricPublicKey::<V2>::from(&[1u8; 33]).is_err());
787
788 assert!(SymmetricKey::<V2>::from(&[0u8; 31]).is_err());
789 assert!(SymmetricKey::<V2>::from(&[0u8; 32]).is_ok());
790 assert!(SymmetricKey::<V2>::from(&[0u8; 33]).is_err());
791 }
792
793 #[test]
794 fn try_from_secret_to_public() {
795 let kpv2 = AsymmetricKeyPair::<V2>::generate().unwrap();
796 let pubv2 = AsymmetricPublicKey::<V2>::try_from(&kpv2.secret).unwrap();
797 assert_eq!(pubv2.as_bytes(), kpv2.public.as_bytes());
798 assert_eq!(pubv2, kpv2.public);
799 assert_eq!(&kpv2.secret.as_bytes()[32..], pubv2.as_bytes());
800 }
801
802 #[test]
803 fn test_trait_impls() {
804 let debug = format!("{:?}", SymmetricKey::<V2>::generate().unwrap());
805 assert_eq!(debug, "SymmetricKey {***OMITTED***}");
806
807 let randomv = SymmetricKey::<V2>::generate().unwrap();
808 let zero = SymmetricKey::<V2>::from(&[0u8; V2::LOCAL_KEY]).unwrap();
809 assert_ne!(randomv, zero);
810
811 let debug = format!("{:?}", AsymmetricKeyPair::<V2>::generate().unwrap().secret);
812 assert_eq!(debug, "AsymmetricSecretKey {***OMITTED***}");
813
814 let random1 = AsymmetricKeyPair::<V2>::generate().unwrap();
815 let random2 = AsymmetricKeyPair::<V2>::generate().unwrap();
816 assert_ne!(random1.secret, random2.secret);
817 }
818
819 #[test]
820 fn test_clone() {
821 let sk = SymmetricKey::<V2>::generate().unwrap();
822 assert_eq!(sk, sk.clone());
823
824 let kp = AsymmetricKeyPair::<V2>::generate().unwrap();
825 assert_eq!(kp.secret, kp.secret.clone());
826 assert_eq!(kp.public, kp.public.clone());
827 }
828}