Skip to main content

Mountain/IPC/WindServiceHandlers/Encryption/
Key.rs

1
2//! Machine-stable 256-bit key derivation for AES-256-GCM.
3//!
4//! The key is derived once per process from the host's hardware UUID using
5//! SHA-256: `key = SHA-256("Land-Encryption-v1" ++ machine_id)`.
6//!
7//! If the machine ID cannot be obtained on this system, `DeriveKey` returns
8//! `Err` so callers can surface a meaningful error rather than silently
9//! falling back to a constant key that is identical on every affected machine.
10
11use std::sync::OnceLock;
12
13use ring::digest::{SHA256, digest};
14
15static DERIVED_KEY:OnceLock<Option<[u8; 32]>> = OnceLock::new();
16
17/// Returns the process-wide 256-bit encryption key.
18///
19/// Returns `Err` when no machine-bound seed is available on this system so
20/// that callers can propagate the failure instead of encrypting with a
21/// predictable constant.
22pub fn Fn() -> Result<[u8; 32], String> {
23	DERIVED_KEY
24		.get_or_init(ComputeKey)
25		.ok_or_else(|| "encryption unavailable: machine ID lookup failed".to_string())
26}
27
28fn ComputeKey() -> Option<[u8; 32]> {
29	let MachineId = ReadMachineId()?;
30
31	let Input = format!("Land-Encryption-v1{}", MachineId);
32
33	let Hash = digest(&SHA256, Input.as_bytes());
34
35	let mut Key = [0u8; 32];
36
37	Key.copy_from_slice(Hash.as_ref());
38
39	Some(Key)
40}
41
42fn ReadMachineId() -> Option<String> {
43	#[cfg(target_os = "macos")]
44	{
45		if let Ok(Out) = std::process::Command::new("ioreg")
46			.args(["-rd1", "-c", "IOPlatformExpertDevice"])
47			.output()
48		{
49			let S = String::from_utf8_lossy(&Out.stdout);
50
51			for Line in S.lines() {
52				if Line.contains("IOPlatformUUID") {
53					if let Some(Start) = Line.rfind('"') {
54						let Rest = &Line[..Start];
55
56						if let Some(End) = Rest.rfind('"') {
57							return Some(Rest[End + 1..].to_string());
58						}
59					}
60				}
61			}
62		}
63	}
64
65	#[cfg(target_os = "linux")]
66	{
67		if let Ok(Id) = std::fs::read_to_string("/etc/machine-id") {
68			let Trimmed = Id.trim().to_string();
69
70			if !Trimmed.is_empty() {
71				return Some(Trimmed);
72			}
73		}
74
75		if let Ok(Id) = std::fs::read_to_string("/var/lib/dbus/machine-id") {
76			let Trimmed = Id.trim().to_string();
77
78			if !Trimmed.is_empty() {
79				return Some(Trimmed);
80			}
81		}
82	}
83
84	#[cfg(target_os = "windows")]
85	{
86		use std::process::Command;
87
88		if let Ok(Out) = Command::new("reg")
89			.args(["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"])
90			.output()
91		{
92			let S = String::from_utf8_lossy(&Out.stdout);
93
94			if let Some(Line) = S.lines().find(|L| L.contains("MachineGuid")) {
95				if let Some(Id) = Line.split_whitespace().last() {
96					return Some(Id.to_string());
97				}
98			}
99		}
100	}
101
102	None
103}