Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deprecated feature #74

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ default-target = "x86_64-pc-windows-msvc"

[dependencies]
lazy_static = "1.0"
winapi = { version = "0.3", features = ["lmcons", "minschannel", "securitybaseapi", "schannel", "sspi", "sysinfoapi", "timezoneapi", "winbase", "wincrypt", "winerror"] }
winapi = { version = "0.3", features = ["minwindef", "ntdef", "lmcons", "minschannel", "securitybaseapi", "schannel", "sspi", "sysinfoapi", "timezoneapi", "winbase", "wincrypt", "winerror", "ncrypt"] }


[features]
allow-deprecated = []
26 changes: 20 additions & 6 deletions src/cert_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use winapi::um::wincrypt;

use crate::Inner;
use crate::ncrypt_key::NcryptKey;
use crate::crypt_prov::{CryptProv, ProviderType};
#[cfg(feature = "allow-deprecated")]
use crate::deprecated::crypt_prov::{CryptProv, ProviderType};
use crate::cert_store::CertStore;

/// A supported hashing algorithm
Expand Down Expand Up @@ -456,7 +457,13 @@ impl<'a> AcquirePrivateKeyOptions<'a> {
/// Acquires the private key handle.
pub fn acquire(&self) -> io::Result<PrivateKey> {
unsafe {
let flags = self.flags | wincrypt::CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG;
let api_flag = if cfg!(feature = "allow-deprecated") {
wincrypt::CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG
} else {
wincrypt:: CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG
};

let flags = self.flags | api_flag;
let mut handle = 0;
let mut spec = 0;
let mut free = winapi::FALSE;
Expand All @@ -471,17 +478,23 @@ impl<'a> AcquirePrivateKeyOptions<'a> {
}
assert!(free == winapi::TRUE);
if spec & wincrypt::CERT_NCRYPT_KEY_SPEC != 0 {
Ok(PrivateKey::NcryptKey(NcryptKey::from_inner(handle)))
} else {
Ok(PrivateKey::CryptProv(CryptProv::from_inner(handle)))
}
return Ok(PrivateKey::NcryptKey(NcryptKey::from_inner(handle)));
}

#[cfg(feature = "allow-deprecated")]
return Ok(PrivateKey::CryptProv(CryptProv::from_inner(handle)));

#[cfg(not(feature = "allow-deprecated"))]
return Err(io::Error::new(io::ErrorKind::Other, "Api failure"));

}
}
}

/// The private key associated with a certificate context.
pub enum PrivateKey {
/// A CryptoAPI provider.
#[cfg(feature = "allow-deprecated")]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing it this way leads to a breaking change.
I'm fine with changing the default to CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, as that should not break anything.
We can mark it #[deprecated] but I don't think there's any advantage to hiding it behind a feature?

CryptProv(CryptProv),
/// A CNG provider.
NcryptKey(NcryptKey),
Expand Down Expand Up @@ -520,6 +533,7 @@ impl<'a> SetKeyProvInfo<'a> {
///
/// If not provided, the key container is one of the CNG key storage
/// providers.
#[cfg(feature = "allow-deprecated")]
pub fn type_(&mut self, type_: ProviderType) -> &mut SetKeyProvInfo<'a> {
self.type_ = type_.as_raw();
self
Expand Down
File renamed without changes.
178 changes: 94 additions & 84 deletions src/crypt_prov.rs → src/deprecated/crypt_prov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ use winapi::shared::minwindef as winapi;
use winapi::shared::ntdef;
use winapi::um::winbase;
use winapi::um::wincrypt;

use crate::deprecated::crypt_key::CryptKey;
use crate::Inner;
use crate::crypt_key::CryptKey;

/// A CryptoAPI handle to a provider of a key.
pub struct CryptProv(wincrypt::HCRYPTPROV);
Expand Down Expand Up @@ -98,15 +97,25 @@ impl AcquireOptions {
/// Acquires a container.
pub fn acquire(&self, type_: ProviderType) -> io::Result<CryptProv> {
unsafe {
let container = self.container.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null());
let provider = self.provider.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null());
let container = self
.container
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or(ptr::null());
let provider = self
.provider
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or(ptr::null());

let mut prov = 0;
let res = wincrypt::CryptAcquireContextW(&mut prov,
container as *mut _,
provider as *mut _,
type_.0,
self.flags);
let res = wincrypt::CryptAcquireContextW(
&mut prov,
container as *mut _,
provider as *mut _,
type_.0,
self.flags,
);
if res == winapi::TRUE {
Ok(CryptProv(prov))
} else {
Expand Down Expand Up @@ -181,15 +190,16 @@ impl<'a> ImportOptions<'a> {
assert!(der.len() <= winapi::DWORD::max_value() as usize);
let mut buf = ptr::null_mut();
let mut len = 0;
let res = wincrypt::CryptDecodeObjectEx(wincrypt::X509_ASN_ENCODING |
wincrypt::PKCS_7_ASN_ENCODING,
wincrypt::PKCS_RSA_PRIVATE_KEY,
der.as_ptr(),
der.len() as winapi::DWORD,
wincrypt::CRYPT_DECODE_ALLOC_FLAG,
ptr::null_mut(),
&mut buf as *mut _ as winapi::LPVOID,
&mut len);
let res = wincrypt::CryptDecodeObjectEx(
wincrypt::X509_ASN_ENCODING | wincrypt::PKCS_7_ASN_ENCODING,
wincrypt::PKCS_RSA_PRIVATE_KEY,
der.as_ptr(),
der.len() as winapi::DWORD,
wincrypt::CRYPT_DECODE_ALLOC_FLAG,
ptr::null_mut(),
&mut buf as *mut _ as winapi::LPVOID,
&mut len,
);
if res == winapi::FALSE {
return Err(io::Error::last_os_error());
}
Expand All @@ -214,15 +224,16 @@ impl<'a> ImportOptions<'a> {
// Decode the der format into a CRYPT_PRIVATE_KEY_INFO struct
let mut buf = ptr::null_mut();
let mut len = 0;
let res = wincrypt::CryptDecodeObjectEx(wincrypt::X509_ASN_ENCODING |
wincrypt::PKCS_7_ASN_ENCODING,
wincrypt::PKCS_PRIVATE_KEY_INFO,
der.as_ptr(),
der.len() as winapi::DWORD,
wincrypt::CRYPT_DECODE_ALLOC_FLAG,
ptr::null_mut(),
&mut buf as *mut _ as winapi::LPVOID,
&mut len);
let res = wincrypt::CryptDecodeObjectEx(
wincrypt::X509_ASN_ENCODING | wincrypt::PKCS_7_ASN_ENCODING,
wincrypt::PKCS_PRIVATE_KEY_INFO,
der.as_ptr(),
der.len() as winapi::DWORD,
wincrypt::CRYPT_DECODE_ALLOC_FLAG,
ptr::null_mut(),
&mut buf as *mut _ as winapi::LPVOID,
&mut len,
);
if res == winapi::FALSE {
return Err(io::Error::last_os_error());
}
Expand All @@ -245,11 +256,14 @@ impl<'a> ImportOptions<'a> {
.trim();

if pem_str.starts_with("-----") {
if !pem_str.starts_with("-----BEGIN PRIVATE KEY-----") ||
!pem_str.ends_with("-----END PRIVATE KEY-----") {
return Err(io::Error::new(io::ErrorKind::InvalidData,
"expected '-----BEGIN PRIVATE KEY-----'\
and '-----END PRIVATE KEY-----' PEM guards"));
if !pem_str.starts_with("-----BEGIN PRIVATE KEY-----")
|| !pem_str.ends_with("-----END PRIVATE KEY-----")
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"expected '-----BEGIN PRIVATE KEY-----'\
and '-----END PRIVATE KEY-----' PEM guards",
));
}
}
unsafe {
Expand All @@ -258,26 +272,30 @@ impl<'a> ImportOptions<'a> {
// Decode the pem wrapper before passing it to import_pkcs8
// Call once first to figure out the necessary buffer size
let mut len = 0;
let res = wincrypt::CryptStringToBinaryA(pem.as_ptr() as ntdef::LPCSTR,
pem.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64_ANY,
ptr::null_mut(),
&mut len,
ptr::null_mut(),
ptr::null_mut());
let res = wincrypt::CryptStringToBinaryA(
pem.as_ptr() as ntdef::LPCSTR,
pem.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64_ANY,
ptr::null_mut(),
&mut len,
ptr::null_mut(),
ptr::null_mut(),
);
if res == winapi::FALSE {
return Err(io::Error::last_os_error());
}

// Call second time to actually get the DER bytes
let mut der_buf = vec![0; len as usize];
let res = wincrypt::CryptStringToBinaryA(pem.as_ptr() as ntdef::LPCSTR,
pem.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64_ANY,
der_buf.as_mut_ptr(),
&mut len,
ptr::null_mut(),
ptr::null_mut());
let res = wincrypt::CryptStringToBinaryA(
pem.as_ptr() as ntdef::LPCSTR,
pem.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64_ANY,
der_buf.as_mut_ptr(),
&mut len,
ptr::null_mut(),
ptr::null_mut(),
);
if res == winapi::FALSE {
return Err(io::Error::last_os_error());
}
Expand All @@ -299,40 +317,44 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
context.import()
.import(key)
.unwrap();
context.import().import(key).unwrap();
}

#[test]
fn pkcs8_key() {
let key = include_str!("../test/key.pem");
let der = unsafe {
let mut len = 0;
assert!(wincrypt::CryptStringToBinaryA(key.as_ptr() as ntdef::LPCSTR,
key.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64HEADER,
ptr::null_mut(),
&mut len,
ptr::null_mut(),
ptr::null_mut()) == winapi::TRUE);
assert!(
wincrypt::CryptStringToBinaryA(
key.as_ptr() as ntdef::LPCSTR,
key.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64HEADER,
ptr::null_mut(),
&mut len,
ptr::null_mut(),
ptr::null_mut()
) == winapi::TRUE
);
let mut buf = vec![0; len as usize];
assert!(wincrypt::CryptStringToBinaryA(key.as_ptr() as ntdef::LPCSTR,
key.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64HEADER,
buf.as_mut_ptr(),
&mut len,
ptr::null_mut(),
ptr::null_mut()) == winapi::TRUE);
assert!(
wincrypt::CryptStringToBinaryA(
key.as_ptr() as ntdef::LPCSTR,
key.len() as winapi::DWORD,
wincrypt::CRYPT_STRING_BASE64HEADER,
buf.as_mut_ptr(),
&mut len,
ptr::null_mut(),
ptr::null_mut()
) == winapi::TRUE
);
buf
};
let mut context = AcquireOptions::new()
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
context.import()
.import_pkcs8(&der)
.unwrap();
context.import().import_pkcs8(&der).unwrap();
}

#[test]
Expand All @@ -343,9 +365,7 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
assert!(context.import()
.import_pkcs8(&key[..])
.is_err());
assert!(context.import().import_pkcs8(&key[..]).is_err());
}

#[test]
Expand All @@ -355,9 +375,7 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
context.import()
.import_pkcs8_pem(key)
.unwrap();
context.import().import_pkcs8_pem(key).unwrap();
}

#[test]
Expand All @@ -367,9 +385,7 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
context.import()
.import_pkcs8_pem(key)
.unwrap();
context.import().import_pkcs8_pem(key).unwrap();
}

#[test]
Expand All @@ -379,9 +395,7 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
assert!(context.import()
.import_pkcs8_pem(key)
.is_err());
assert!(context.import().import_pkcs8_pem(key).is_err());
}

#[test]
Expand All @@ -391,9 +405,7 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
assert!(context.import()
.import_pkcs8_pem(key)
.is_err());
assert!(context.import().import_pkcs8_pem(key).is_err());
}

#[test]
Expand All @@ -403,8 +415,6 @@ mod test {
.verify_context(true)
.acquire(ProviderType::rsa_full())
.unwrap();
assert!(context.import()
.import_pkcs8_pem(key)
.is_err());
assert!(context.import().import_pkcs8_pem(key).is_err());
}
}
2 changes: 2 additions & 0 deletions src/deprecated/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod crypt_key;
pub mod crypt_prov;
Loading