mirror of
https://github.com/Portabase/agent.git
synced 2026-09-10 01:57:10 +00:00
feat: add decrypt_json_gcm for encrypted status storages
This commit is contained in:
@@ -86,3 +86,27 @@ async fn encrypt_stream_starts_with_json_header_line() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use crate::utils::file::decrypt_json_gcm;
|
||||
|
||||
const VECTOR_KEY_B64: &str = "BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwc=";
|
||||
const VECTOR_ENVELOPE_B64: &str = "AQIDBAUGBwgJCgsM4AfK9w7I2A7UDzMvpJaScfnUYAGDZgPWT5Chrp1pdzMPPQVpNjb6ZEiFea9YdWVFv1UEo9RGmmf+zYUv4I3gE4SU/SBrMwkCHEpJGJOzJtK3tSpJmzLVX3+7EeUNwp4qjZheL8p0pe1x6dRUtx3JmLjz1W/RhWd6zuReDItv6+0jg4CaPOHvFXBreaGNCTRslxbImD+lFBoEOvw8lsbH";
|
||||
const VECTOR_PLAINTEXT: &str = "[{\"id\":\"11111111-1111-1111-1111-111111111111\",\"config\":{\"bucket\":\"my-bucket\",\"accessKeyId\":\"AKIA\",\"secretAccessKey\":\"s3cr3t\"},\"provider\":\"s3\"}]";
|
||||
|
||||
#[test]
|
||||
fn decrypt_json_gcm_decrypts_node_vector() {
|
||||
let plaintext = decrypt_json_gcm(VECTOR_ENVELOPE_B64, VECTOR_KEY_B64).unwrap();
|
||||
assert_eq!(String::from_utf8(plaintext).unwrap(), VECTOR_PLAINTEXT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decrypt_json_gcm_rejects_wrong_key() {
|
||||
let wrong_key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||
assert!(decrypt_json_gcm(VECTOR_ENVELOPE_B64, wrong_key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decrypt_json_gcm_rejects_short_input() {
|
||||
// 8 bytes base64 -> shorter than nonce(12)+tag(16)
|
||||
assert!(decrypt_json_gcm("AAAAAAAAAAA=", VECTOR_KEY_B64).is_err());
|
||||
}
|
||||
|
||||
@@ -175,3 +175,32 @@ pub async fn decrypt_file_stream_gcm(
|
||||
writer.flush().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrypt a base64 `nonce(12) ‖ ciphertext ‖ tag(16)` AES-256-GCM envelope
|
||||
/// using the raw master key (STANDARD base64). Returns the plaintext bytes.
|
||||
pub fn decrypt_json_gcm(ciphertext_b64: &str, master_key_b64: &str) -> Result<Vec<u8>> {
|
||||
let master_key_bytes = general_purpose::STANDARD
|
||||
.decode(master_key_b64)
|
||||
.map_err(|_| anyhow::anyhow!("Invalid base64 master key"))?;
|
||||
|
||||
let data = general_purpose::STANDARD
|
||||
.decode(ciphertext_b64)
|
||||
.map_err(|_| anyhow::anyhow!("Invalid base64 ciphertext"))?;
|
||||
|
||||
if data.len() < 12 + 16 {
|
||||
return Err(anyhow::anyhow!("Ciphertext too short"));
|
||||
}
|
||||
|
||||
let key = Key::<Aes256Gcm>::try_from(master_key_bytes.as_slice())
|
||||
.map_err(|_| anyhow::anyhow!("Invalid AES-256 key length"))?;
|
||||
let cipher = Aes256Gcm::new(&key);
|
||||
|
||||
let nonce = Nonce::try_from(&data[..12])
|
||||
.map_err(|_| anyhow::anyhow!("Invalid nonce length"))?;
|
||||
|
||||
let plaintext = cipher
|
||||
.decrypt(&nonce, &data[12..])
|
||||
.map_err(|e| anyhow::anyhow!("AES-GCM decryption failed: {:?}", e))?;
|
||||
|
||||
Ok(plaintext)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user