1use base64::engine::general_purpose::URL_SAFE_NO_PAD;
29use base64::Engine;
30use bincode::{Decode, Encode};
31use blake2s_simd::Params;
32use c2_chacha::stream_cipher::{NewStreamCipher, SyncStreamCipher};
33use c2_chacha::ChaCha20;
34use certify::{CertInfo, KeyPair, CA};
35use ed25519_compact::{KeyPair as Ed25519KeyPair, Seed as Ed25519Seed};
36use rand::{rngs::StdRng, RngCore, SeedableRng};
37use serde::{Deserialize, Serialize};
38use zeroize::{Zeroize, Zeroizing};
39
40mod error;
41pub use error::CellarError;
42
43pub const KEY_SIZE: usize = 32;
44pub type Key = Zeroizing<[u8; KEY_SIZE]>;
45
46#[derive(Serialize, Deserialize, Clone, Debug, Zeroize, PartialEq, Eq)]
47#[zeroize(drop)]
48pub struct AuxiliaryData {
49 salt: String,
50 encrypted_seed: String,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
54pub struct CertificatePem {
55 pub cert: String,
56 pub sk: String,
57}
58
59#[derive(Debug, Clone)]
60pub enum KeyType {
61 Password,
62 Keypair,
63 CA(CertInfo),
64 ServerCert((String, String, CertInfo)),
65 ClientCert((String, String, CertInfo)),
66}
67
68impl Default for KeyType {
69 fn default() -> Self {
70 KeyType::Password
71 }
72}
73
74impl AuxiliaryData {
75 pub fn new(salt: &str, seed: &str) -> Self {
76 Self {
77 salt: salt.to_owned(),
78 encrypted_seed: seed.to_owned(),
79 }
80 }
81}
82
83const AUTH_KEY_INFO: &[u8] = b"Auth Key";
84const MASTER_KEY_INFO: &[u8] = b"Master Key";
85const CHACHA20_NONCE_LENGTH: usize = 8;
86
87pub fn random_passphrase() -> String {
89 let mut rng = StdRng::from_entropy();
90 let mut buf = [0u8; 32];
91 rng.fill_bytes(&mut buf);
92 URL_SAFE_NO_PAD.encode(buf)
93}
94
95pub fn init(passphrase: &str) -> Result<AuxiliaryData, CellarError> {
97 let mut rng = StdRng::from_entropy();
98 let mut salt: Key = Zeroizing::new([0u8; KEY_SIZE]);
99 let mut seed: Key = Zeroizing::new([0u8; KEY_SIZE]);
100
101 rng.fill_bytes(salt.as_mut());
102 rng.fill_bytes(seed.as_mut());
103
104 let stretch_key = generate_stretch_key(passphrase, salt.as_ref())?;
105 let auth_key = generate_derived_key(&stretch_key, AUTH_KEY_INFO);
106
107 let mut encrypted_seed = seed.as_ref().to_vec();
108 let nonce = &salt[..CHACHA20_NONCE_LENGTH];
109 let mut cipher = ChaCha20::new_var(auth_key.as_ref(), nonce).unwrap();
110 cipher.apply_keystream(&mut encrypted_seed);
111
112 Ok(AuxiliaryData {
113 salt: URL_SAFE_NO_PAD.encode(salt.as_ref()),
114 encrypted_seed: URL_SAFE_NO_PAD.encode(&encrypted_seed),
115 })
116}
117
118pub fn generate_master_key(passphrase: &str, aux: &AuxiliaryData) -> Result<Key, CellarError> {
120 let salt = URL_SAFE_NO_PAD.decode(&aux.salt)?;
121 let mut seed = URL_SAFE_NO_PAD.decode(&aux.encrypted_seed)?;
122
123 let stretch_key = generate_stretch_key(passphrase, &salt)?;
125
126 let auth_key = generate_derived_key(&stretch_key, AUTH_KEY_INFO);
128
129 let partial_key = generate_derived_key(&stretch_key, MASTER_KEY_INFO);
131
132 let nonce = &salt[..CHACHA20_NONCE_LENGTH];
134 let mut cipher = ChaCha20::new_var(auth_key.as_ref(), nonce).unwrap();
135 cipher.apply_keystream(&mut seed);
136
137 let master_key = generate_derived_key(&partial_key, &seed);
139 Ok(master_key)
140}
141
142pub fn generate_app_key(
144 passphrase: &str,
145 aux: &AuxiliaryData,
146 info: &[u8],
147 key_type: KeyType,
148) -> Result<Vec<u8>, CellarError> {
149 let master_key = generate_master_key(passphrase, aux)?;
150 let app_key = generate_derived_key(&master_key, info);
151 generate_by_key_type(app_key, key_type)
152}
153
154pub fn generate_app_key_by_path(
156 parent_key: Key,
157 path: &str,
158 key_type: KeyType,
159) -> Result<Vec<u8>, CellarError> {
160 let app_key = path.split('/').fold(parent_key, |acc, part| {
162 generate_derived_key(&acc, part.as_bytes())
163 });
164 generate_by_key_type(app_key, key_type)
165}
166
167pub fn to_base64(key: &[u8]) -> String {
168 URL_SAFE_NO_PAD.encode(key)
169}
170
171pub fn from_base64(key: &str) -> Result<Key, CellarError> {
172 let data = URL_SAFE_NO_PAD.decode(key)?;
173
174 let key: [u8; KEY_SIZE] = data
175 .try_into()
176 .map_err(|_e| CellarError::InvalidKey("Cannot convert the Vec<u8> to Key".to_owned()))?;
177 Ok(key.into())
178}
179
180pub fn as_parent_key(app_key: &[u8]) -> Key {
182 let mut key = Zeroizing::new([0u8; KEY_SIZE]);
183 key.copy_from_slice(app_key);
184 key
185}
186
187#[inline]
188fn generate_stretch_key(passphrase: &str, salt: &[u8]) -> Result<Key, CellarError> {
189 let hash = argon2::hash_raw(passphrase.as_bytes(), salt, &argon2::Config::default())?;
190 let mut key = Zeroizing::new([0u8; KEY_SIZE]);
191 key.copy_from_slice(&hash);
192 Ok(key)
193}
194
195#[inline]
196fn generate_derived_key(stretch_key: &Key, info: &[u8]) -> Key {
197 let mut params = Params::new();
198 params.key(stretch_key.as_ref());
199 let hash = params.hash(info).as_array().to_owned();
200 let mut key = Zeroizing::new([0u8; KEY_SIZE]);
201 key.copy_from_slice(&hash);
202 key
203}
204
205#[inline]
206fn generate_by_key_type(app_key: Key, key_type: KeyType) -> Result<Vec<u8>, CellarError> {
207 match key_type {
208 KeyType::Password => Ok(Vec::from(&app_key[..])),
209 KeyType::Keypair => {
210 let keypair = Ed25519KeyPair::from_seed(Ed25519Seed::from_slice(app_key.as_ref())?);
211 let mut ret = keypair.sk.to_der();
212 ret.extend_from_slice(&keypair.pk.to_der());
213 Ok(ret)
214 }
215 KeyType::CA(info) => {
216 let key = Ed25519KeyPair::from_seed(Ed25519Seed::from_slice(app_key.as_ref())?);
217 let keypair = KeyPair::from_der(&key.sk.to_der())?;
218 let ca = info.ca_cert(Some(keypair))?;
219 let cert_pem = CertificatePem {
220 cert: ca.serialize_pem().unwrap(),
221 sk: ca.serialize_private_key_pem(),
222 };
223 Ok(bincode::encode_to_vec(
224 &cert_pem,
225 bincode::config::standard(),
226 )?)
227 }
228 KeyType::ServerCert((ca_pem, key_pem, info)) => {
229 let ca = CA::load(&ca_pem, &key_pem)?;
230 let key = Ed25519KeyPair::from_seed(Ed25519Seed::from_slice(app_key.as_ref())?);
231 let keypair = KeyPair::from_der(&key.sk.to_der())?;
232 let cert = info.server_cert(Some(keypair))?;
233 let (server_cert_pem, server_key_pem) = ca.sign_cert(&cert)?;
234 let cert_pem = CertificatePem {
235 cert: server_cert_pem,
236 sk: server_key_pem,
237 };
238 Ok(bincode::encode_to_vec(
239 &cert_pem,
240 bincode::config::standard(),
241 )?)
242 }
243 KeyType::ClientCert((ca_pem, key_pem, info)) => {
244 let ca = CA::load(&ca_pem, &key_pem)?;
245 let key = Ed25519KeyPair::from_seed(Ed25519Seed::from_slice(app_key.as_ref())?);
246 let keypair = KeyPair::from_der(&key.sk.to_der())?;
247 let cert = info.client_cert(Some(keypair))?;
248 let (server_cert_pem, server_key_pem) = ca.sign_cert(&cert)?;
249 let cert_pem = CertificatePem {
250 cert: server_cert_pem,
251 sk: server_key_pem,
252 };
253 Ok(bincode::encode_to_vec(
254 &cert_pem,
255 bincode::config::standard(),
256 )?)
257 }
258 }
259}
260
261#[cfg(test)]
262extern crate quickcheck;
263#[cfg(test)]
264#[macro_use(quickcheck)]
265extern crate quickcheck_macros;
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270 use certify::CertSigAlgo;
271
272 #[test]
273 fn same_passphrase_produce_same_keys() -> Result<(), CellarError> {
274 let passphrase = "hello";
275 let aux = init(passphrase)?;
276 let app_key = generate_app_key(passphrase, &aux, b"user@gmail.com", KeyType::Password)?;
277 let app_key1 = generate_app_key(passphrase, &aux, b"user1@gmail.com", KeyType::Password)?;
278
279 assert_ne!(app_key1, app_key);
280
281 let app_key2 = generate_app_key(passphrase, &aux, b"user@gmail.com", KeyType::Password)?;
282 assert_eq!(app_key2, app_key);
283 Ok(())
284 }
285
286 #[test]
287 fn generate_usable_keypair_should_work() -> Result<(), CellarError> {
288 let passphrase = "hello";
289 let aux = init(passphrase)?;
290 let key = generate_app_key(passphrase, &aux, b"user@gmail.com", KeyType::Keypair)?;
291
292 let keypair = Ed25519KeyPair::from_der(&key[..48]).unwrap();
293 let content = b"hello world";
294 let sig = keypair.sk.sign(content, None);
295 let verified = keypair.pk.verify(content, &sig);
296 assert!(verified.is_ok());
297 Ok(())
298 }
299
300 #[test]
301 fn generate_key_by_path_should_work() -> Result<(), CellarError> {
302 let passphrase = "hello";
303 let aux = init(passphrase)?;
304 let key = generate_master_key(passphrase, &aux)?;
305 let parent_key = generate_app_key(passphrase, &aux, b"apps", KeyType::Password)?;
306 let app_key = generate_app_key_by_path(key, "apps/my/awesome/key", KeyType::Password)?;
307 let app_key1 = generate_app_key_by_path(
308 as_parent_key(&parent_key),
309 "my/awesome/key",
310 KeyType::Password,
311 )?;
312 assert_eq!(app_key, app_key1);
313 Ok(())
314 }
315
316 #[test]
317 fn generate_ca_cert_should_work() -> Result<(), CellarError> {
318 let info = CertInfo::new(
319 vec!["localhost"],
320 Vec::<String>::new(),
321 "US",
322 "Domain Inc.",
323 "Domain CA",
324 None,
325 CertSigAlgo::ED25519,
326 );
327 let (_, parent_key, cert_pem) = generate_ca(info.clone())?;
328
329 CA::load(&cert_pem.cert, &cert_pem.sk)?;
330
331 let cert1 = generate_app_key_by_path(
332 as_parent_key(&parent_key),
333 "localhost/ca",
334 KeyType::CA(info),
335 )?;
336
337 let (cert_pem1, _) =
338 bincode::decode_from_slice::<CertificatePem, _>(&cert1, bincode::config::standard())?;
339
340 assert_eq!(&cert_pem.sk, &cert_pem1.sk);
341 assert_eq!(&cert_pem.cert, &cert_pem1.cert);
342
343 Ok(())
344 }
345
346 #[test]
347 fn generate_server_cert_should_work() -> Result<(), CellarError> {
348 let info = CertInfo::new(
349 vec!["localhost"],
350 Vec::<String>::new(),
351 "US",
352 "Domain Inc.",
353 "Domain CA",
354 None,
355 CertSigAlgo::ED25519,
356 );
357 let (key, parent_key, cert_pem) = generate_ca(info)?;
358
359 let info = CertInfo::new(
360 vec!["localhost"],
361 Vec::<String>::new(),
362 "US",
363 "Domain Inc.",
364 "GRPC Server",
365 Some(365),
366 CertSigAlgo::ED25519,
367 );
368 let cert = generate_app_key_by_path(
369 key,
370 "apps/localhost/server",
371 KeyType::ServerCert((cert_pem.cert.clone(), cert_pem.sk.clone(), info.clone())),
372 )?;
373
374 let cert1 = generate_app_key_by_path(
375 as_parent_key(&parent_key),
376 "localhost/server",
377 KeyType::ServerCert((cert_pem.cert.clone(), cert_pem.sk.clone(), info)),
378 )?;
379
380 println!("{}\n{}", &cert_pem.cert, &cert_pem.sk);
381
382 let (cert_pem, _) =
383 bincode::decode_from_slice::<CertificatePem, _>(&cert, bincode::config::standard())?;
384 println!("{}\n{}", &cert_pem.cert, &cert_pem.sk);
385
386 assert_eq!(cert, cert1);
387
388 Ok(())
389 }
390
391 #[test]
392 fn generate_client_cert_should_work() -> Result<(), CellarError> {
393 let info = CertInfo::new(
394 vec!["localhost"],
395 Vec::<String>::new(),
396 "US",
397 "Domain Inc.",
398 "Domain CA",
399 None,
400 CertSigAlgo::ED25519,
401 );
402 let (key, parent_key, ca_cert_pem) = generate_ca(info)?;
403
404 println!("CA cert:\n\n{}\n{}", &ca_cert_pem.cert, &ca_cert_pem.sk);
405
406 let info = CertInfo::new(
407 vec!["localhost"],
408 Vec::<String>::new(),
409 "US",
410 "Domain Inc.",
411 "GRPC Server",
412 Some(365),
413 CertSigAlgo::ED25519,
414 );
415 let server_cert = generate_app_key_by_path(
416 as_parent_key(&parent_key),
417 "localhost/server",
418 KeyType::ServerCert((ca_cert_pem.cert.clone(), ca_cert_pem.sk.clone(), info)),
419 )?;
420
421 let (server_cert_pem, _) = bincode::decode_from_slice::<CertificatePem, _>(
422 &server_cert,
423 bincode::config::standard(),
424 )?;
425 println!(
426 "Server cert:\n\n{}\n{}",
427 &server_cert_pem.cert, &server_cert_pem.sk
428 );
429
430 let info = CertInfo::new(
431 vec!["localhost"],
432 Vec::<String>::new(),
433 "US",
434 "android",
435 "abcd1234",
436 Some(180),
437 CertSigAlgo::ED25519,
438 );
439 let client_cert = generate_app_key_by_path(
440 key,
441 "apps/localhost/client/abcd1234",
442 KeyType::ClientCert((
443 ca_cert_pem.cert.clone(),
444 ca_cert_pem.sk.clone(),
445 info.clone(),
446 )),
447 )?;
448
449 let cert1 = generate_app_key_by_path(
450 as_parent_key(&parent_key),
451 "localhost/client/abcd1234",
452 KeyType::ClientCert((ca_cert_pem.cert.clone(), ca_cert_pem.sk, info)),
453 )?;
454
455 let (client_cert_pem, _) = bincode::decode_from_slice::<CertificatePem, _>(
456 &client_cert,
457 bincode::config::standard(),
458 )?;
459 println!(
460 "Client cert:\n\n{}\n{}",
461 &client_cert_pem.cert, &client_cert_pem.sk
462 );
463
464 assert_eq!(client_cert, cert1);
465
466 Ok(())
467 }
468
469 #[ignore]
470 #[quickcheck]
471 fn prop_same_passphrase_produce_same_keys(passphrase: String, app_info: String) -> bool {
472 let aux = init(&passphrase).unwrap();
473 let app_key =
474 generate_app_key(&passphrase, &aux, app_info.as_bytes(), KeyType::Password).unwrap();
475
476 app_key
477 == generate_app_key(&passphrase, &aux, app_info.as_bytes(), KeyType::Password).unwrap()
478 }
479
480 fn generate_ca(info: CertInfo) -> Result<(Key, Vec<u8>, CertificatePem), CellarError> {
481 let passphrase = "hello";
482 let aux = init(passphrase)?;
483 let key = generate_master_key(passphrase, &aux)?;
484 let parent_key = generate_app_key(passphrase, &aux, b"apps", KeyType::Password)?;
485
486 let cert = generate_app_key_by_path(key.clone(), "apps/localhost/ca", KeyType::CA(info))?;
487 let (cert_pem, _) =
488 bincode::decode_from_slice::<CertificatePem, _>(&cert, bincode::config::standard())?;
489 Ok((key, parent_key, cert_pem))
490 }
491}