use crate::error_sanitize::sanitize_error;
use crate::windows_hello;
use crate::windows_raii::{
EnumStateGuard, HLocalGuard, KeyHandle, KeyNameBufferGuard, ProviderHandle,
};
use std::os::windows::io::FromRawHandle;
use windows::core::{HSTRING, PCWSTR};
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Security::Authorization::ConvertSidToStringSidW;
use windows::Win32::Security::Cryptography::{
NCryptCreatePersistedKey, NCryptDeleteKey, NCryptEnumKeys, NCryptExportKey, NCryptFinalizeKey,
NCryptFreeObject, NCryptKeyName, NCryptOpenKey, NCryptOpenStorageProvider, NCryptSetProperty,
NCryptSignHash, CERT_KEY_SPEC, NCRYPT_ALLOW_SIGNING_FLAG, NCRYPT_FLAGS, NCRYPT_KEY_HANDLE,
NCRYPT_PROV_HANDLE, NCRYPT_SILENT_FLAG,
};
use windows::Win32::Security::{GetTokenInformation, TokenUser, TOKEN_QUERY, TOKEN_USER};
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
use windows::Win32::System::TpmBaseServices::{Tbsi_GetDeviceInfo, TPM_DEVICE_INFO};
pub const MS_PLATFORM_CRYPTO_PROVIDER: &str = "Microsoft Platform Crypto Provider";
pub const MS_NGC_KEY_STORAGE_PROVIDER: &str = "Microsoft Passport Key Storage Provider";
const KEY_PREFIX_TPM_BASE: &str = "tauri_se_tpm_";
const NGC_DOMAIN: &str = "tauri_se";
fn sanitize_app_id(app_id: &str) -> String {
app_id
.chars()
.map(|c| match c {
'.' | '/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
_ => c,
})
.collect()
}
fn tpm_key_prefix(app_id: &str) -> String {
format!("{}{}_", KEY_PREFIX_TPM_BASE, sanitize_app_id(app_id))
}
fn tpm_key_name(app_id: &str, key_name: &str) -> String {
format!("{}{}", tpm_key_prefix(app_id), key_name)
}
fn ngc_key_marker(app_id: &str) -> String {
format!("/{}/{}/", NGC_DOMAIN, sanitize_app_id(app_id))
}
fn ngc_key_name(sid: &str, app_id: &str, key_name: &str) -> String {
format!(
"{}//{}/{}/{}",
sid,
NGC_DOMAIN,
sanitize_app_id(app_id),
key_name
)
}
const NCRYPT_PIN_CACHE_IS_GESTURE_REQUIRED_PROPERTY: &str = "PinCacheIsGestureRequired";
const NCRYPT_WINDOW_HANDLE_PROPERTY: &str = "HWND Handle";
const NCRYPT_USE_CONTEXT_PROPERTY: &str = "Use Context";
fn is_windows_10_1607_or_higher() -> crate::Result<bool> {
let version = winver::WindowsVersion::detect().ok_or_else(|| {
crate::Error::Io(std::io::Error::other("Failed to detect Windows version"))
})?;
let windows_10_1607_min = winver::WindowsVersion::new(10, 0, 14393);
Ok(version >= windows_10_1607_min)
}
fn require_windows_10_1607() -> crate::Result<()> {
match is_windows_10_1607_or_higher() {
Ok(true) => Ok(()),
Ok(false) => Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"This plugin requires Windows 10 version 1607 (build 14393) or higher",
))),
Err(e) => Err(e),
}
}
const TPM_VERSION_20: u32 = 2;
const TPM_IFTYPE_UNKNOWN: u32 = 0;
const TPM_IFTYPE_1: u32 = 1; const TPM_IFTYPE_TRUSTZONE: u32 = 2; const TPM_IFTYPE_HW: u32 = 3; const TPM_IFTYPE_EMULATOR: u32 = 4; const TPM_IFTYPE_SPB: u32 = 5;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TpmType {
None,
Firmware,
Integrated,
Discrete,
}
#[derive(Debug, Clone)]
pub struct TpmInfo {
pub present: bool,
pub version: Option<u32>,
pub interface_type: Option<u32>,
pub tpm_type: TpmType,
#[allow(dead_code)]
pub implementation_revision: Option<u32>,
}
pub fn detect_tpm() -> TpmInfo {
unsafe {
let mut device_info = TPM_DEVICE_INFO {
structVersion: 1,
tpmVersion: 0,
tpmInterfaceType: 0,
tpmImpRevision: 0,
};
let size = std::mem::size_of::<TPM_DEVICE_INFO>() as u32;
let result = Tbsi_GetDeviceInfo(size, &mut device_info as *mut _ as *mut std::ffi::c_void);
if result == 0 {
let interface_type = device_info.tpmInterfaceType;
let tpm_type = match interface_type {
TPM_IFTYPE_HW | TPM_IFTYPE_SPB | TPM_IFTYPE_1 => TpmType::Discrete,
TPM_IFTYPE_TRUSTZONE => TpmType::Integrated,
TPM_IFTYPE_UNKNOWN => TpmType::Firmware, TPM_IFTYPE_EMULATOR => TpmType::None, _ => TpmType::Firmware, };
TpmInfo {
present: true,
version: Some(device_info.tpmVersion),
interface_type: Some(interface_type),
tpm_type,
implementation_revision: Some(device_info.tpmImpRevision),
}
} else {
TpmInfo {
present: false,
version: None,
interface_type: None,
tpm_type: TpmType::None,
implementation_revision: None,
}
}
}
}
pub fn is_tpm2_available() -> bool {
let info = detect_tpm();
info.present && info.version.map(|v| v >= TPM_VERSION_20).unwrap_or(false)
}
pub fn get_secure_element_capabilities() -> crate::models::CheckSecureElementSupportResponse {
let info = detect_tpm();
let emulated = info
.interface_type
.map(|t| t == TPM_IFTYPE_EMULATOR)
.unwrap_or(false);
let has_tpm2 = info.present && info.version.map(|v| v >= TPM_VERSION_20).unwrap_or(false);
if !has_tpm2 {
return crate::models::CheckSecureElementSupportResponse {
discrete: false,
integrated: false,
firmware: false,
emulated,
strongest: crate::models::SecureElementBacking::None,
can_enforce_biometric_only: false,
};
}
let (discrete, integrated, firmware) = match info.tpm_type {
TpmType::Discrete => (true, false, false),
TpmType::Integrated => (false, true, false), TpmType::Firmware => (false, false, true),
TpmType::None => (false, false, false),
};
let strongest = if discrete {
crate::models::SecureElementBacking::Discrete
} else if integrated {
crate::models::SecureElementBacking::Integrated
} else if firmware {
crate::models::SecureElementBacking::Firmware
} else {
crate::models::SecureElementBacking::None
};
crate::models::CheckSecureElementSupportResponse {
discrete,
integrated,
firmware,
emulated,
strongest,
can_enforce_biometric_only: can_enforce_biometric_only(),
}
}
pub fn open_provider() -> crate::Result<ProviderHandle> {
open_provider_by_name(MS_PLATFORM_CRYPTO_PROVIDER)
}
pub fn open_ngc_provider() -> crate::Result<ProviderHandle> {
open_provider_by_name(MS_NGC_KEY_STORAGE_PROVIDER)
}
fn open_provider_by_name(provider_name: &str) -> crate::Result<ProviderHandle> {
require_windows_10_1607()?;
unsafe {
let mut provider = NCRYPT_PROV_HANDLE::default();
let provider_name_h = HSTRING::from(provider_name);
NCryptOpenStorageProvider(&mut provider, PCWSTR(provider_name_h.as_ptr()), 0).map_err(
|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to open {}: {}", provider_name, e),
&format!("Failed to open {}", provider_name),
)))
},
)?;
Ok(ProviderHandle(provider))
}
}
pub fn is_tpm_available(provider: &ProviderHandle) -> bool {
!provider.0.is_invalid() && is_tpm2_available()
}
pub enum KeyProviderType {
Ngc,
Tpm,
}
pub fn open_key_auto(app_id: &str, key_name: &str) -> crate::Result<(KeyHandle, KeyProviderType)> {
if let Ok(sid) = get_current_user_sid() {
let ngc_full_name = ngc_key_name(&sid, app_id, key_name);
if let Ok(ngc_provider) = open_ngc_provider() {
if let Ok(key) = open_key_internal(&ngc_provider, &ngc_full_name) {
return Ok((key, KeyProviderType::Ngc));
}
}
}
let tpm_full_name = tpm_key_name(app_id, key_name);
let tpm_provider = open_provider()?;
let key = open_key_internal(&tpm_provider, &tpm_full_name)?;
Ok((key, KeyProviderType::Tpm))
}
fn open_key_internal(provider: &ProviderHandle, full_name: &str) -> crate::Result<KeyHandle> {
unsafe {
let mut key_handle = NCRYPT_KEY_HANDLE::default();
let key_name_h = HSTRING::from(full_name);
NCryptOpenKey(
provider.0,
&mut key_handle,
PCWSTR(key_name_h.as_ptr()),
CERT_KEY_SPEC(0),
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to open key '{}': {}", full_name, e),
"Failed to open key",
)))
})?;
Ok(KeyHandle(key_handle))
}
}
fn key_exists(app_id: &str, key_name: &str) -> bool {
if let Ok(sid) = get_current_user_sid() {
let ngc_full_name = ngc_key_name(&sid, app_id, key_name);
if let Ok(ngc_provider) = open_ngc_provider() {
if open_key_internal(&ngc_provider, &ngc_full_name).is_ok() {
return true;
}
}
}
let tpm_full_name = tpm_key_name(app_id, key_name);
if let Ok(tpm_provider) = open_provider() {
if open_key_internal(&tpm_provider, &tpm_full_name).is_ok() {
return true;
}
}
false
}
pub fn create_key(
app_id: &str,
key_name: &str,
auth_mode: &crate::models::AuthenticationMode,
) -> crate::Result<KeyHandle> {
if key_exists(app_id, key_name) {
return Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!("A key with name '{}' already exists", key_name),
)));
}
match auth_mode {
crate::models::AuthenticationMode::BiometricOnly => {
Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"biometricOnly authentication mode is not supported on Windows. Use 'pinOrBiometric' instead.",
)))
}
crate::models::AuthenticationMode::PinOrBiometric => {
if !windows_hello::is_windows_hello_configured() {
return Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Windows Hello is not configured or enrolled on this system. Please set up Windows Hello (PIN or biometric) in Windows Settings before creating keys with authentication.",
)));
}
create_ngc_key(app_id, key_name)
}
crate::models::AuthenticationMode::None => create_tpm_key(app_id, key_name),
}
}
fn get_current_user_sid() -> crate::Result<String> {
unsafe {
let mut raw_token = HANDLE::default();
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut raw_token).map_err(|e| {
crate::Error::Io(std::io::Error::other(format!(
"Failed to open process token: {}",
e
)))
})?;
let token_handle = std::os::windows::io::OwnedHandle::from_raw_handle(raw_token.0);
let mut size_needed: u32 = 0;
let token = HANDLE(std::os::windows::io::AsRawHandle::as_raw_handle(
&token_handle,
));
let _ = GetTokenInformation(token, TokenUser, None, 0, &mut size_needed);
let mut buffer = vec![0u8; size_needed as usize];
let result = GetTokenInformation(
token,
TokenUser,
Some(buffer.as_mut_ptr() as *mut _),
size_needed,
&mut size_needed,
);
result.map_err(|e| {
crate::Error::Io(std::io::Error::other(format!(
"Failed to get token information: {}",
e
)))
})?;
let token_user = &*(buffer.as_ptr() as *const TOKEN_USER);
let mut sid_string_ptr = windows::core::PWSTR::null();
ConvertSidToStringSidW(token_user.User.Sid, &mut sid_string_ptr).map_err(|e| {
crate::Error::Io(std::io::Error::other(format!(
"Failed to convert SID to string: {}",
e
)))
})?;
let mut sid_string_guard = HLocalGuard::new();
sid_string_guard.set_from_pwstr(sid_string_ptr);
let sid_string = sid_string_ptr.to_string().map_err(|e| {
crate::Error::Io(std::io::Error::other(format!(
"Failed to read SID string: {}",
e
)))
})?;
Ok(sid_string)
}
}
fn create_ngc_key(app_id: &str, key_name: &str) -> crate::Result<KeyHandle> {
let provider = open_ngc_provider()?;
let sid = get_current_user_sid()?;
let full_name = ngc_key_name(&sid, app_id, key_name);
unsafe {
let mut key_handle = NCRYPT_KEY_HANDLE::default();
let key_name_h = HSTRING::from(full_name.as_str());
let algorithm = HSTRING::from("ECDSA_P256");
NCryptCreatePersistedKey(
provider.0,
&mut key_handle,
PCWSTR(algorithm.as_ptr()),
PCWSTR(key_name_h.as_ptr()),
CERT_KEY_SPEC(0),
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("NCryptCreatePersistedKey failed for '{}': {}", key_name, e),
"Failed to create key",
)))
})?;
let key = KeyHandle(key_handle);
let usage: u32 = NCRYPT_ALLOW_SIGNING_FLAG;
let key_usage_property = HSTRING::from("Key Usage");
if let Err(e) = NCryptSetProperty(
key.0.into(),
PCWSTR(key_usage_property.as_ptr()),
&usage.to_le_bytes(),
NCRYPT_FLAGS(0),
) {
return Err(crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to set key usage: {}", e),
"Failed to set key usage",
))));
}
if let Err(e) = NCryptFinalizeKey(key.0, NCRYPT_SILENT_FLAG) {
return Err(crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("NCryptFinalizeKey failed: {}", e),
"Failed to finalize key",
))));
}
Ok(key)
}
}
fn create_tpm_key(app_id: &str, key_name: &str) -> crate::Result<KeyHandle> {
let provider = open_provider()?;
if !is_tpm_available(&provider) {
return Err(crate::Error::Io(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"TPM 2.0 not available on this system",
)));
}
let full_name = tpm_key_name(app_id, key_name);
unsafe {
let mut key_handle = NCRYPT_KEY_HANDLE::default();
let key_name_h = HSTRING::from(full_name.as_str());
let algorithm = HSTRING::from("ECDSA_P256");
NCryptCreatePersistedKey(
provider.0,
&mut key_handle,
PCWSTR(algorithm.as_ptr()),
PCWSTR(key_name_h.as_ptr()),
CERT_KEY_SPEC(0),
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to create TPM key '{}': {}", key_name, e),
"Failed to create key",
)))
})?;
let key = KeyHandle(key_handle);
let usage_bytes = NCRYPT_ALLOW_SIGNING_FLAG.to_le_bytes();
let key_usage_property = HSTRING::from("Key Usage");
if let Err(e) = NCryptSetProperty(
key.0.into(),
PCWSTR(key_usage_property.as_ptr()),
usage_bytes.as_slice(),
NCRYPT_FLAGS(0),
) {
return Err(crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to set key usage: {}", e),
"Failed to set key usage",
))));
}
if let Err(e) = NCryptFinalizeKey(key.0, NCRYPT_FLAGS(0)) {
return Err(crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to finalize key: {}", e),
"Failed to finalize key",
))));
}
Ok(key)
}
}
pub fn export_public_key(key: &KeyHandle) -> crate::Result<Vec<u8>> {
unsafe {
let blob_type = HSTRING::from("ECCPUBLICBLOB");
let mut blob_size: u32 = 0;
NCryptExportKey(
key.0,
None,
PCWSTR(blob_type.as_ptr()),
None,
None,
&mut blob_size,
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to get public key size: {}", e),
"Failed to get public key size",
)))
})?;
let mut blob = vec![0u8; blob_size as usize];
NCryptExportKey(
key.0,
None,
PCWSTR(blob_type.as_ptr()),
None,
Some(&mut blob),
&mut blob_size,
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to export public key: {}", e),
"Failed to export public key",
)))
})?;
if blob.len() < 72 {
return Err(crate::Error::Io(std::io::Error::other(sanitize_error(
&format!(
"Public key blob too small: {} bytes, expected at least 72",
blob.len()
),
"Failed to export public key",
))));
}
let x = &blob[8..40];
let y = &blob[40..72];
let mut x962 = Vec::with_capacity(65);
x962.push(0x04);
x962.extend_from_slice(x);
x962.extend_from_slice(y);
Ok(x962)
}
}
pub fn sha256_hash(data: &[u8]) -> crate::Result<[u8; 32]> {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(data);
let result = hasher.finalize();
Ok(result.into())
}
pub fn sign_hash(key: &KeyHandle, hash: &[u8]) -> crate::Result<Vec<u8>> {
sign_hash_internal(key, hash)
}
pub fn sign_hash_with_window(
key: &KeyHandle,
hash: &[u8],
hwnd: Option<isize>,
) -> crate::Result<Vec<u8>> {
unsafe {
if let Some(handle) = hwnd {
let hwnd_property = HSTRING::from(NCRYPT_WINDOW_HANDLE_PROPERTY);
let hwnd_bytes = handle.to_ne_bytes();
if let Err(e) = NCryptSetProperty(
key.0.into(),
PCWSTR(hwnd_property.as_ptr()),
&hwnd_bytes,
NCRYPT_FLAGS(0),
) {
#[cfg(debug_assertions)]
eprintln!(
"Warning: Failed to set window handle property for Windows Hello dialog: {}",
e
);
}
} else {
#[cfg(debug_assertions)]
eprintln!("Warning: No HWND available for Windows Hello dialog parenting");
}
let context_property = HSTRING::from(NCRYPT_USE_CONTEXT_PROPERTY);
let context_bytes: Vec<u8> = "Authenticate to sign data"
.encode_utf16()
.flat_map(|c| c.to_le_bytes())
.collect();
if let Err(e) = NCryptSetProperty(
key.0.into(),
PCWSTR(context_property.as_ptr()),
&context_bytes,
NCRYPT_FLAGS(0),
) {
#[cfg(debug_assertions)]
eprintln!(
"Warning: Failed to set context message for Windows Hello dialog: {}",
e
);
}
let gesture_property = HSTRING::from(NCRYPT_PIN_CACHE_IS_GESTURE_REQUIRED_PROPERTY);
let gesture_required: u32 = 1;
let gesture_bytes = gesture_required.to_le_bytes();
NCryptSetProperty(
key.0.into(),
PCWSTR(gesture_property.as_ptr()),
&gesture_bytes,
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to set gesture required property: {}", e),
"Failed to configure per-operation authentication",
)))
})?;
}
sign_hash_internal(key, hash)
}
fn sign_hash_internal(key: &KeyHandle, hash: &[u8]) -> crate::Result<Vec<u8>> {
unsafe {
let mut sig_size: u32 = 0;
NCryptSignHash(key.0, None, hash, None, &mut sig_size, NCRYPT_FLAGS(0)).map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to get signature size: {}", e),
"Failed to sign",
)))
})?;
let mut signature = vec![0u8; sig_size as usize];
NCryptSignHash(
key.0,
None,
hash,
Some(&mut signature),
&mut sig_size,
NCRYPT_FLAGS(0),
)
.map_err(|e| {
crate::Error::Io(std::io::Error::other(sanitize_error(
&format!("Failed to sign: {}", e),
"Failed to sign",
)))
})?;
signature.truncate(sig_size as usize);
let der_signature = crate::der::raw_ecdsa_to_der(&signature)?;
Ok(der_signature)
}
}
pub fn delete_key(key: KeyHandle) -> crate::Result<bool> {
unsafe {
let handle = key.take();
match NCryptDeleteKey(handle, 0u32) {
Ok(_) => Ok(true),
Err(_) => {
let _ = NCryptFreeObject(handle.into());
Ok(false)
}
}
}
}
fn extract_ngc_key_name<'a>(full_name: &'a str, app_id: &str) -> Option<&'a str> {
let marker = ngc_key_marker(app_id);
full_name
.find(&marker)
.map(|pos| &full_name[pos + marker.len()..])
}
fn list_keys_from_provider(
provider: &ProviderHandle,
prefix: &str,
filter_key_name: Option<&str>,
filter_public_key: Option<&str>,
) -> crate::Result<Vec<crate::models::KeyInfo>> {
use base64::Engine;
let mut keys = Vec::new();
unsafe {
let mut enum_state_guard = EnumStateGuard::new();
let scope = PCWSTR::null();
loop {
let mut key_name_ptr: *mut NCryptKeyName = std::ptr::null_mut();
let result = NCryptEnumKeys(
provider.0,
scope,
&mut key_name_ptr,
enum_state_guard.as_mut_ptr(),
NCRYPT_SILENT_FLAG,
);
if result.is_err() {
break;
}
if key_name_ptr.is_null() {
break;
}
let key_name_guard = KeyNameBufferGuard::new(key_name_ptr);
let key_name_struct = key_name_guard.as_ref();
let key_name_wide = key_name_struct.pszName;
if !key_name_wide.is_null() {
let full_name = key_name_wide.to_string().unwrap_or_default();
if let Some(user_name) = full_name.strip_prefix(prefix) {
let name_matches = filter_key_name.map(|f| user_name == f).unwrap_or(true);
if name_matches {
if let Ok(key_handle) = open_key_internal(provider, &full_name) {
if let Ok(public_key_bytes) = export_public_key(&key_handle) {
let public_key_b64 = base64::engine::general_purpose::STANDARD
.encode(&public_key_bytes);
let pk_matches = filter_public_key
.map(|f| public_key_b64 == f)
.unwrap_or(true);
if pk_matches {
keys.push(crate::models::KeyInfo {
key_name: user_name.to_string(),
public_key: public_key_b64,
});
}
}
}
}
}
}
}
}
Ok(keys)
}
fn list_ngc_keys(
provider: &ProviderHandle,
app_id: &str,
filter_key_name: Option<&str>,
filter_public_key: Option<&str>,
) -> crate::Result<Vec<crate::models::KeyInfo>> {
use base64::Engine;
let mut keys = Vec::new();
unsafe {
let mut enum_state_guard = EnumStateGuard::new();
let scope = PCWSTR::null();
loop {
let mut key_name_ptr: *mut NCryptKeyName = std::ptr::null_mut();
let result = NCryptEnumKeys(
provider.0,
scope,
&mut key_name_ptr,
enum_state_guard.as_mut_ptr(),
NCRYPT_SILENT_FLAG,
);
if result.is_err() {
break;
}
if key_name_ptr.is_null() {
break;
}
let key_name_guard = KeyNameBufferGuard::new(key_name_ptr);
let key_name_struct = key_name_guard.as_ref();
let key_name_wide = key_name_struct.pszName;
if !key_name_wide.is_null() {
let full_name = key_name_wide.to_string().unwrap_or_default();
if let Some(user_name) = extract_ngc_key_name(&full_name, app_id) {
let name_matches = filter_key_name.map(|f| user_name == f).unwrap_or(true);
if name_matches {
if let Ok(key_handle) = open_key_internal(provider, &full_name) {
if let Ok(public_key_bytes) = export_public_key(&key_handle) {
let public_key_b64 = base64::engine::general_purpose::STANDARD
.encode(&public_key_bytes);
let pk_matches = filter_public_key
.map(|f| public_key_b64 == f)
.unwrap_or(true);
if pk_matches {
keys.push(crate::models::KeyInfo {
key_name: user_name.to_string(),
public_key: public_key_b64,
});
}
}
}
}
}
}
}
}
Ok(keys)
}
pub fn list_keys(
app_id: &str,
filter_key_name: Option<&str>,
filter_public_key: Option<&str>,
) -> crate::Result<Vec<crate::models::KeyInfo>> {
let mut all_keys = Vec::new();
let tpm_prefix = tpm_key_prefix(app_id);
if let Ok(tpm_provider) = open_provider() {
if let Ok(tpm_keys) = list_keys_from_provider(
&tpm_provider,
&tpm_prefix,
filter_key_name,
filter_public_key,
) {
all_keys.extend(tpm_keys);
}
}
if let Ok(ngc_provider) = open_ngc_provider() {
if let Ok(ngc_keys) =
list_ngc_keys(&ngc_provider, app_id, filter_key_name, filter_public_key)
{
all_keys.extend(ngc_keys);
}
}
Ok(all_keys)
}
pub fn can_enforce_biometric_only() -> bool {
false
}
#[cfg(target_os = "windows")]
pub fn hwnd_from_raw(handle: raw_window_handle::RawWindowHandle) -> Option<isize> {
match handle {
raw_window_handle::RawWindowHandle::Win32(h) => Some(h.hwnd.get()),
_ => None,
}
}