Skip to main content

quantacipher_core/
lib.rs

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
13/// Generates a base64 encoded Kyber-1024 keypair (publicKey, privateKey)
14pub 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
24/// Encrypts data in Vault Mode (ephemeral keypair, permanently sealed)
25pub 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
48/// Encrypts data using the recipient's Base64 Kyber public key
49pub 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
81/// Decrypts data using the user's Base64 Kyber private key
82pub 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); // Nonce length
118    }
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}