Skip to main content

Mountain/IPC/WindServiceHandlers/Encryption/
Encrypt.rs

1
2//! `encryption:encrypt(value: string) -> string`
3//!
4//! Encrypts a plaintext string with AES-256-GCM and returns a base64-encoded
5//! `<12-byte nonce><ciphertext+tag>` blob that `encryption:decrypt` can
6//! reverse. Called by VS Code's `EncryptionMainService` to store extension
7//! secrets and auth tokens safely at rest.
8
9use base64::{Engine, engine::general_purpose::STANDARD as B64};
10use ring::{
11	aead::{AES_256_GCM, Aad, LessSafeKey, Nonce, UnboundKey},
12	rand::{SecureRandom, SystemRandom},
13};
14use serde_json::{Value, json};
15
16use crate::dev_log;
17use super::Key::Fn as DeriveKey;
18
19pub async fn Fn(Arguments:Vec<Value>) -> Result<Value, String> {
20	let Plaintext = Arguments.first().and_then(|V| V.as_str()).unwrap_or("").to_string();
21
22	if Plaintext.is_empty() {
23		return Ok(json!(""));
24	}
25
26	let KeyBytes = DeriveKey().map_err(|E| format!("encryption:encrypt unavailable - {E}"))?;
27
28	let UnboundK = UnboundKey::new(&AES_256_GCM, &KeyBytes).map_err(|E| format!("encrypt key: {E:?}"))?;
29
30	let Key = LessSafeKey::new(UnboundK);
31
32	let Rng = SystemRandom::new();
33
34	let mut NonceBytes = [0u8; 12];
35
36	Rng.fill(&mut NonceBytes).map_err(|E| format!("encrypt rng: {E:?}"))?;
37
38	let NonceVal = Nonce::assume_unique_for_key(NonceBytes);
39
40	let mut Data = Plaintext.into_bytes();
41
42	Key.seal_in_place_append_tag(NonceVal, Aad::empty(), &mut Data)
43		.map_err(|E| format!("encrypt seal: {E:?}"))?;
44
45	let mut Out = NonceBytes.to_vec();
46
47	Out.extend_from_slice(&Data);
48
49	dev_log!(
50		"encryption",
51		"encryption:encrypt {} bytes → {} bytes",
52		Out.len() - 12 - 16,
53		Out.len()
54	);
55
56	Ok(json!(B64.encode(&Out)))
57}