astraguard 1.1.7

Official AstraGuard SDK - license validation, HWID binding, anti-debug, and offline cache for Rust applications
Documentation
//! Hardware ID derivation for license binding.
//!
//! On Windows: reads `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid` and
//! hashes it with SHA-256 to produce a stable, non-reversible identifier.
//! On other platforms (or when the registry read fails): falls back to a
//! SHA-256 of the hostname + username.

use sha2::{Digest, Sha256};

/// Returns a stable machine identifier as a 32-character hex string
/// (the first 16 bytes of SHA-256).
pub fn get() -> String {
    let raw = get_raw_identifier();
    let hash = Sha256::digest(raw.as_bytes());
    hex::encode(&hash[..16])
}

fn get_raw_identifier() -> String {
    #[cfg(target_os = "windows")]
    {
        if let Some(guid) = read_machine_guid_windows() {
            return guid;
        }
    }

    let hostname = std::env::var("COMPUTERNAME")
        .or_else(|_| std::env::var("HOSTNAME"))
        .unwrap_or_else(|_| "unknown-host".to_string());
    let username = std::env::var("USERNAME")
        .or_else(|_| std::env::var("USER"))
        .unwrap_or_else(|_| "unknown-user".to_string());
    format!("{}_{}", hostname, username)
}

#[cfg(target_os = "windows")]
fn read_machine_guid_windows() -> Option<String> {
    use crate::security::strings;
    use winapi::shared::minwindef::HKEY;
    use winapi::um::winnt::KEY_READ;
    use winapi::um::winreg::{
        RegCloseKey, RegGetValueW, RegOpenKeyExW, HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ,
    };

    let subkey_str = strings::decode(&strings::REG_MACHINE_GUID_PATH);
    let subkey_clean = subkey_str.trim_end_matches('\0');
    let value_str = strings::decode(&strings::REG_MACHINE_GUID_KEY);

    let subkey_wide: Vec<u16> = subkey_clean.encode_utf16().chain(Some(0)).collect();
    let value_wide: Vec<u16> = value_str.encode_utf16().chain(Some(0)).collect();

    // SAFETY:
    // - `subkey_wide` and `value_wide` are null-terminated UTF-16 strings
    //   allocated on the stack and alive for the duration of the unsafe block.
    // - `RegOpenKeyExW` writes a valid HKEY into `hkey` on success (ret == 0)
    //   or leaves `hkey` null on failure; we guard with the `ret != 0` check.
    // - `buf` is a [u16; 64] stack buffer; `buf_size` is initialised to
    //   `size_of_val(&buf)` (the byte count), which is what `RegGetValueW`
    //   expects for a `REG_SZ` value.
    // - `RegCloseKey` is always called to release the handle.
    unsafe {
        let mut hkey: HKEY = std::ptr::null_mut();
        let ret = RegOpenKeyExW(
            HKEY_LOCAL_MACHINE,
            subkey_wide.as_ptr(),
            0,
            KEY_READ,
            &mut hkey,
        );
        if ret != 0 {
            return None;
        }

        let mut buf = [0u16; 64];
        // Correct byte count: 64 u16 values * 2 bytes each = 128 bytes.
        let mut buf_size = std::mem::size_of_val(&buf) as u32;
        let ret = RegGetValueW(
            hkey,
            std::ptr::null(),
            value_wide.as_ptr(),
            RRF_RT_REG_SZ,
            std::ptr::null_mut(),
            buf.as_mut_ptr() as *mut _,
            &mut buf_size,
        );
        RegCloseKey(hkey);

        if ret != 0 {
            return None;
        }

        let guid_wide: Vec<u16> = buf.iter().take_while(|&&c| c != 0).copied().collect();
        let guid = String::from_utf16_lossy(&guid_wide);
        if guid.is_empty() {
            None
        } else {
            Some(guid)
        }
    }
}