rustfs_crypto/
license_token.rs1use rsa::{
16 Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey,
17 pkcs8::{DecodePrivateKey, DecodePublicKey},
18 pss::{BlindedSigningKey, Signature, VerifyingKey},
19 sha2::Sha256,
20 signature::{RandomizedSigner, Verifier},
21 traits::PublicKeyParts,
22};
23use serde::{Deserialize, Serialize};
24use std::io::{Error, ErrorKind, Result};
25
26#[derive(Serialize, Deserialize, Debug, Default, Clone)]
27pub struct Token {
28 pub name: String, pub expired: u64, }
31
32#[deprecated(note = "use sign_license_token for signed license issuance")]
37pub fn gencode(token: &Token, key: &str) -> Result<String> {
38 let data = serde_json::to_vec(token)?;
39 let mut rng = rand::rng();
40 let public_key = RsaPublicKey::from_public_key_pem(key).map_err(Error::other)?;
41 let encrypted_data = public_key.encrypt(&mut rng, Pkcs1v15Encrypt, &data).map_err(Error::other)?;
42 Ok(base64_simd::URL_SAFE_NO_PAD.encode_to_string(&encrypted_data))
43}
44
45#[deprecated(note = "use parse_signed_license_token or parse_license_with_public_key for signed license verification")]
50pub fn parse(token: &str, key: &str) -> Result<Token> {
51 let encrypted_data = base64_simd::URL_SAFE_NO_PAD
52 .decode_to_vec(token.as_bytes())
53 .map_err(Error::other)?;
54 let private_key = RsaPrivateKey::from_pkcs8_pem(key).map_err(Error::other)?;
55 let decrypted_data = private_key.decrypt(Pkcs1v15Encrypt, &encrypted_data).map_err(Error::other)?;
56 serde_json::from_slice(&decrypted_data).map_err(Error::other)
57}
58
59pub fn sign_license_token(token: &Token, private_key_pem: &str) -> Result<String> {
64 let payload = serde_json::to_vec(token)?;
65 let mut rng = rand::rng();
66 let private_key = RsaPrivateKey::from_pkcs8_pem(private_key_pem).map_err(Error::other)?;
67 let signing_key = BlindedSigningKey::<Sha256>::new(private_key);
68 let signature: Signature = signing_key.try_sign_with_rng(&mut rng, &payload).map_err(Error::other)?;
69 let signature: Box<[u8]> = signature.into();
70
71 let mut signed_payload = Vec::with_capacity(signature.as_ref().len() + payload.len());
72 signed_payload.extend_from_slice(signature.as_ref());
73 signed_payload.extend_from_slice(&payload);
74
75 Ok(base64_simd::URL_SAFE_NO_PAD.encode_to_string(&signed_payload))
76}
77
78pub fn parse_signed_license_token(token: &str, public_key_pem: &str) -> Result<Token> {
80 let signed_payload = base64_simd::URL_SAFE_NO_PAD
81 .decode_to_vec(token.as_bytes())
82 .map_err(Error::other)?;
83 let public_key = RsaPublicKey::from_public_key_pem(public_key_pem).map_err(Error::other)?;
84 let signature_len = public_key.size();
85
86 if signed_payload.len() <= signature_len {
87 return Err(Error::new(ErrorKind::InvalidData, "license token is missing signed payload"));
88 }
89
90 let (signature, payload) = signed_payload.split_at(signature_len);
91 let signature = Signature::try_from(signature).map_err(Error::other)?;
92 let verifying_key = VerifyingKey::<Sha256>::new(public_key);
93 verifying_key.verify(payload, &signature).map_err(Error::other)?;
94
95 serde_json::from_slice(payload).map_err(Error::other)
96}
97
98pub fn parse_license_with_public_key(license: &str, public_key: &str) -> Result<Token> {
99 parse_signed_license_token(license, public_key)
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use rsa::{
106 RsaPrivateKey,
107 pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
108 };
109 use std::time::{SystemTime, UNIX_EPOCH};
110
111 #[test]
112 fn test_sign_license_token_and_parse_signed_license_token() {
113 let mut rng = rand::rng();
114 let bits = 2048;
115 let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key");
116 let public_key = RsaPublicKey::from(&private_key);
117
118 let private_key_pem = private_key
119 .to_pkcs8_pem(LineEnding::LF)
120 .expect("failed to encode private key pem");
121 let public_key_pem = public_key
122 .to_public_key_pem(LineEnding::LF)
123 .expect("failed to encode public key pem");
124
125 let token = Token {
126 name: "test_app".to_string(),
127 expired: SystemTime::now()
128 .duration_since(UNIX_EPOCH)
129 .expect("system clock before unix epoch")
130 .as_secs()
131 + 3600, };
133
134 let encoded = sign_license_token(&token, &private_key_pem).expect("Failed to encode token");
135
136 let decoded = parse_signed_license_token(&encoded, &public_key_pem).expect("Failed to decode token");
137
138 assert_eq!(token.name, decoded.name);
139 assert_eq!(token.expired, decoded.expired);
140 }
141
142 #[test]
143 #[allow(deprecated)]
144 fn test_legacy_gencode_and_parse_roundtrip() {
145 let mut rng = rand::rng();
146 let bits = 2048;
147 let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key");
148 let public_key = RsaPublicKey::from(&private_key);
149
150 let private_key_pem = private_key
151 .to_pkcs8_pem(LineEnding::LF)
152 .expect("failed to encode private key pem");
153 let public_key_pem = public_key
154 .to_public_key_pem(LineEnding::LF)
155 .expect("failed to encode public key pem");
156
157 let token = Token {
158 name: "test_app".to_string(),
159 expired: SystemTime::now()
160 .duration_since(UNIX_EPOCH)
161 .expect("system clock before unix epoch")
162 .as_secs()
163 + 3600,
164 };
165
166 let encoded = gencode(&token, &public_key_pem).expect("Failed to encode token");
167 let decoded = parse(&encoded, &private_key_pem).expect("Failed to decode token");
168
169 assert_eq!(token.name, decoded.name);
170 assert_eq!(token.expired, decoded.expired);
171 }
172
173 #[test]
174 fn test_parse_signed_license_token_rejects_tampered_payload() {
175 let mut rng = rand::rng();
176 let private_key = RsaPrivateKey::new(&mut rng, 2048).expect("Failed to generate private key");
177 let public_key = RsaPublicKey::from(&private_key);
178 let private_key_pem = private_key
179 .to_pkcs8_pem(LineEnding::LF)
180 .expect("failed to encode private key pem");
181 let public_key_pem = public_key
182 .to_public_key_pem(LineEnding::LF)
183 .expect("failed to encode public key pem");
184 let token = Token {
185 name: "test_app".to_string(),
186 expired: SystemTime::now()
187 .duration_since(UNIX_EPOCH)
188 .expect("system clock before unix epoch")
189 .as_secs()
190 + 3600,
191 };
192
193 let encoded = sign_license_token(&token, &private_key_pem).expect("Failed to encode token");
194 let mut signed_payload = base64_simd::URL_SAFE_NO_PAD
195 .decode_to_vec(encoded.as_bytes())
196 .expect("Failed to decode signed payload");
197 let last_byte = signed_payload.last_mut().expect("Signed payload should not be empty");
198 *last_byte ^= 0x01;
199 let tampered = base64_simd::URL_SAFE_NO_PAD.encode_to_string(&signed_payload);
200
201 let result = parse_signed_license_token(&tampered, &public_key_pem);
202
203 assert!(result.is_err());
204 }
205
206 #[test]
207 fn test_parse_signed_license_token_rejects_invalid_token() {
208 let mut rng = rand::rng();
209 let private_key = RsaPrivateKey::new(&mut rng, 2048).expect("Failed to generate private key");
210 let public_key = RsaPublicKey::from(&private_key);
211 let public_key_pem = public_key
212 .to_public_key_pem(LineEnding::LF)
213 .expect("failed to encode public key pem");
214
215 let invalid_token = "invalid_base64_token";
216 let result = parse_signed_license_token(invalid_token, &public_key_pem);
217
218 assert!(result.is_err());
219 }
220
221 #[test]
222 fn test_sign_license_token_with_invalid_signing_key() {
223 let token = Token {
224 name: "test_app".to_string(),
225 expired: SystemTime::now()
226 .duration_since(UNIX_EPOCH)
227 .expect("system clock before unix epoch")
228 .as_secs()
229 + 3600, };
231
232 let invalid_key = "invalid_private_key";
233 let result = sign_license_token(&token, invalid_key);
234
235 assert!(result.is_err());
236 }
237}