1use aes_gcm::aead::Aead;
4use aes_gcm::aead::generic_array::typenum::U12;
5use aes_gcm::{Aes256Gcm, KeyInit, Nonce};
6use base64::Engine;
7use base64::engine::general_purpose::URL_SAFE_NO_PAD;
8use hkdf::Hkdf;
9use p256::PublicKey;
10use p256::ecdh::EphemeralSecret;
11use rand::rngs::OsRng;
12use sha2::Sha256;
13
14const NONCE_LEN: usize = 12;
16
17pub fn generate_keypair() -> (EphemeralSecret, PublicKey) {
19 let secret = EphemeralSecret::random(&mut OsRng);
20 let public = secret.public_key();
21 (secret, public)
22}
23
24pub fn derive_aes_key(shared_secret: &p256::ecdh::SharedSecret) -> [u8; 32] {
26 let hk = Hkdf::<Sha256>::new(None, shared_secret.raw_secret_bytes());
27 let mut key = [0u8; 32];
28 hk.expand(b"j-remote-aes256gcm", &mut key)
29 .expect("HKDF expand should not fail for 32 bytes");
30 key
31}
32
33pub fn encrypt(key: &[u8; 32], plaintext: &[u8]) -> Vec<u8> {
37 use rand::RngCore;
38 let cipher = Aes256Gcm::new(key.into());
39 let mut nonce_bytes = [0u8; NONCE_LEN];
40 OsRng.fill_bytes(&mut nonce_bytes);
41 let nonce: &Nonce<U12> = (&nonce_bytes).into();
42
43 let ciphertext = cipher
44 .encrypt(nonce, plaintext)
45 .expect("AES-GCM encrypt should not fail");
46
47 let mut out = Vec::with_capacity(NONCE_LEN + ciphertext.len());
48 out.extend_from_slice(&nonce_bytes);
49 out.extend_from_slice(&ciphertext);
50 out
51}
52
53pub fn decrypt(key: &[u8; 32], data: &[u8]) -> Result<Vec<u8>, &'static str> {
57 if data.len() < NONCE_LEN + 16 {
58 return Err("密文太短");
59 }
60 let (nonce_bytes, ciphertext) = data.split_at(NONCE_LEN);
61 let cipher = Aes256Gcm::new(key.into());
62 let nonce: &Nonce<U12> = nonce_bytes.into();
63
64 cipher.decrypt(nonce, ciphertext).map_err(|_| "解密失败")
65}
66
67pub fn export_public_key(pk: &PublicKey) -> String {
69 use p256::elliptic_curve::sec1::ToEncodedPoint;
70 let point = pk.to_encoded_point(false); URL_SAFE_NO_PAD.encode(point.as_bytes())
72}
73
74pub fn import_public_key(b64: &str) -> Result<PublicKey, &'static str> {
76 let bytes = URL_SAFE_NO_PAD.decode(b64).map_err(|_| "base64 解码失败")?;
77 PublicKey::from_sec1_bytes(&bytes).map_err(|_| "无效的 P-256 公钥")
78}