1use chacha20poly1305::{XChaCha20Poly1305, Key, XNonce, KeyInit, AeadInPlace};
2use rand::rngs::OsRng;
3use rand::RngCore;
4
5use crate::claims::Claims;
6use crate::crypto::{QVSigningKey, sha3_256, sign};
7use crate::error::{QVError, QVResult};
8use crate::mutation::{MutationChain, certify_entropy};
9use crate::crypto::SuiteId;
10use crate::token::{QVRawToken, QVTokenHeader, TokenType};
11
12pub struct IssueParams<'a> {
14 pub suite: SuiteId,
15 pub token_type: TokenType,
16 pub ttl_secs: u32,
17 pub device_fp: Option<[u8; 32]>,
18 pub claims: &'a Claims,
19 pub signing_key: &'a QVSigningKey,
20 pub encrypt_key: &'a [u8; 32], pub chain: &'a mut MutationChain,
22}
23
24pub fn issue_token(p: IssueParams<'_>) -> QVResult<QVRawToken> {
26 let (shell, msg) = prepare_unsigned(
27 p.suite, p.token_type, p.ttl_secs, p.device_fp,
28 p.claims, p.encrypt_key, p.chain,
29 )?;
30 let signature = sign(p.signing_key, &msg)?;
31 Ok(QVRawToken { header: shell.header, encrypted_payload: shell.encrypted_payload, signature })
32}
33
34pub fn prepare_unsigned(
38 suite: crate::crypto::SuiteId,
39 token_type: TokenType,
40 ttl_secs: u32,
41 device_fp_opt: Option<[u8; 32]>,
42 claims: &crate::claims::Claims,
43 encrypt_key: &[u8; 32],
44 chain: &mut MutationChain,
45) -> QVResult<(QVRawToken, Vec<u8>)> {
46 let issued_at = std::time::SystemTime::now()
47 .duration_since(std::time::UNIX_EPOCH)
48 .map_err(|e| QVError::SerializationError(e.to_string()))?
49 .as_micros() as u64;
50
51 let mut nonce = [0u8; 32];
52 OsRng.fill_bytes(&mut nonce);
53 certify_entropy(&nonce)?;
54
55 let device_fp = device_fp_opt.unwrap_or_else(|| sha3_256(&nonce));
56
57 let _ = chain.advance();
58 let mutation_ctr = chain.current_counter();
59
60 let plaintext = claims.encode()?;
61 let encrypted_payload = encrypt_payload(&plaintext, encrypt_key, &nonce)?;
62
63 let header = QVTokenHeader {
64 suite, token_type, issued_at, ttl: ttl_secs,
65 nonce, device_fp, mutation_ctr,
66 };
67
68 let shell = QVRawToken { header, encrypted_payload, signature: Vec::new() };
69 let msg = shell.signed_bytes();
70 Ok((shell, msg))
71}
72
73#[cfg(feature = "falcon")]
75pub fn issue_token_falcon512(
76 token_type: TokenType,
77 ttl_secs: u32,
78 device_fp: Option<[u8; 32]>,
79 claims: &crate::claims::Claims,
80 signing_key: &crate::falcon::falcon512::QVFalcon512SigningKey,
81 encrypt_key: &[u8; 32],
82 chain: &mut MutationChain,
83) -> QVResult<QVRawToken> {
84 let (shell, msg) = prepare_unsigned(
85 crate::crypto::SuiteId::Falcon512, token_type, ttl_secs,
86 device_fp, claims, encrypt_key, chain,
87 )?;
88 let signature = crate::falcon::falcon512::sign(signing_key, &msg)?;
89 Ok(QVRawToken { header: shell.header, encrypted_payload: shell.encrypted_payload, signature })
90}
91
92#[cfg(feature = "falcon")]
94pub fn issue_token_falcon1024(
95 token_type: TokenType,
96 ttl_secs: u32,
97 device_fp: Option<[u8; 32]>,
98 claims: &crate::claims::Claims,
99 signing_key: &crate::falcon::falcon1024::QVFalcon1024SigningKey,
100 encrypt_key: &[u8; 32],
101 chain: &mut MutationChain,
102) -> QVResult<QVRawToken> {
103 let (shell, msg) = prepare_unsigned(
104 crate::crypto::SuiteId::Falcon1024, token_type, ttl_secs,
105 device_fp, claims, encrypt_key, chain,
106 )?;
107 let signature = crate::falcon::falcon1024::sign(signing_key, &msg)?;
108 Ok(QVRawToken { header: shell.header, encrypted_payload: shell.encrypted_payload, signature })
109}
110
111fn encrypt_payload(plaintext: &[u8], key: &[u8; 32], token_nonce: &[u8; 32]) -> QVResult<Vec<u8>> {
114 let digest = sha3_256(token_nonce);
115 let xchacha_nonce = XNonce::from_slice(&digest[..24]);
116 let cipher = XChaCha20Poly1305::new(Key::from_slice(key));
117
118 let mut buf = plaintext.to_vec();
119 cipher
120 .encrypt_in_place(xchacha_nonce, b"", &mut buf)
121 .map_err(|_| QVError::DecryptionFailed)?;
122 Ok(buf)
123}
124
125pub fn decrypt_payload(ciphertext: &[u8], key: &[u8; 32], token_nonce: &[u8; 32]) -> QVResult<Vec<u8>> {
127 let digest = sha3_256(token_nonce);
128 let xchacha_nonce = XNonce::from_slice(&digest[..24]);
129 let cipher = XChaCha20Poly1305::new(Key::from_slice(key));
130
131 let mut buf = ciphertext.to_vec();
132 cipher
133 .decrypt_in_place(xchacha_nonce, b"", &mut buf)
134 .map_err(|_| QVError::DecryptionFailed)?;
135 Ok(buf)
136}