use sha2::{Digest, Sha256};
pub fn in_ssh_session() -> bool {
std::env::var("SSH_CONNECTION").is_ok()
|| std::env::var("SSH_TTY").is_ok()
|| std::env::var("SSH_CLIENT").is_ok()
}
pub fn hostname_or_unknown() -> String {
hostname::get()
.ok()
.and_then(|os| os.into_string().ok())
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "unknown".to_string())
}
pub fn self_source_key() -> String {
format!("remote:{}", hostname_or_unknown())
}
pub fn stable_machine_id() -> String {
match raw_machine_source() {
Some(raw) if !raw.is_empty() => hash_short(&raw),
_ => String::new(),
}
}
fn hash_short(raw: &str) -> String {
let digest = Sha256::digest(raw.as_bytes());
let mut s = String::with_capacity(16);
for b in &digest[..8] {
s.push_str(&format!("{:02x}", b));
}
s
}
#[cfg(target_os = "macos")]
fn raw_machine_source() -> Option<String> {
use std::process::Command;
let out = Command::new("ioreg")
.args(["-rd1", "-c", "IOPlatformExpertDevice"])
.output()
.ok()?;
if !out.status.success() {
return None;
}
let text = String::from_utf8_lossy(&out.stdout);
for line in text.lines() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("\"IOPlatformUUID\"") {
if let Some(start) = rest.find('=') {
let after = rest[start + 1..].trim();
let unq = after.trim_matches('"');
if !unq.is_empty() {
return Some(unq.to_string());
}
}
}
}
None
}
#[cfg(target_os = "linux")]
fn raw_machine_source() -> Option<String> {
for path in ["/etc/machine-id", "/var/lib/dbus/machine-id"] {
if let Ok(s) = std::fs::read_to_string(path) {
let v = s.trim();
if !v.is_empty() {
return Some(v.to_string());
}
}
}
None
}
#[cfg(target_os = "windows")]
fn raw_machine_source() -> Option<String> {
use std::process::Command;
let out = Command::new("reg")
.args([
"query",
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
"/v",
"MachineGuid",
])
.output()
.ok()?;
if !out.status.success() {
return None;
}
let text = String::from_utf8_lossy(&out.stdout);
for line in text.lines() {
let trimmed = line.trim();
if trimmed.starts_with("MachineGuid") {
let mut parts = trimmed.split_whitespace();
let _ = parts.next(); let _ = parts.next(); if let Some(v) = parts.next() {
if !v.is_empty() {
return Some(v.to_string());
}
}
}
}
None
}
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
fn raw_machine_source() -> Option<String> {
None
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn hash_is_16_hex_chars() {
let h = hash_short("any-input");
assert_eq!(h.len(), 16);
assert!(h.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn hash_is_deterministic() {
assert_eq!(hash_short("same"), hash_short("same"));
assert_ne!(hash_short("a"), hash_short("b"));
}
fn with_env<F: FnOnce()>(vars: &[(&str, Option<&str>)], f: F) {
let prev: Vec<_> = vars
.iter()
.map(|(k, _)| (k.to_string(), env::var(k).ok()))
.collect();
for (k, v) in vars {
match v {
Some(val) => env::set_var(k, val),
None => env::remove_var(k),
}
}
f();
for (k, v) in prev {
match v {
Some(val) => env::set_var(&k, val),
None => env::remove_var(&k),
}
}
}
#[test]
fn ssh_connection_triggers() {
with_env(
&[
("SSH_CONNECTION", Some("1.2.3.4 22 5.6.7.8 22")),
("SSH_TTY", None),
("SSH_CLIENT", None),
],
|| assert!(in_ssh_session()),
);
}
#[test]
fn ssh_tty_triggers() {
with_env(
&[
("SSH_CONNECTION", None),
("SSH_TTY", Some("/dev/pts/0")),
("SSH_CLIENT", None),
],
|| assert!(in_ssh_session()),
);
}
#[test]
fn ssh_client_triggers() {
with_env(
&[
("SSH_CONNECTION", None),
("SSH_TTY", None),
("SSH_CLIENT", Some("1.2.3.4 22 22")),
],
|| assert!(in_ssh_session()),
);
}
#[test]
fn no_ssh_env_returns_false() {
with_env(
&[
("SSH_CONNECTION", None),
("SSH_TTY", None),
("SSH_CLIENT", None),
],
|| assert!(!in_ssh_session()),
);
}
#[test]
fn self_source_key_matches_push_format() {
let key = self_source_key();
let host = hostname_or_unknown();
assert_eq!(key, format!("remote:{}", host));
}
#[test]
fn self_source_key_has_remote_prefix() {
assert!(self_source_key().starts_with("remote:"));
}
}