diff --git a/Cargo.toml b/Cargo.toml index d52a1ee..ba97d83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustls-cng" -version = "0.5.3" +version = "0.6.0" authors = ["Dmitry Pankratov "] description = "Windows CNG API bridge for rustls" license = "MIT/Apache-2.0" diff --git a/examples/client.rs b/examples/client.rs index e445967..2776efa 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -34,7 +34,7 @@ fn get_chain( let context = contexts .first() .ok_or_else(|| anyhow::Error::msg("No client cert"))?; - let key = context.acquire_key()?; + let key = context.acquire_key(true)?; let signing_key = CngSigningKey::new(key)?; let chain = context .as_chain_der()? diff --git a/examples/server.rs b/examples/server.rs index 385ffb0..1869ff7 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -63,7 +63,7 @@ impl ResolvesServerCert for ServerCertResolver { // attempt to acquire a private key and construct CngSigningKey let (context, key) = contexts.into_iter().find_map(|ctx| { - let key = ctx.acquire_key().ok()?; + let key = ctx.acquire_key(true).ok()?; if let Some(ref pin) = self.pin { key.set_pin(pin).ok()?; } diff --git a/src/cert.rs b/src/cert.rs index 4eb4a02..984ea1e 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -57,11 +57,15 @@ impl CertContext { unsafe { &*self.0.inner() } } - /// Attempt to silently acquire a CNG private key from this context. - pub fn acquire_key(&self) -> Result { + /// Attempt to acquire a CNG private key from this context. + /// The `silent` parameter indicates whether to suppress the user prompts. + pub fn acquire_key(&self, silent: bool) -> Result { let mut handle = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE::default(); let mut key_spec = CERT_KEY_SPEC::default(); - let flags = CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG; + + let flags = + if silent { CRYPT_ACQUIRE_SILENT_FLAG } else { 0 } | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG; + unsafe { let result = CryptAcquireCertificatePrivateKey( self.inner(), @@ -72,7 +76,9 @@ impl CertContext { ptr::null_mut(), ) != 0; if result { - Ok(NCryptKey::new_owned(handle)) + let mut key = NCryptKey::new_owned(handle); + key.set_silent(silent); + Ok(key) } else { Err(CngError::from_win32_error()) } @@ -137,7 +143,9 @@ impl CertContext { for (index, element) in elements.iter().enumerate() { if index != 0 { - if 0 != ((**element).TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) { + if 0 != ((**element).TrustStatus.dwInfoStatus + & CERT_TRUST_IS_SELF_SIGNED) + { break; } } diff --git a/src/key.rs b/src/key.rs index cdff351..3eaaa5c 100644 --- a/src/key.rs +++ b/src/key.rs @@ -66,22 +66,31 @@ impl Drop for InnerKey { /// CNG private key wrapper #[derive(Clone, Debug)] -pub struct NCryptKey(Arc); +pub struct NCryptKey { + inner: Arc, + silent: bool, +} impl NCryptKey { /// Create an owned instance which frees the underlying handle automatically pub fn new_owned(handle: NCRYPT_KEY_HANDLE) -> Self { - NCryptKey(Arc::new(InnerKey::Owned(handle))) + NCryptKey { + inner: Arc::new(InnerKey::Owned(handle)), + silent: true, + } } /// Create a borrowed instance which doesn't free the key handle pub fn new_borrowed(handle: NCRYPT_KEY_HANDLE) -> Self { - NCryptKey(Arc::new(InnerKey::Borrowed(handle))) + NCryptKey { + inner: Arc::new(InnerKey::Borrowed(handle)), + silent: true, + } } /// Return an inner CNG key handle pub fn inner(&self) -> NCRYPT_KEY_HANDLE { - self.0.inner() + self.inner.inner() } fn get_string_property(&self, property: PCWSTR) -> Result { @@ -160,6 +169,11 @@ impl NCryptKey { CngError::from_hresult(result) } + /// Enable or disable silent key operations without prompting the user. The default value is 'true'. + pub fn set_silent(&mut self, silent: bool) { + self.silent = silent; + } + /// Sign a given digest with this key. The `hash` slice must be 32, 48 or 64 bytes long. pub fn sign(&self, hash: &[u8], padding: SignaturePadding) -> Result> { unsafe { @@ -189,6 +203,7 @@ impl NCryptKey { }; let mut result = 0; + let dwflags = flag | if self.silent { NCRYPT_SILENT_FLAG } else { 0 }; CngError::from_hresult(NCryptSignHash( self.inner(), @@ -198,7 +213,7 @@ impl NCryptKey { ptr::null_mut(), 0, &mut result, - NCRYPT_SILENT_FLAG | flag, + dwflags, ))?; let mut signature = vec![0u8; result as usize]; @@ -211,7 +226,7 @@ impl NCryptKey { signature.as_mut_ptr(), signature.len() as u32, &mut result, - NCRYPT_SILENT_FLAG | flag, + dwflags, ))?; Ok(signature) diff --git a/tests/test_client_server.rs b/tests/test_client_server.rs index c325fad..f13d6de 100644 --- a/tests/test_client_server.rs +++ b/tests/test_client_server.rs @@ -29,7 +29,7 @@ mod client { let context = contexts .first() .ok_or_else(|| anyhow::Error::msg("No client cert"))?; - let key = context.acquire_key()?; + let key = context.acquire_key(true)?; let signing_key = CngSigningKey::new(key)?; let chain = context .as_chain_der()? @@ -123,7 +123,7 @@ mod server { let contexts = self.0.find_by_subject_str(name).ok()?; let (context, key) = contexts.into_iter().find_map(|ctx| { - let key = ctx.acquire_key().ok()?; + let key = ctx.acquire_key(true).ok()?; CngSigningKey::new(key).ok().map(|key| (ctx, key)) })?; diff --git a/tests/test_sign.rs b/tests/test_sign.rs index 1aa4f8a..a178d7c 100644 --- a/tests/test_sign.rs +++ b/tests/test_sign.rs @@ -28,7 +28,7 @@ fn test_sign() { SignatureScheme::ECDSA_NISTP384_SHA384, ]; - let key = context.acquire_key().unwrap(); + let key = context.acquire_key(true).unwrap(); let signing_key = CngSigningKey::new(key).unwrap(); assert_eq!(signing_key.algorithm(), SignatureAlgorithm::ECDSA); let signer = signing_key.choose_scheme(&offered).unwrap();