Some checks failed
Secrets v3 CI / 检查 (push) Has been cancelled
- Add apps/api, desktop Tauri shell, domain/application/crypto/device-auth/infrastructure-db - Replace desktop-daemon vault integration; drop secrets-core and secrets-mcp* - Ignore apps/desktop/dist and generated Tauri icons; document icon/dist steps in AGENTS.md - Apply rustfmt; fix clippy (collapsible_if, HTTP method as str)
48 lines
1.4 KiB
Rust
48 lines
1.4 KiB
Rust
use aes_gcm::aead::{Aead, KeyInit};
|
|
use aes_gcm::{Aes256Gcm, Nonce};
|
|
use anyhow::{Context, Result};
|
|
use rand::Rng;
|
|
|
|
pub const KEY_CHECK_PLAINTEXT: &[u8] = b"secrets-v3-key-check";
|
|
|
|
pub fn decode_hex(input: &str) -> Result<Vec<u8>> {
|
|
hex::decode(input.trim()).context("invalid hex")
|
|
}
|
|
|
|
pub fn encode_hex(input: &[u8]) -> String {
|
|
hex::encode(input)
|
|
}
|
|
|
|
pub fn extract_key_32(input: &str) -> Result<[u8; 32]> {
|
|
let bytes = decode_hex(input)?;
|
|
let key: [u8; 32] = bytes
|
|
.try_into()
|
|
.map_err(|_| anyhow::anyhow!("expected 32-byte key"))?;
|
|
Ok(key)
|
|
}
|
|
|
|
pub fn encrypt(key: &[u8; 32], plaintext: &[u8]) -> Result<Vec<u8>> {
|
|
let cipher = Aes256Gcm::new_from_slice(key).context("invalid AES-256 key")?;
|
|
let mut nonce_bytes = [0_u8; 12];
|
|
rand::rng().fill_bytes(&mut nonce_bytes);
|
|
let nonce = Nonce::from_slice(&nonce_bytes);
|
|
let mut out = nonce_bytes.to_vec();
|
|
out.extend(
|
|
cipher
|
|
.encrypt(nonce, plaintext)
|
|
.map_err(|_| anyhow::anyhow!("encryption failed"))?,
|
|
);
|
|
Ok(out)
|
|
}
|
|
|
|
pub fn decrypt(key: &[u8; 32], ciphertext: &[u8]) -> Result<Vec<u8>> {
|
|
if ciphertext.len() < 12 {
|
|
anyhow::bail!("ciphertext too short");
|
|
}
|
|
let cipher = Aes256Gcm::new_from_slice(key).context("invalid AES-256 key")?;
|
|
let (nonce, body) = ciphertext.split_at(12);
|
|
cipher
|
|
.decrypt(Nonce::from_slice(nonce), body)
|
|
.map_err(|_| anyhow::anyhow!("decryption failed"))
|
|
}
|