1pub mod error;
2
3use aes_gcm::{
4 aead::{Aead, AeadCore, KeyInit, OsRng},
5 Aes256Gcm, Key,
6};
7use base64::{engine::general_purpose, Engine as _};
8use pqc_kyber::*;
9use rand::rngs::OsRng as RandOsRng;
10
11pub use error::QuantaCipherError;
12
13pub fn generate_keypair() -> Result<(String, String), QuantaCipherError> {
15 let mut rng = RandOsRng;
16 let keys = keypair(&mut rng).map_err(|_| QuantaCipherError::KeygenFailed)?;
17
18 let b64_public = general_purpose::STANDARD.encode(&keys.public);
19 let b64_private = general_purpose::STANDARD.encode(&keys.secret);
20
21 Ok((b64_public, b64_private))
22}
23
24pub fn vault_encrypt(plaintext: &str) -> Result<String, QuantaCipherError> {
26 let mut rng = RandOsRng;
27
28 let keys = keypair(&mut rng).map_err(|_| QuantaCipherError::KeygenFailed)?;
29
30 let (kyber_ciphertext, shared_secret) =
31 encapsulate(&keys.public, &mut rng).map_err(|_| QuantaCipherError::EncapsulationFailed)?;
32
33 let aes_key = Key::<Aes256Gcm>::from_slice(&shared_secret);
34 let cipher = Aes256Gcm::new(aes_key);
35 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
36
37 let aes_ciphertext = cipher
38 .encrypt(&nonce, plaintext.as_bytes())
39 .map_err(|_| QuantaCipherError::EncryptionFailed)?;
40
41 let b64_kyber_ct = general_purpose::STANDARD.encode(kyber_ciphertext);
42 let b64_nonce = general_purpose::STANDARD.encode(nonce.as_slice());
43 let b64_aes_ct = general_purpose::STANDARD.encode(aes_ciphertext);
44
45 Ok(format!("QZ_VAULT_V1:{}:{}:{}", b64_kyber_ct, b64_nonce, b64_aes_ct))
46}
47
48pub fn secure_encrypt(plaintext: &str, public_key_b64: &str) -> Result<String, QuantaCipherError> {
50 let mut rng = RandOsRng;
51
52 let public_key_bytes = general_purpose::STANDARD
53 .decode(public_key_b64)
54 .map_err(QuantaCipherError::DecodeError)?;
55
56 if public_key_bytes.len() != KYBER_PUBLICKEYBYTES {
57 return Err(QuantaCipherError::InvalidPublicKeyLength);
58 }
59
60 let mut pk = [0u8; KYBER_PUBLICKEYBYTES];
61 pk.copy_from_slice(&public_key_bytes);
62
63 let (kyber_ciphertext, shared_secret) =
64 encapsulate(&pk, &mut rng).map_err(|_| QuantaCipherError::EncapsulationFailed)?;
65
66 let aes_key = Key::<Aes256Gcm>::from_slice(&shared_secret);
67 let cipher = Aes256Gcm::new(aes_key);
68 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
69
70 let aes_ciphertext = cipher
71 .encrypt(&nonce, plaintext.as_bytes())
72 .map_err(|_| QuantaCipherError::EncryptionFailed)?;
73
74 let b64_kyber_ct = general_purpose::STANDARD.encode(kyber_ciphertext);
75 let b64_nonce = general_purpose::STANDARD.encode(nonce.as_slice());
76 let b64_aes_ct = general_purpose::STANDARD.encode(aes_ciphertext);
77
78 Ok(format!("QZ_SECURE_V1:{}:{}:{}", b64_kyber_ct, b64_nonce, b64_aes_ct))
79}
80
81pub fn secure_decrypt(ciphertext_payload: &str, private_key_b64: &str) -> Result<String, QuantaCipherError> {
83 if !ciphertext_payload.starts_with("QZ_SECURE_V1:") {
84 return Err(QuantaCipherError::InvalidPayloadFormat);
85 }
86
87 let parts: Vec<&str> = ciphertext_payload.splitn(4, ':').collect();
88 if parts.len() != 4 {
89 return Err(QuantaCipherError::InvalidPayloadFormat);
90 }
91
92 let kyber_ct_bytes = general_purpose::STANDARD.decode(parts[1]).map_err(QuantaCipherError::DecodeError)?;
93 let nonce_bytes = general_purpose::STANDARD.decode(parts[2]).map_err(QuantaCipherError::DecodeError)?;
94 let aes_ct_bytes = general_purpose::STANDARD.decode(parts[3]).map_err(QuantaCipherError::DecodeError)?;
95 let private_key_bytes = general_purpose::STANDARD.decode(private_key_b64).map_err(QuantaCipherError::DecodeError)?;
96
97 if private_key_bytes.len() != KYBER_SECRETKEYBYTES {
98 return Err(QuantaCipherError::InvalidPrivateKeyLength);
99 }
100
101 let mut sk = [0u8; KYBER_SECRETKEYBYTES];
102 sk.copy_from_slice(&private_key_bytes);
103
104 if kyber_ct_bytes.len() != KYBER_CIPHERTEXTBYTES {
105 return Err(QuantaCipherError::InvalidCiphertextLength);
106 }
107
108 let mut kyber_ct = [0u8; KYBER_CIPHERTEXTBYTES];
109 kyber_ct.copy_from_slice(&kyber_ct_bytes);
110
111 let shared_secret = decapsulate(&kyber_ct, &sk).map_err(|_| QuantaCipherError::DecapsulationFailed)?;
112
113 let aes_key = Key::<Aes256Gcm>::from_slice(&shared_secret);
114 let cipher = Aes256Gcm::new(aes_key);
115
116 if nonce_bytes.len() != 12 {
117 return Err(QuantaCipherError::InvalidPayloadFormat); }
119 let nonce = aes_gcm::Nonce::from_slice(&nonce_bytes);
120
121 let plaintext_bytes = cipher
122 .decrypt(nonce, aes_ct_bytes.as_ref())
123 .map_err(|_| QuantaCipherError::DecryptionFailed)?;
124
125 String::from_utf8(plaintext_bytes).map_err(QuantaCipherError::Utf8Error)
126}
127
128pub fn get_version() -> String {
129 "3.0.0-dual-mode-pqc".to_string()
130}