1#![cfg_attr(docsrs, doc(cfg(feature = "v4")))]
2
3use core::convert::TryFrom;
4use core::marker::PhantomData;
5
6use crate::common::{encode_b64, validate_footer_untrusted_token};
7use crate::errors::Error;
8use crate::keys::{
9 AsymmetricKeyPair, AsymmetricPublicKey, AsymmetricSecretKey, Generate, SymmetricKey,
10};
11use crate::pae;
12use crate::token::{Local, Public, TrustedToken, UntrustedToken};
13use crate::version::private::Version;
14use alloc::string::String;
15use alloc::vec::Vec;
16use blake2b::SecretKey as AuthKey;
17use ed25519_compact::{KeyPair, PublicKey, SecretKey, Seed, Signature};
18use orion::hazardous::mac::blake2b;
19use orion::hazardous::mac::blake2b::Blake2b;
20use orion::hazardous::stream::xchacha20;
21use subtle::ConstantTimeEq;
22use xchacha20::Nonce as EncNonce;
23use xchacha20::SecretKey as EncKey;
24
25#[derive(Debug, PartialEq, Eq, Clone)]
26pub struct V4;
28
29impl Version for V4 {
30 const LOCAL_KEY: usize = 32;
31 const SECRET_KEY: usize = 32 + Self::PUBLIC_KEY; const PUBLIC_KEY: usize = 32;
33 const PUBLIC_SIG: usize = 64;
34 const LOCAL_NONCE: usize = 32;
35 const LOCAL_TAG: usize = 32;
36 const PUBLIC_HEADER: &'static str = "v4.public.";
37 const LOCAL_HEADER: &'static str = "v4.local.";
38 #[cfg(feature = "paserk")]
39 const PASERK_ID: usize = 44;
40
41 fn validate_local_key(key_bytes: &[u8]) -> Result<(), Error> {
42 if key_bytes.len() != Self::LOCAL_KEY {
43 return Err(Error::Key);
44 }
45
46 Ok(())
47 }
48
49 fn validate_secret_key(key_bytes: &[u8]) -> Result<(), Error> {
50 if key_bytes.len() != Self::SECRET_KEY {
51 return Err(Error::Key);
52 }
53
54 let seed = Seed::from_slice(&key_bytes[..32]).map_err(|_| Error::Key)?;
55 let kp = KeyPair::from_seed(seed);
56
57 if !bool::from(kp.pk.as_slice().ct_eq(&key_bytes[32..])) {
58 return Err(Error::Key);
59 }
60
61 Ok(())
62 }
63
64 fn validate_public_key(key_bytes: &[u8]) -> Result<(), Error> {
65 if key_bytes.len() != Self::PUBLIC_KEY {
66 return Err(Error::Key);
67 }
68
69 Ok(())
70 }
71}
72
73impl TryFrom<&AsymmetricSecretKey<V4>> for AsymmetricPublicKey<V4> {
74 type Error = Error;
75
76 fn try_from(value: &AsymmetricSecretKey<V4>) -> Result<Self, Self::Error> {
77 AsymmetricPublicKey::<V4>::from(&value.as_bytes()[32..])
78 }
79}
80
81impl Generate<AsymmetricKeyPair<V4>, V4> for AsymmetricKeyPair<V4> {
82 fn generate() -> Result<AsymmetricKeyPair<V4>, Error> {
83 let key_pair = KeyPair::generate();
84
85 let secret = AsymmetricSecretKey::<V4>::from(key_pair.sk.as_ref())
86 .map_err(|_| Error::KeyGeneration)?;
87 let public = AsymmetricPublicKey::<V4>::from(key_pair.pk.as_ref())
88 .map_err(|_| Error::KeyGeneration)?;
89
90 Ok(Self { public, secret })
91 }
92}
93
94impl Generate<SymmetricKey<V4>, V4> for SymmetricKey<V4> {
95 fn generate() -> Result<SymmetricKey<V4>, Error> {
96 let mut rng_bytes = vec![0u8; V4::LOCAL_KEY];
97 V4::validate_local_key(&rng_bytes)?;
98 getrandom::fill(&mut rng_bytes)?;
99
100 Ok(Self {
101 bytes: rng_bytes,
102 phantom: PhantomData,
103 })
104 }
105}
106
107pub struct PublicToken;
109
110impl PublicToken {
111 pub const HEADER: &'static str = "v4.public.";
113
114 pub fn sign(
116 secret_key: &AsymmetricSecretKey<V4>,
117 message: &[u8],
118 footer: Option<&[u8]>,
119 implicit_assert: Option<&[u8]>,
120 ) -> Result<String, Error> {
121 if message.is_empty() {
122 return Err(Error::EmptyPayload);
123 }
124
125 let sk = SecretKey::from_slice(secret_key.as_bytes()).map_err(|_| Error::Key)?;
126
127 let f = footer.unwrap_or(&[]);
128 let i = implicit_assert.unwrap_or(&[]);
129 let m2 = pae::pae(&[Self::HEADER.as_bytes(), message, f, i])?;
130 let sig = sk.sign(m2, None);
131
132 let mut m_sig: Vec<u8> = Vec::from(message);
133 m_sig.extend_from_slice(sig.as_ref());
134
135 let token_no_footer = format!("{}{}", Self::HEADER, encode_b64(m_sig)?);
136
137 if f.is_empty() {
138 Ok(token_no_footer)
139 } else {
140 Ok(format!("{}.{}", token_no_footer, encode_b64(f)?))
141 }
142 }
143
144 pub fn verify(
149 public_key: &AsymmetricPublicKey<V4>,
150 token: &UntrustedToken<Public, V4>,
151 footer: Option<&[u8]>,
152 implicit_assert: Option<&[u8]>,
153 ) -> Result<TrustedToken, Error> {
154 validate_footer_untrusted_token(token, footer)?;
155
156 let f = token.untrusted_footer();
157 let i = implicit_assert.unwrap_or(&[]);
158 let sm = token.untrusted_message();
159 let m = token.untrusted_payload();
160 let s = sm[m.len()..m.len() + V4::PUBLIC_SIG].as_ref();
161
162 let m2 = pae::pae(&[Self::HEADER.as_bytes(), m, f, i])?;
163 let pk: PublicKey = PublicKey::from_slice(public_key.as_bytes()).map_err(|_| Error::Key)?;
164
165 debug_assert!(s.len() == V4::PUBLIC_SIG);
166 let sig = Signature::from_slice(s).map_err(|_| Error::TokenValidation)?;
168
169 if pk.verify(m2, &sig).is_ok() {
170 TrustedToken::_new(Self::HEADER, m, f, i)
171 } else {
172 Err(Error::TokenValidation)
173 }
174 }
175}
176
177pub struct LocalToken;
179
180impl LocalToken {
181 pub const HEADER: &'static str = "v4.local.";
183
184 const DOMAIN_SEPARATOR_ENC: &'static str = "paseto-encryption-key";
186
187 const DOMAIN_SEPARATOR_AUTH: &'static str = "paseto-auth-key-for-aead";
189
190 const M1_LEN: usize = V4::LOCAL_NONCE + Self::DOMAIN_SEPARATOR_ENC.len();
191 const M2_LEN: usize = V4::LOCAL_NONCE + Self::DOMAIN_SEPARATOR_AUTH.len();
192
193 fn key_split(sk: &[u8], n: &[u8]) -> Result<(EncKey, EncNonce, AuthKey), Error> {
195 debug_assert_eq!(n.len(), V4::LOCAL_NONCE);
196 debug_assert_eq!(sk.len(), V4::LOCAL_KEY);
197
198 let mut m1 = [0u8; Self::M1_LEN];
199 m1[..21].copy_from_slice(Self::DOMAIN_SEPARATOR_ENC.as_bytes());
200 m1[21..].copy_from_slice(n);
201
202 let mut m2 = [0u8; Self::M2_LEN];
203 m2[..24].copy_from_slice(Self::DOMAIN_SEPARATOR_AUTH.as_bytes());
204 m2[24..].copy_from_slice(n);
205
206 let sk = blake2b::SecretKey::from_slice(sk).unwrap();
207 let mut b2_ctx = Blake2b::new(&sk, 56).unwrap();
208 b2_ctx.update(&m1).unwrap();
209 let tmp = b2_ctx.finalize().unwrap();
210 let enc_key = EncKey::from_slice(&tmp.unprotected_as_bytes()[..32]).unwrap();
211 let n2 = EncNonce::from_slice(&tmp.unprotected_as_bytes()[32..]).unwrap();
212
213 b2_ctx = Blake2b::new(&sk, V4::LOCAL_TAG).unwrap();
214 b2_ctx.update(&m2).unwrap();
215 let auth_key =
216 AuthKey::from_slice(b2_ctx.finalize().unwrap().unprotected_as_bytes()).unwrap();
217
218 Ok((enc_key, n2, auth_key))
219 }
220
221 pub(crate) fn encrypt_with_nonce(
223 secret_key: &SymmetricKey<V4>,
224 nonce: &[u8],
225 message: &[u8],
226 footer: Option<&[u8]>,
227 implicit_assert: Option<&[u8]>,
228 ) -> Result<String, Error> {
229 debug_assert_eq!(nonce.len(), V4::LOCAL_NONCE);
230 let f = footer.unwrap_or(&[]);
231 let i = implicit_assert.unwrap_or(&[]);
232
233 let (enc_key, n2, auth_key) = Self::key_split(secret_key.as_bytes(), nonce)?;
234
235 let mut ciphertext = vec![0u8; message.len()];
236 xchacha20::encrypt(&enc_key, &n2, 0, message, &mut ciphertext)
237 .map_err(|_| Error::Encryption)?;
238 let pre_auth = pae::pae(&[Self::HEADER.as_bytes(), nonce, ciphertext.as_slice(), f, i])?;
239
240 let mut b2_ctx = Blake2b::new(&auth_key, V4::LOCAL_TAG).unwrap();
241 b2_ctx
242 .update(pre_auth.as_slice())
243 .map_err(|_| Error::Encryption)?;
244 let tag = b2_ctx.finalize().map_err(|_| Error::Encryption)?;
245
246 let concat_len: usize = match (nonce.len() + tag.len()).checked_add(ciphertext.len()) {
248 Some(len) => len,
249 None => return Err(Error::Encryption),
250 };
251 let mut concat = vec![0u8; concat_len];
252 concat[..32].copy_from_slice(nonce);
253 concat[32..32 + ciphertext.len()].copy_from_slice(ciphertext.as_slice());
254 concat[concat_len - V4::LOCAL_TAG..].copy_from_slice(tag.unprotected_as_bytes());
255
256 let token_no_footer = format!("{}{}", Self::HEADER, encode_b64(concat)?);
257
258 if f.is_empty() {
259 Ok(token_no_footer)
260 } else {
261 Ok(format!("{}.{}", token_no_footer, encode_b64(f)?))
262 }
263 }
264
265 pub fn encrypt(
267 secret_key: &SymmetricKey<V4>,
268 message: &[u8],
269 footer: Option<&[u8]>,
270 implicit_assert: Option<&[u8]>,
271 ) -> Result<String, Error> {
272 if message.is_empty() {
273 return Err(Error::EmptyPayload);
274 }
275
276 let mut n = [0u8; V4::LOCAL_NONCE];
277 getrandom::fill(&mut n)?;
278
279 Self::encrypt_with_nonce(secret_key, &n, message, footer, implicit_assert)
280 }
281
282 #[allow(clippy::many_single_char_names)] pub fn decrypt(
288 secret_key: &SymmetricKey<V4>,
289 token: &UntrustedToken<Local, V4>,
290 footer: Option<&[u8]>,
291 implicit_assert: Option<&[u8]>,
292 ) -> Result<TrustedToken, Error> {
293 validate_footer_untrusted_token(token, footer)?;
294
295 let f = token.untrusted_footer();
296 let i = implicit_assert.unwrap_or(&[]);
297 let nc = token.untrusted_message();
298
299 let mut n: [u8; 32] = [0u8; V4::LOCAL_NONCE];
300 n.copy_from_slice(nc[..V4::LOCAL_NONCE].as_ref());
301 let c = token.untrusted_payload();
302 let t = nc[nc.len() - V4::LOCAL_TAG..].as_ref();
303
304 let (enc_key, n2, auth_key) = Self::key_split(secret_key.as_bytes(), &n)?;
305
306 let pre_auth = pae::pae(&[Self::HEADER.as_bytes(), n.as_ref(), c, f, i])?;
307 let expected_tag = blake2b::Tag::from_slice(t).map_err(|_| Error::TokenValidation)?;
308 Blake2b::verify(&expected_tag, &auth_key, 32, pre_auth.as_slice())
309 .map_err(|_| Error::TokenValidation)?;
310
311 let mut out = vec![0u8; c.len()];
312 xchacha20::decrypt(&enc_key, &n2, 0, c, &mut out).map_err(|_| Error::TokenValidation)?;
313
314 TrustedToken::_new(Self::HEADER, &out, f, i)
315 }
316}
317
318#[cfg(test)]
319#[cfg(feature = "std")]
320mod test_vectors {
321
322 use hex;
323
324 use super::*;
325 use core::convert::TryFrom;
326 use std::fs::File;
327 use std::io::BufReader;
328
329 use crate::claims::Claims;
330 use crate::common::tests::*;
331
332 fn test_local(test: &PasetoTest) {
333 debug_assert!(test.nonce.is_some());
334 debug_assert!(test.key.is_some());
335
336 let sk =
337 SymmetricKey::<V4>::from(&hex::decode(test.key.as_ref().unwrap()).unwrap()).unwrap();
338
339 let nonce = hex::decode(test.nonce.as_ref().unwrap()).unwrap();
340 let footer: Option<&[u8]> = if test.footer.is_empty() {
341 None
342 } else {
343 Some(test.footer.as_bytes())
344 };
345 let implicit_assert = test.implicit_assertion.as_bytes();
346
347 if test.expect_fail {
349 if let Ok(ut) = UntrustedToken::<Local, V4>::try_from(&test.token) {
350 assert!(LocalToken::decrypt(&sk, &ut, footer, Some(implicit_assert)).is_err());
351 }
352
353 return;
354 }
355
356 let message = test.payload.as_ref().unwrap().as_str().unwrap();
357
358 let actual = LocalToken::encrypt_with_nonce(
359 &sk,
360 &nonce,
361 message.as_bytes(),
362 footer,
363 Some(implicit_assert),
364 )
365 .unwrap();
366 assert_eq!(actual, test.token, "Failed {:?}", test.name);
367
368 let ut = UntrustedToken::<Local, V4>::try_from(&test.token).unwrap();
369 let trusted = LocalToken::decrypt(&sk, &ut, footer, Some(implicit_assert)).unwrap();
370 assert_eq!(trusted.payload(), message, "Failed {:?}", test.name);
371 assert_eq!(trusted.footer(), test.footer.as_bytes());
372 assert_eq!(trusted.header(), LocalToken::HEADER);
373 assert_eq!(trusted.implicit_assert(), implicit_assert);
374
375 let parsed_claims = Claims::from_bytes(trusted.payload().as_bytes()).unwrap();
376 let test_vector_claims = serde_json::from_str::<Payload>(message).unwrap();
377
378 assert_eq!(
379 parsed_claims.get_claim("data").unwrap().as_str().unwrap(),
380 test_vector_claims.data,
381 );
382 assert_eq!(
383 parsed_claims.get_claim("exp").unwrap().as_str().unwrap(),
384 test_vector_claims.exp,
385 );
386 }
387
388 fn test_public(test: &PasetoTest) {
389 debug_assert!(test.public_key.is_some());
390 debug_assert!(test.secret_key.is_some());
391
392 let sk = AsymmetricSecretKey::<V4>::from(
393 &hex::decode(test.secret_key.as_ref().unwrap()).unwrap(),
394 )
395 .unwrap();
396 let pk = AsymmetricPublicKey::<V4>::from(
397 &hex::decode(test.public_key.as_ref().unwrap()).unwrap(),
398 )
399 .unwrap();
400 let footer: Option<&[u8]> = if test.footer.is_empty() {
401 None
402 } else {
403 Some(test.footer.as_bytes())
404 };
405 let implicit_assert = test.implicit_assertion.as_bytes();
406
407 if test.expect_fail {
409 if let Ok(ut) = UntrustedToken::<Public, V4>::try_from(&test.token) {
410 assert!(PublicToken::verify(&pk, &ut, footer, Some(implicit_assert)).is_err());
411 }
412
413 return;
414 }
415
416 let message = test.payload.as_ref().unwrap().as_str().unwrap();
417
418 let actual =
419 PublicToken::sign(&sk, message.as_bytes(), footer, Some(implicit_assert)).unwrap();
420 assert_eq!(actual, test.token, "Failed {:?}", test.name);
421 let ut = UntrustedToken::<Public, V4>::try_from(&test.token).unwrap();
422
423 let trusted = PublicToken::verify(&pk, &ut, footer, Some(implicit_assert)).unwrap();
424 assert_eq!(trusted.payload(), message);
425 assert_eq!(trusted.footer(), test.footer.as_bytes());
426 assert_eq!(trusted.header(), PublicToken::HEADER);
427 assert_eq!(trusted.implicit_assert(), implicit_assert);
428 }
429
430 #[test]
431 fn run_test_vectors() {
432 let path = "./test_vectors/v4.json";
433 let file = File::open(path).unwrap();
434 let reader = BufReader::new(file);
435 let tests: TestFile = serde_json::from_reader(reader).unwrap();
436
437 for t in tests.tests {
438 if t.public_key.is_some() {
440 test_public(&t);
441 }
442 if t.nonce.is_some() {
444 test_local(&t);
445 }
446 }
447 }
448}
449
450#[cfg(test)]
451mod test_tokens {
452 use super::*;
453 use crate::common::decode_b64;
454 use crate::keys::{AsymmetricKeyPair, Generate, SymmetricKey};
455 use crate::token::UntrustedToken;
456 use core::convert::TryFrom;
457
458 const TEST_LOCAL_SK_BYTES: [u8; 32] = [
459 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
460 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
461 ];
462
463 pub(crate) const TEST_SK_BYTES: [u8; 64] = [
464 180, 203, 251, 67, 223, 76, 226, 16, 114, 125, 149, 62, 74, 113, 51, 7, 250, 25, 187, 125,
465 159, 133, 4, 20, 56, 217, 225, 27, 148, 42, 55, 116, 30, 185, 219, 187, 188, 4, 124, 3,
466 253, 112, 96, 78, 0, 113, 240, 152, 126, 22, 178, 139, 117, 114, 37, 193, 31, 0, 65, 93,
467 14, 32, 177, 162,
468 ];
469
470 const TEST_PK_BYTES: [u8; 32] = [
471 30, 185, 219, 187, 188, 4, 124, 3, 253, 112, 96, 78, 0, 113, 240, 152, 126, 22, 178, 139,
472 117, 114, 37, 193, 31, 0, 65, 93, 14, 32, 177, 162,
473 ];
474
475 const MESSAGE: &str =
476 "{\"data\":\"this is a signed message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}";
477 const FOOTER: &str = "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}";
478 const VALID_PUBLIC_TOKEN: &str = "v4.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAyMi0wMS0wMVQwMDowMDowMCswMDowMCJ9v3Jt8mx_TdM2ceTGoqwrh4yDFn0XsHvvV_D0DtwQxVrJEBMl0F2caAdgnpKlt4p7xBnx1HcO-SPo8FPp214HDw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9";
479 const VALID_LOCAL_TOKEN: &str = "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4x-RMNXtQNbz7FvFZ_G-lFpk5RG3EOrwDL6CgDqcerSQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9";
480
481 #[test]
482 fn test_gen_keypair() {
483 let kp = AsymmetricKeyPair::<V4>::generate().unwrap();
484
485 let token = PublicToken::sign(&kp.secret, MESSAGE.as_bytes(), None, None).unwrap();
486
487 let ut = UntrustedToken::<Public, V4>::try_from(&token).unwrap();
488 assert!(PublicToken::verify(&kp.public, &ut, None, None).is_ok());
489 }
490
491 #[test]
492 fn test_untrusted_token_usage() {
493 let sk = SymmetricKey::<V4>::generate().unwrap();
495 let token =
496 LocalToken::encrypt(&sk, MESSAGE.as_bytes(), Some(FOOTER.as_bytes()), None).unwrap();
497
498 let untrusted_token = UntrustedToken::<Local, V4>::try_from(token.as_str()).unwrap();
499 let _ = LocalToken::decrypt(
500 &sk,
501 &untrusted_token,
502 Some(untrusted_token.untrusted_footer()),
503 None,
504 )
505 .unwrap();
506
507 let kp = AsymmetricKeyPair::<V4>::generate().unwrap();
509 let token = PublicToken::sign(
510 &kp.secret,
511 MESSAGE.as_bytes(),
512 Some(FOOTER.as_bytes()),
513 None,
514 )
515 .unwrap();
516
517 let untrusted_token = UntrustedToken::<Public, V4>::try_from(token.as_str()).unwrap();
518 assert!(
519 PublicToken::verify(&kp.public, &untrusted_token, Some(FOOTER.as_bytes()), None)
520 .is_ok()
521 );
522 }
523
524 #[test]
525 fn test_roundtrip_local() {
526 let sk = SymmetricKey::<V4>::generate().unwrap();
527 let message = "token payload";
528
529 let token = LocalToken::encrypt(&sk, message.as_bytes(), None, None).unwrap();
530 let ut = UntrustedToken::<Local, V4>::try_from(&token).unwrap();
531 let trusted_token = LocalToken::decrypt(&sk, &ut, None, None).unwrap();
532
533 assert_eq!(trusted_token.payload(), message);
534 }
535
536 #[test]
537 fn test_roundtrip_public() {
538 let test_sk = AsymmetricSecretKey::<V4>::from(&TEST_SK_BYTES).unwrap();
539 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
540
541 let token = PublicToken::sign(&test_sk, MESSAGE.as_bytes(), None, None).unwrap();
542 let ut = UntrustedToken::<Public, V4>::try_from(&token).unwrap();
543
544 assert!(PublicToken::verify(&test_pk, &ut, None, None).is_ok());
545 }
546
547 #[test]
548 fn footer_logic() {
549 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
550 let test_sk = AsymmetricSecretKey::<V4>::from(&TEST_SK_BYTES).unwrap();
551 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
552 let message =
553 b"{\"data\":\"this is a signed message\",\"exp\":\"2019-01-01T00:00:00+00:00\"}";
554
555 let actual_some = UntrustedToken::<Public, V4>::try_from(
557 &PublicToken::sign(&test_sk, message, Some(FOOTER.as_bytes()), None).unwrap(),
558 )
559 .unwrap();
560 let actual_none = UntrustedToken::<Public, V4>::try_from(
561 &PublicToken::sign(&test_sk, message, None, None).unwrap(),
562 )
563 .unwrap();
564
565 assert!(PublicToken::verify(&test_pk, &actual_some, None, None).is_ok());
571 assert!(PublicToken::verify(&test_pk, &actual_some, Some(FOOTER.as_bytes()), None).is_ok());
573 assert!(
575 PublicToken::verify(&test_pk, &actual_none, Some(FOOTER.as_bytes()), None).is_err()
576 );
577
578 let actual_some = UntrustedToken::<Local, V4>::try_from(
579 &LocalToken::encrypt(&test_local_sk, message, Some(FOOTER.as_bytes()), None).unwrap(),
580 )
581 .unwrap();
582 let actual_none = UntrustedToken::<Local, V4>::try_from(
583 &LocalToken::encrypt(&test_local_sk, message, None, None).unwrap(),
584 )
585 .unwrap();
586
587 assert!(LocalToken::decrypt(&test_local_sk, &actual_some, None, None).is_ok());
589 assert!(
590 LocalToken::decrypt(&test_local_sk, &actual_some, Some(FOOTER.as_bytes()), None)
591 .is_ok()
592 );
593 assert!(
594 LocalToken::decrypt(&test_local_sk, &actual_none, Some(FOOTER.as_bytes()), None)
595 .is_err()
596 );
597 }
598
599 #[test]
600 fn implicit_none_some_empty_is_same() {
601 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
602 let test_sk = AsymmetricSecretKey::<V4>::from(&TEST_SK_BYTES).unwrap();
603 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
604 let message =
605 b"{\"data\":\"this is a signed message\",\"exp\":\"2019-01-01T00:00:00+00:00\"}";
606 let implicit = b"";
607
608 let actual_some = UntrustedToken::<Public, V4>::try_from(
609 &PublicToken::sign(&test_sk, message, None, Some(implicit)).unwrap(),
610 )
611 .unwrap();
612 let actual_none = UntrustedToken::<Public, V4>::try_from(
613 &PublicToken::sign(&test_sk, message, None, None).unwrap(),
614 )
615 .unwrap();
616 assert_eq!(actual_some, actual_none);
617
618 assert!(PublicToken::verify(&test_pk, &actual_none, None, Some(implicit)).is_ok());
619 assert!(PublicToken::verify(&test_pk, &actual_some, None, None).is_ok());
620
621 let actual_some = UntrustedToken::<Local, V4>::try_from(
622 &LocalToken::encrypt(&test_local_sk, message, None, Some(implicit)).unwrap(),
623 )
624 .unwrap();
625 let actual_none = UntrustedToken::<Local, V4>::try_from(
626 &LocalToken::encrypt(&test_local_sk, message, None, None).unwrap(),
627 )
628 .unwrap();
629 assert!(LocalToken::decrypt(&test_local_sk, &actual_none, None, Some(implicit)).is_ok());
632 assert!(LocalToken::decrypt(&test_local_sk, &actual_some, None, None).is_ok());
633 }
634
635 #[test]
636 fn empty_payload() {
638 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
639 let test_sk = AsymmetricSecretKey::<V4>::from(&TEST_SK_BYTES).unwrap();
640
641 assert_eq!(
642 PublicToken::sign(&test_sk, b"", None, None).unwrap_err(),
643 Error::EmptyPayload
644 );
645 assert_eq!(
646 LocalToken::encrypt(&test_local_sk, b"", None, None).unwrap_err(),
647 Error::EmptyPayload
648 );
649 }
650
651 #[test]
652 fn err_on_modified_footer() {
653 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
654 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
655
656 assert_eq!(
657 PublicToken::verify(
658 &test_pk,
659 &UntrustedToken::<Public, V4>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
660 Some(FOOTER.replace("kid", "mid").as_bytes()),
661 None
662 )
663 .unwrap_err(),
664 Error::TokenValidation
665 );
666 assert_eq!(
667 LocalToken::decrypt(
668 &test_local_sk,
669 &UntrustedToken::<Local, V4>::try_from(VALID_LOCAL_TOKEN).unwrap(),
670 Some(FOOTER.replace("kid", "mid").as_bytes()),
671 None
672 )
673 .unwrap_err(),
674 Error::TokenValidation
675 );
676 }
677
678 #[test]
679 fn err_on_wrong_implicit_assert() {
680 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
681 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
682 assert!(PublicToken::verify(
683 &test_pk,
684 &UntrustedToken::<Public, V4>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
685 Some(FOOTER.as_bytes()),
686 None
687 )
688 .is_ok());
689 assert_eq!(
690 PublicToken::verify(
691 &test_pk,
692 &UntrustedToken::<Public, V4>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
693 Some(FOOTER.as_bytes()),
694 Some(b"WRONG IMPLICIT")
695 )
696 .unwrap_err(),
697 Error::TokenValidation
698 );
699 assert!(LocalToken::decrypt(
700 &test_local_sk,
701 &UntrustedToken::<Local, V4>::try_from(VALID_LOCAL_TOKEN).unwrap(),
702 Some(FOOTER.as_bytes()),
703 None
704 )
705 .is_ok());
706 assert_eq!(
707 LocalToken::decrypt(
708 &test_local_sk,
709 &UntrustedToken::<Local, V4>::try_from(VALID_LOCAL_TOKEN).unwrap(),
710 Some(FOOTER.as_bytes()),
711 Some(b"WRONG IMPLICIT")
712 )
713 .unwrap_err(),
714 Error::TokenValidation
715 );
716 }
717
718 #[test]
719 fn err_on_footer_in_token_none_supplied() {
720 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
721 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
722
723 assert_eq!(
724 PublicToken::verify(
725 &test_pk,
726 &UntrustedToken::<Public, V4>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
727 Some(b""),
728 None
729 )
730 .unwrap_err(),
731 Error::TokenValidation
732 );
733 assert_eq!(
734 LocalToken::decrypt(
735 &test_local_sk,
736 &UntrustedToken::<Local, V4>::try_from(VALID_LOCAL_TOKEN).unwrap(),
737 Some(b""),
738 None
739 )
740 .unwrap_err(),
741 Error::TokenValidation
742 );
743 }
744
745 #[test]
746 fn err_on_no_footer_in_token_some_supplied() {
747 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
748 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
749
750 let split_public = VALID_PUBLIC_TOKEN.split('.').collect::<Vec<&str>>();
751 let invalid_public: String = format!(
752 "{}.{}.{}",
753 split_public[0], split_public[1], split_public[2]
754 );
755
756 let split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
757 let invalid_local: String =
758 format!("{}.{}.{}", split_local[0], split_local[1], split_local[2]);
759
760 assert_eq!(
761 PublicToken::verify(
762 &test_pk,
763 &UntrustedToken::<Public, V4>::try_from(&invalid_public).unwrap(),
764 Some(FOOTER.as_bytes()),
765 None
766 )
767 .unwrap_err(),
768 Error::TokenValidation
769 );
770 assert_eq!(
771 LocalToken::decrypt(
772 &test_local_sk,
773 &UntrustedToken::<Local, V4>::try_from(&invalid_local).unwrap(),
774 Some(FOOTER.as_bytes()),
775 None
776 )
777 .unwrap_err(),
778 Error::TokenValidation
779 );
780 }
781
782 #[test]
783 fn err_on_modified_signature() {
784 let test_pk = AsymmetricPublicKey::<V4>::from(&TEST_PK_BYTES).unwrap();
785
786 let mut split_public = VALID_PUBLIC_TOKEN.split('.').collect::<Vec<&str>>();
787 let mut bad_sig = decode_b64(split_public[2]).unwrap();
788 bad_sig.copy_within(0..32, 32);
789 let tmp = encode_b64(bad_sig).unwrap();
790 split_public[2] = &tmp;
791 let invalid_public: String = format!(
792 "{}.{}.{}.{}",
793 split_public[0], split_public[1], split_public[2], split_public[3]
794 );
795
796 assert_eq!(
797 PublicToken::verify(
798 &test_pk,
799 &UntrustedToken::<Public, V4>::try_from(&invalid_public).unwrap(),
800 Some(FOOTER.as_bytes()),
801 None
802 )
803 .unwrap_err(),
804 Error::TokenValidation
805 );
806 }
807
808 #[test]
809 fn err_on_modified_tag() {
810 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
811
812 let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
813 let mut bad_tag = decode_b64(split_local[2]).unwrap();
814 let tlen = bad_tag.len();
815 bad_tag.copy_within(0..16, tlen - 16);
816 let tmp = encode_b64(bad_tag).unwrap();
817 split_local[2] = &tmp;
818 let invalid_local: String = format!(
819 "{}.{}.{}.{}",
820 split_local[0], split_local[1], split_local[2], split_local[3]
821 );
822
823 assert_eq!(
824 LocalToken::decrypt(
825 &test_local_sk,
826 &UntrustedToken::<Local, V4>::try_from(&invalid_local).unwrap(),
827 Some(FOOTER.as_bytes()),
828 None
829 )
830 .unwrap_err(),
831 Error::TokenValidation
832 );
833 }
834
835 #[test]
836 fn err_on_modified_ciphertext() {
837 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
838
839 let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
840 let mut bad_ct = decode_b64(split_local[2]).unwrap();
841 let ctlen = bad_ct.len();
842 bad_ct.copy_within((ctlen - 16)..ctlen, 24);
843 let tmp = encode_b64(bad_ct).unwrap();
844 split_local[2] = &tmp;
845 let invalid_local: String = format!(
846 "{}.{}.{}.{}",
847 split_local[0], split_local[1], split_local[2], split_local[3]
848 );
849
850 assert_eq!(
851 LocalToken::decrypt(
852 &test_local_sk,
853 &UntrustedToken::<Local, V4>::try_from(&invalid_local).unwrap(),
854 Some(FOOTER.as_bytes()),
855 None
856 )
857 .unwrap_err(),
858 Error::TokenValidation
859 );
860 }
861
862 #[test]
863 fn err_on_modified_nonce() {
864 let test_local_sk = SymmetricKey::<V4>::from(&TEST_LOCAL_SK_BYTES).unwrap();
865
866 let mut split_local = VALID_LOCAL_TOKEN.split('.').collect::<Vec<&str>>();
867 let mut bad_nonce = decode_b64(split_local[2]).unwrap();
868 let nlen = bad_nonce.len();
869 bad_nonce.copy_within((nlen - 24)..nlen, 0);
870 let tmp = encode_b64(bad_nonce).unwrap();
871 split_local[2] = &tmp;
872 let invalid_local: String = format!(
873 "{}.{}.{}.{}",
874 split_local[0], split_local[1], split_local[2], split_local[3]
875 );
876
877 assert_eq!(
878 LocalToken::decrypt(
879 &test_local_sk,
880 &UntrustedToken::<Local, V4>::try_from(&invalid_local).unwrap(),
881 Some(FOOTER.as_bytes()),
882 None
883 )
884 .unwrap_err(),
885 Error::TokenValidation
886 );
887 }
888
889 #[test]
890 fn err_on_invalid_public_secret_key() {
891 let bad_pk = AsymmetricPublicKey::<V4>::from(&[0u8; 32]).unwrap();
892
893 assert_eq!(
894 PublicToken::verify(
895 &bad_pk,
896 &UntrustedToken::<Public, V4>::try_from(VALID_PUBLIC_TOKEN).unwrap(),
897 Some(FOOTER.as_bytes()),
898 None
899 )
900 .unwrap_err(),
901 Error::TokenValidation
902 );
903 }
904
905 #[test]
906 fn err_on_invalid_shared_secret_key() {
907 let bad_local_sk = SymmetricKey::<V4>::from(&[0u8; 32]).unwrap();
908
909 assert_eq!(
910 LocalToken::decrypt(
911 &bad_local_sk,
912 &UntrustedToken::<Local, V4>::try_from(VALID_LOCAL_TOKEN).unwrap(),
913 Some(FOOTER.as_bytes()),
914 None
915 )
916 .unwrap_err(),
917 Error::TokenValidation
918 );
919 }
920}
921
922#[cfg(test)]
923mod test_keys {
924 use super::*;
925 use crate::version4::test_tokens::TEST_SK_BYTES;
926
927 #[test]
928 fn test_symmetric_gen() {
929 let randomv = SymmetricKey::<V4>::generate().unwrap();
930 assert_ne!(randomv.as_bytes(), &[0u8; 32]);
931 }
932
933 #[test]
934 fn test_invalid_sizes() {
935 assert!(AsymmetricSecretKey::<V4>::from(&[1u8; 63]).is_err());
936 assert!(AsymmetricSecretKey::<V4>::from(&TEST_SK_BYTES).is_ok());
937 assert!(AsymmetricSecretKey::<V4>::from(&[1u8; 65]).is_err());
938
939 assert!(AsymmetricPublicKey::<V4>::from(&[1u8; 31]).is_err());
940 assert!(AsymmetricPublicKey::<V4>::from(&[1u8; 32]).is_ok());
941 assert!(AsymmetricPublicKey::<V4>::from(&[1u8; 33]).is_err());
942
943 assert!(SymmetricKey::<V4>::from(&[0u8; 31]).is_err());
944 assert!(SymmetricKey::<V4>::from(&[0u8; 32]).is_ok());
945 assert!(SymmetricKey::<V4>::from(&[0u8; 33]).is_err());
946 }
947
948 #[test]
949 fn try_from_secret_to_public() {
950 let kpv4 = AsymmetricKeyPair::<V4>::generate().unwrap();
951 let pubv4 = AsymmetricPublicKey::<V4>::try_from(&kpv4.secret).unwrap();
952 assert_eq!(pubv4.as_bytes(), kpv4.public.as_bytes());
953 assert_eq!(pubv4, kpv4.public);
954 assert_eq!(&kpv4.secret.as_bytes()[32..], pubv4.as_bytes());
955 }
956
957 #[test]
958 fn test_trait_impls() {
959 let debug = format!("{:?}", SymmetricKey::<V4>::generate().unwrap());
960 assert_eq!(debug, "SymmetricKey {***OMITTED***}");
961
962 let randomv = SymmetricKey::<V4>::generate().unwrap();
963 let zero = SymmetricKey::<V4>::from(&[0u8; V4::LOCAL_KEY]).unwrap();
964 assert_ne!(randomv, zero);
965
966 let debug = format!("{:?}", AsymmetricKeyPair::<V4>::generate().unwrap().secret);
967 assert_eq!(debug, "AsymmetricSecretKey {***OMITTED***}");
968
969 let random1 = AsymmetricKeyPair::<V4>::generate().unwrap();
970 let random2 = AsymmetricKeyPair::<V4>::generate().unwrap();
971 assert_ne!(random1.secret, random2.secret);
972 }
973
974 #[test]
975 fn test_clone() {
976 let sk = SymmetricKey::<V4>::generate().unwrap();
977 assert_eq!(sk, sk.clone());
978
979 let kp = AsymmetricKeyPair::<V4>::generate().unwrap();
980 assert_eq!(kp.secret, kp.secret.clone());
981 assert_eq!(kp.public, kp.public.clone());
982 }
983}