summaryrefslogtreecommitdiffstats
path: root/vendor/schannel
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:47:55 +0000
commit2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4 (patch)
tree033cc839730fda84ff08db877037977be94e5e3a /vendor/schannel
parentInitial commit. (diff)
downloadcargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.tar.xz
cargo-2aadc03ef15cb5ca5cc2af8a7c08e070742f0ac4.zip
Adding upstream version 0.70.1+ds1.upstream/0.70.1+ds1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/schannel')
-rw-r--r--vendor/schannel/.cargo-checksum.json1
-rw-r--r--vendor/schannel/Cargo.toml51
-rw-r--r--vendor/schannel/LICENSE.md7
-rw-r--r--vendor/schannel/README.md6
-rw-r--r--vendor/schannel/src/alpn_list.rs93
-rw-r--r--vendor/schannel/src/cert_chain.rs139
-rw-r--r--vendor/schannel/src/cert_context.rs742
-rw-r--r--vendor/schannel/src/cert_store.rs471
-rw-r--r--vendor/schannel/src/context_buffer.rs26
-rw-r--r--vendor/schannel/src/crypt_key.rs16
-rw-r--r--vendor/schannel/src/crypt_prov.rs420
-rw-r--r--vendor/schannel/src/ctl_context.rs187
-rw-r--r--vendor/schannel/src/key_handle.rs3
-rw-r--r--vendor/schannel/src/lib.rs114
-rw-r--r--vendor/schannel/src/ncrypt_key.rs16
-rw-r--r--vendor/schannel/src/schannel_cred.rs292
-rw-r--r--vendor/schannel/src/security_context.rs134
-rw-r--r--vendor/schannel/src/test.rs921
-rw-r--r--vendor/schannel/src/tls_stream.rs1050
-rw-r--r--vendor/schannel/test/cert.derbin0 -> 799 bytes
-rw-r--r--vendor/schannel/test/cert.pem19
-rw-r--r--vendor/schannel/test/identity.p12bin0 -> 3386 bytes
-rw-r--r--vendor/schannel/test/key.keybin0 -> 1193 bytes
-rw-r--r--vendor/schannel/test/key.pem28
-rw-r--r--vendor/schannel/test/key_invalid_header.pem28
-rw-r--r--vendor/schannel/test/key_no_end_header.pem27
-rw-r--r--vendor/schannel/test/key_no_headers.pem26
-rw-r--r--vendor/schannel/test/key_wrong_header.pem28
-rw-r--r--vendor/schannel/test/self-signed.badssl.com.cerbin0 -> 893 bytes
-rw-r--r--vendor/schannel/update_test_cert.bat2
30 files changed, 4847 insertions, 0 deletions
diff --git a/vendor/schannel/.cargo-checksum.json b/vendor/schannel/.cargo-checksum.json
new file mode 100644
index 0000000..804dcf5
--- /dev/null
+++ b/vendor/schannel/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{},"package":"0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"} \ No newline at end of file
diff --git a/vendor/schannel/Cargo.toml b/vendor/schannel/Cargo.toml
new file mode 100644
index 0000000..b404bb4
--- /dev/null
+++ b/vendor/schannel/Cargo.toml
@@ -0,0 +1,51 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "schannel"
+version = "0.1.22"
+authors = [
+ "Steven Fackler <sfackler@gmail.com>",
+ "Steffen Butzer <steffen.butzer@outlook.com>",
+]
+description = "Schannel bindings for rust, allowing SSL/TLS (e.g. https) without openssl"
+documentation = "https://docs.rs/schannel/0.1.19/schannel/"
+readme = "README.md"
+keywords = [
+ "windows",
+ "schannel",
+ "tls",
+ "ssl",
+ "https",
+]
+license = "MIT"
+repository = "https://github.com/steffengy/schannel-rs"
+
+[package.metadata.docs.rs]
+default-target = "x86_64-pc-windows-msvc"
+
+[dependencies.windows-sys]
+version = "0.48"
+features = [
+ "Win32_Foundation",
+ "Win32_Security_Cryptography",
+ "Win32_Security_Authentication_Identity",
+ "Win32_Security_Credentials",
+ "Win32_System_Memory",
+]
+
+[dev-dependencies.windows-sys]
+version = "0.48"
+features = [
+ "Win32_System_SystemInformation",
+ "Win32_System_Time",
+]
diff --git a/vendor/schannel/LICENSE.md b/vendor/schannel/LICENSE.md
new file mode 100644
index 0000000..0d9d26d
--- /dev/null
+++ b/vendor/schannel/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright (c) 2015 steffengy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/schannel/README.md b/vendor/schannel/README.md
new file mode 100644
index 0000000..fcdf479
--- /dev/null
+++ b/vendor/schannel/README.md
@@ -0,0 +1,6 @@
+schannel-rs [![.github/workflows/ci.yaml](https://github.com/steffengy/schannel-rs/actions/workflows/ci.yaml/badge.svg)](https://github.com/steffengy/schannel-rs/actions/workflows/ci.yaml)
+=====
+
+[Documentation](https://docs.rs/schannel/0/x86_64-pc-windows-msvc/schannel/)
+
+Rust bindings to the Windows SChannel APIs providing TLS client and server functionality.
diff --git a/vendor/schannel/src/alpn_list.rs b/vendor/schannel/src/alpn_list.rs
new file mode 100644
index 0000000..8854e67
--- /dev/null
+++ b/vendor/schannel/src/alpn_list.rs
@@ -0,0 +1,93 @@
+use std::alloc;
+use std::mem;
+use std::ptr;
+use std::slice;
+
+use windows_sys::Win32::Security::Authentication::Identity;
+
+// This is manually calculated here rather than using `size_of::<SEC_APPLICATION_PROTOCOL_LIST>()`,
+// as the latter is 2 bytes too large because it accounts for padding at the end of the struct for
+// alignment requirements, which is irrelevant in actual usage because there is a variable-length
+// array at the end of the struct.
+const SEC_APPLICATION_PROTOCOL_LIST_HEADER_SIZE: usize =
+ mem::size_of::<u32>() + mem::size_of::<u16>();
+const SEC_APPLICATION_PROTOCOL_HEADER_SIZE: usize =
+ mem::size_of::<u32>() + SEC_APPLICATION_PROTOCOL_LIST_HEADER_SIZE;
+
+pub struct AlpnList {
+ layout: alloc::Layout,
+ memory: ptr::NonNull<u8>,
+}
+
+impl Drop for AlpnList {
+ fn drop(&mut self) {
+ unsafe {
+ // Safety: `self.memory` was allocated with `self.layout` and is non-null.
+ alloc::dealloc(self.memory.as_ptr(), self.layout);
+ }
+ }
+}
+
+impl AlpnList {
+ pub fn new(protos: &[Vec<u8>]) -> Self {
+ // ALPN wire format is each ALPN preceded by its length as a byte.
+ let mut alpn_wire_format =
+ Vec::with_capacity(protos.iter().map(Vec::len).sum::<usize>() + protos.len());
+ for alpn in protos {
+ alpn_wire_format.push(alpn.len() as u8);
+ alpn_wire_format.extend(alpn);
+ }
+
+ let size = SEC_APPLICATION_PROTOCOL_HEADER_SIZE + alpn_wire_format.len();
+ let layout = alloc::Layout::from_size_align(
+ size,
+ mem::align_of::<Identity::SEC_APPLICATION_PROTOCOLS>(),
+ )
+ .unwrap();
+
+ unsafe {
+ // Safety: `layout` is guaranteed to have non-zero size.
+ let memory = match ptr::NonNull::new(alloc::alloc(layout)) {
+ Some(ptr) => ptr,
+ None => alloc::handle_alloc_error(layout),
+ };
+
+ // Safety: `memory` was created from `layout`.
+ let buf = slice::from_raw_parts_mut(memory.as_ptr(), layout.size());
+ let protocols = &mut *(buf.as_mut_ptr() as *mut Identity::SEC_APPLICATION_PROTOCOLS);
+ protocols.ProtocolListsSize =
+ (SEC_APPLICATION_PROTOCOL_LIST_HEADER_SIZE + alpn_wire_format.len()) as u32;
+
+ let protocol = &mut *protocols.ProtocolLists.as_mut_ptr();
+ protocol.ProtoNegoExt = Identity::SecApplicationProtocolNegotiationExt_ALPN;
+ protocol.ProtocolListSize = alpn_wire_format.len() as u16;
+
+ let protocol_list_offset =
+ protocol.ProtocolList.as_ptr() as usize - buf.as_ptr() as usize;
+ let protocol_list = &mut buf[protocol_list_offset..];
+ protocol_list.copy_from_slice(&alpn_wire_format);
+
+ Self { layout, memory }
+ }
+ }
+}
+
+impl std::ops::Deref for AlpnList {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ unsafe {
+ // Safety: `self.memory` was created from `self.layout`.
+ slice::from_raw_parts(self.memory.as_ptr(), self.layout.size())
+ }
+ }
+}
+
+impl std::ops::DerefMut for AlpnList {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ unsafe {
+ // Safety: `self.memory` was created from `self.layout`.
+ slice::from_raw_parts_mut(self.memory.as_ptr(), self.layout.size())
+ }
+ }
+}
diff --git a/vendor/schannel/src/cert_chain.rs b/vendor/schannel/src/cert_chain.rs
new file mode 100644
index 0000000..9a2f740
--- /dev/null
+++ b/vendor/schannel/src/cert_chain.rs
@@ -0,0 +1,139 @@
+//! Bindings to winapi's certificate-chain related APIs.
+
+use std::mem;
+use std::slice;
+
+use windows_sys::Win32::Security::Cryptography;
+
+use crate::cert_context::CertContext;
+use crate::Inner;
+
+/// A certificate chain context (consisting of multiple chains)
+pub struct CertChainContext(pub(crate) *mut Cryptography::CERT_CHAIN_CONTEXT);
+inner!(CertChainContext, *mut Cryptography::CERT_CHAIN_CONTEXT);
+
+unsafe impl Sync for CertChainContext {}
+unsafe impl Send for CertChainContext {}
+
+impl Clone for CertChainContext {
+ fn clone(&self) -> Self {
+ let rced = unsafe { Cryptography::CertDuplicateCertificateChain(self.0) };
+ CertChainContext(rced)
+ }
+}
+
+impl Drop for CertChainContext {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::CertFreeCertificateChain(self.0);
+ }
+ }
+}
+
+impl CertChainContext {
+ /// Get the final (for a successful verification this means successful) certificate chain
+ ///
+ /// https://msdn.microsoft.com/de-de/library/windows/desktop/aa377182(v=vs.85).aspx
+ /// rgpChain[cChain - 1] is the final chain
+ pub fn final_chain(&self) -> Option<CertChain> {
+ if let Some(chain) = self.chains().last() {
+ return Some(CertChain(chain.0, self.clone()));
+ }
+ None
+ }
+
+ /// Retrieves the specified chain from the context.
+ pub fn get_chain(&self, index: usize) -> Option<CertChain> {
+ let cert_chain = unsafe {
+ let cert_chain = *self.0;
+ if index >= cert_chain.cChain as usize {
+ None
+ } else {
+ let chain_slice =
+ slice::from_raw_parts(cert_chain.rgpChain, cert_chain.cChain as usize);
+ Some(CertChain(chain_slice[index], self.clone()))
+ }
+ };
+ cert_chain
+ }
+
+ /// Return an iterator over all certificate chains in this context
+ pub fn chains(&self) -> CertificateChains {
+ CertificateChains {
+ context: self,
+ idx: 0,
+ }
+ }
+}
+
+/// A (simple) certificate chain
+pub struct CertChain(*mut Cryptography::CERT_SIMPLE_CHAIN, CertChainContext);
+
+impl CertChain {
+ /// Returns the number of certificates in the chain
+ pub fn len(&self) -> usize {
+ unsafe { (*self.0).cElement as usize }
+ }
+
+ /// Returns true if there are no certificates in the chain
+ pub fn is_empty(&self) -> bool {
+ unsafe { (*self.0).cElement == 0 }
+ }
+
+ /// Get the n-th certificate from the current chain
+ pub fn get(&self, idx: usize) -> Option<CertContext> {
+ let elements = unsafe {
+ let cert_chain = *self.0;
+ slice::from_raw_parts(
+ cert_chain.rgpElement as *mut &mut Cryptography::CERT_CHAIN_ELEMENT,
+ cert_chain.cElement as usize,
+ )
+ };
+ elements.get(idx).map(|el| {
+ let cert = unsafe { CertContext::from_inner(el.pCertContext) };
+ let rc_cert = cert.clone();
+ mem::forget(cert);
+ rc_cert
+ })
+ }
+
+ /// Return an iterator over all certificates in this chain
+ pub fn certificates(&self) -> Certificates {
+ Certificates {
+ chain: self,
+ idx: 0,
+ }
+ }
+}
+
+/// An iterator that iterates over all chains in a context
+pub struct CertificateChains<'a> {
+ context: &'a CertChainContext,
+ idx: usize,
+}
+
+impl<'a> Iterator for CertificateChains<'a> {
+ type Item = CertChain;
+
+ fn next(&mut self) -> Option<CertChain> {
+ let idx = self.idx;
+ self.idx += 1;
+ self.context.get_chain(idx)
+ }
+}
+
+/// An iterator that iterates over all certificates in a chain
+pub struct Certificates<'a> {
+ chain: &'a CertChain,
+ idx: usize,
+}
+
+impl<'a> Iterator for Certificates<'a> {
+ type Item = CertContext;
+
+ fn next(&mut self) -> Option<CertContext> {
+ let idx = self.idx;
+ self.idx += 1;
+ self.chain.get(idx)
+ }
+}
diff --git a/vendor/schannel/src/cert_context.rs b/vendor/schannel/src/cert_context.rs
new file mode 100644
index 0000000..6fc5676
--- /dev/null
+++ b/vendor/schannel/src/cert_context.rs
@@ -0,0 +1,742 @@
+//! Bindings to Windows `PCCERT_CONTEXT` APIs.
+
+use std::ffi::{c_void, CStr, OsString};
+use std::io;
+use std::mem;
+use std::os::windows::prelude::*;
+use std::ptr;
+use std::slice;
+
+use windows_sys::Win32::Foundation;
+use windows_sys::Win32::Security::Cryptography;
+
+use crate::cert_store::CertStore;
+use crate::crypt_prov::{CryptProv, ProviderType};
+use crate::ncrypt_key::NcryptKey;
+use crate::Inner;
+
+/// A supported hashing algorithm
+pub struct HashAlgorithm(u32, usize);
+
+#[allow(missing_docs)]
+impl HashAlgorithm {
+ pub fn md5() -> HashAlgorithm {
+ HashAlgorithm(
+ Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD5,
+ 16,
+ )
+ }
+
+ pub fn sha1() -> HashAlgorithm {
+ HashAlgorithm(
+ Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA1,
+ 20,
+ )
+ }
+
+ pub fn sha256() -> HashAlgorithm {
+ HashAlgorithm(
+ Cryptography::ALG_CLASS_HASH
+ | Cryptography::ALG_TYPE_ANY
+ | Cryptography::ALG_SID_SHA_256,
+ 32,
+ )
+ }
+
+ pub fn sha384() -> HashAlgorithm {
+ HashAlgorithm(
+ Cryptography::ALG_CLASS_HASH
+ | Cryptography::ALG_TYPE_ANY
+ | Cryptography::ALG_SID_SHA_384,
+ 48,
+ )
+ }
+
+ pub fn sha512() -> HashAlgorithm {
+ HashAlgorithm(
+ Cryptography::ALG_CLASS_HASH
+ | Cryptography::ALG_TYPE_ANY
+ | Cryptography::ALG_SID_SHA_512,
+ 64,
+ )
+ }
+}
+
+/// Wrapper of a winapi certificate, or a `PCCERT_CONTEXT`.
+#[derive(Debug)]
+pub struct CertContext(*const Cryptography::CERT_CONTEXT);
+
+unsafe impl Sync for CertContext {}
+unsafe impl Send for CertContext {}
+
+impl Drop for CertContext {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::CertFreeCertificateContext(self.0);
+ }
+ }
+}
+
+impl Clone for CertContext {
+ fn clone(&self) -> CertContext {
+ unsafe { CertContext(Cryptography::CertDuplicateCertificateContext(self.0)) }
+ }
+}
+
+inner!(CertContext, *const Cryptography::CERT_CONTEXT);
+
+impl CertContext {
+ /// Decodes a DER-formatted X509 certificate.
+ pub fn new(data: &[u8]) -> io::Result<CertContext> {
+ let ret = unsafe {
+ Cryptography::CertCreateCertificateContext(
+ Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING,
+ data.as_ptr(),
+ data.len() as u32,
+ )
+ };
+ if ret.is_null() {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(CertContext(ret))
+ }
+ }
+
+ /// Get certificate in binary DER form
+ pub fn to_der(&self) -> &[u8] {
+ self.get_encoded_bytes()
+ }
+
+ /// Certificate subject public key info
+ pub fn subject_public_key_info_der(&self) -> io::Result<Vec<u8>> {
+ unsafe {
+ let mut len: u32 = 0;
+ let ok = Cryptography::CryptEncodeObjectEx(
+ Cryptography::X509_ASN_ENCODING,
+ Cryptography::CERT_INFO_SUBJECT_PUBLIC_KEY_INFO_FLAG as *const u8,
+ &(*(*self.0).pCertInfo).SubjectPublicKeyInfo
+ as *const Cryptography::CERT_PUBLIC_KEY_INFO as _,
+ Cryptography::CRYPT_ENCODE_OBJECT_FLAGS::default(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ &mut len,
+ );
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ if len > 0 {
+ let mut buf = vec![0; len as usize];
+ let ok = Cryptography::CryptEncodeObjectEx(
+ Cryptography::X509_ASN_ENCODING,
+ Cryptography::CERT_INFO_SUBJECT_PUBLIC_KEY_INFO_FLAG as *const u32 as *const _,
+ &(*(*self.0).pCertInfo).SubjectPublicKeyInfo
+ as *const Cryptography::CERT_PUBLIC_KEY_INFO as _,
+ Cryptography::CRYPT_ENCODE_OBJECT_FLAGS::default(),
+ ptr::null_mut(),
+ buf.as_mut_ptr() as _,
+ &mut len,
+ );
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ return Ok(buf);
+ }
+ }
+ Err(io::Error::last_os_error())
+ }
+
+ /// Decodes a PEM-formatted X509 certificate.
+ pub fn from_pem(pem: &str) -> io::Result<CertContext> {
+ unsafe {
+ assert!(pem.len() <= u32::max_value() as usize);
+
+ let mut len = 0;
+ let ok = Cryptography::CryptStringToBinaryA(
+ pem.as_ptr(),
+ pem.len() as u32,
+ Cryptography::CRYPT_STRING_BASE64HEADER,
+ ptr::null_mut(),
+ &mut len,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ );
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut buf = vec![0; len as usize];
+ let ok = Cryptography::CryptStringToBinaryA(
+ pem.as_ptr(),
+ pem.len() as u32,
+ Cryptography::CRYPT_STRING_BASE64HEADER,
+ buf.as_mut_ptr(),
+ &mut len,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ );
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ CertContext::new(&buf)
+ }
+ }
+
+ /// Get certificate as PEM-formatted X509 certificate.
+ pub fn to_pem(&self) -> io::Result<String> {
+ unsafe {
+ let mut len = 0;
+ let ok = Cryptography::CryptBinaryToStringA(
+ (*self.0).pbCertEncoded,
+ (*self.0).cbCertEncoded,
+ Cryptography::CRYPT_STRING_BASE64HEADER,
+ ptr::null_mut(),
+ &mut len,
+ );
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut buf = vec![0; len as usize];
+ let ok = Cryptography::CryptBinaryToStringA(
+ (*self.0).pbCertEncoded,
+ (*self.0).cbCertEncoded,
+ Cryptography::CRYPT_STRING_BASE64HEADER,
+ buf.as_mut_ptr(),
+ &mut len,
+ );
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ Ok(CStr::from_ptr(buf.as_ptr() as *const _)
+ .to_string_lossy()
+ .into_owned())
+ }
+ }
+
+ /// Returns a hash of this certificate
+ pub fn fingerprint(&self, alg: HashAlgorithm) -> io::Result<Vec<u8>> {
+ unsafe {
+ let mut buf = vec![0u8; alg.1];
+ let mut len = buf.len() as u32;
+
+ let ret = Cryptography::CryptHashCertificate(
+ Cryptography::HCRYPTPROV_LEGACY::default(),
+ alg.0,
+ 0,
+ (*self.0).pbCertEncoded,
+ (*self.0).cbCertEncoded,
+ buf.as_mut_ptr(),
+ &mut len,
+ );
+
+ if ret == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(buf)
+ }
+ }
+
+ /// Returns the sha1 hash of this certificate
+ ///
+ /// The sha1 is returned as a 20-byte array representing the bits of the
+ /// sha1 hash.
+ #[deprecated(note = "please use fingerprint instead")]
+ pub fn sha1(&self) -> io::Result<[u8; 20]> {
+ let mut out = [0u8; 20];
+ out.copy_from_slice(&self.fingerprint(HashAlgorithm::sha1())?);
+ Ok(out)
+ }
+
+ /// Returns the `<SIGNATURE>/<HASH>` string representing the certificate
+ /// signature.
+ ///
+ /// The `<SIGNATURE>` value identifies the CNG public key
+ /// algorithm. The `<HASH>` value identifies the CNG hash algorithm.
+ ///
+ /// Common examples are:
+ ///
+ /// * `RSA/SHA1`
+ /// * `RSA/SHA256`
+ /// * `ECDSA/SHA256`
+ pub fn sign_hash_algorithms(&self) -> io::Result<String> {
+ self.get_string(Cryptography::CERT_SIGN_HASH_CNG_ALG_PROP_ID)
+ }
+
+ /// Returns the signature hash.
+ pub fn signature_hash(&self) -> io::Result<Vec<u8>> {
+ self.get_bytes(Cryptography::CERT_SIGNATURE_HASH_PROP_ID)
+ }
+
+ /// Returns the property displayed by the certificate UI. This property
+ /// allows the user to describe the certificate's use.
+ pub fn description(&self) -> io::Result<Vec<u8>> {
+ self.get_bytes(Cryptography::CERT_DESCRIPTION_PROP_ID)
+ }
+
+ /// Returns a string that contains the display name for the certificate.
+ pub fn friendly_name(&self) -> io::Result<String> {
+ self.get_string(Cryptography::CERT_FRIENDLY_NAME_PROP_ID)
+ }
+
+ /// Configures the string that contains the display name for this
+ /// certificate.
+ pub fn set_friendly_name(&self, name: &str) -> io::Result<()> {
+ self.set_string(Cryptography::CERT_FRIENDLY_NAME_PROP_ID, name)
+ }
+
+ /// Verifies the time validity of this certificate relative to the system's
+ /// current time.
+ pub fn is_time_valid(&self) -> io::Result<bool> {
+ let ret =
+ unsafe { Cryptography::CertVerifyTimeValidity(ptr::null_mut(), (*self.0).pCertInfo) };
+ Ok(ret == 0)
+ }
+
+ /// Returns a builder used to acquire the private key corresponding to this certificate.
+ pub fn private_key(&self) -> AcquirePrivateKeyOptions {
+ AcquirePrivateKeyOptions {
+ cert: self,
+ flags: 0,
+ }
+ }
+
+ /// Deletes this certificate from its certificate store.
+ pub fn delete(self) -> io::Result<()> {
+ unsafe {
+ let ret = Cryptography::CertDeleteCertificateFromStore(self.0);
+ mem::forget(self);
+ if ret != 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Returns a builder used to set the private key associated with this certificate.
+ pub fn set_key_prov_info(&self) -> SetKeyProvInfo {
+ SetKeyProvInfo {
+ cert: self,
+ container: None,
+ provider: None,
+ type_: 0,
+ flags: 0,
+ key_spec: 0,
+ }
+ }
+
+ /// Returns the valid uses for this certificate
+ pub fn valid_uses(&self) -> io::Result<ValidUses> {
+ unsafe {
+ let mut buf_len = 0;
+ let ok =
+ Cryptography::CertGetEnhancedKeyUsage(self.0, 0, ptr::null_mut(), &mut buf_len);
+
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut buf = vec![0u8; buf_len as usize];
+ let cert_enhkey_usage = buf.as_mut_ptr() as *mut Cryptography::CTL_USAGE;
+
+ let ok =
+ Cryptography::CertGetEnhancedKeyUsage(self.0, 0, cert_enhkey_usage, &mut buf_len);
+ if ok == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let use_cnt = (*cert_enhkey_usage).cUsageIdentifier;
+ if use_cnt == 0 {
+ let last_error = io::Error::last_os_error();
+ match last_error.raw_os_error() {
+ Some(Foundation::CRYPT_E_NOT_FOUND) => return Ok(ValidUses::All),
+ Some(Foundation::S_OK) => (),
+ _ => return Err(last_error),
+ };
+ }
+
+ let mut oids: Vec<String> = Vec::with_capacity(use_cnt as usize);
+ for i in 0..use_cnt {
+ let oid_ptr = (*cert_enhkey_usage).rgpszUsageIdentifier;
+ oids.push(
+ CStr::from_ptr(*(oid_ptr.offset(i as isize)) as *const _)
+ .to_string_lossy()
+ .into_owned(),
+ );
+ }
+ Ok(ValidUses::Oids(oids))
+ }
+ }
+
+ /// For a remote certificate, returns a certificate store containing any intermediate
+ /// certificates provided by the remote sender.
+ pub fn cert_store(&self) -> Option<CertStore> {
+ unsafe {
+ let chain = (*self.0).hCertStore;
+ if chain.is_null() {
+ None
+ } else {
+ Some(CertStore::from_inner(Cryptography::CertDuplicateStore(
+ chain,
+ )))
+ }
+ }
+ }
+
+ fn get_encoded_bytes(&self) -> &[u8] {
+ unsafe {
+ let cert_ctx = *self.0;
+ slice::from_raw_parts(cert_ctx.pbCertEncoded, cert_ctx.cbCertEncoded as usize)
+ }
+ }
+
+ fn get_bytes(&self, prop: u32) -> io::Result<Vec<u8>> {
+ unsafe {
+ let mut len = 0;
+ let ret = Cryptography::CertGetCertificateContextProperty(
+ self.0,
+ prop,
+ ptr::null_mut(),
+ &mut len,
+ );
+ if ret == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut buf = vec![0u8; len as usize];
+ let ret = Cryptography::CertGetCertificateContextProperty(
+ self.0,
+ prop,
+ buf.as_mut_ptr() as *mut c_void,
+ &mut len,
+ );
+ if ret == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ Ok(buf)
+ }
+ }
+
+ fn get_string(&self, prop: u32) -> io::Result<String> {
+ unsafe {
+ let mut len = 0;
+ let ret = Cryptography::CertGetCertificateContextProperty(
+ self.0,
+ prop,
+ ptr::null_mut(),
+ &mut len,
+ );
+ if ret == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ // Divide by 2 b/c `len` is the byte length, but we're allocating
+ // u16 pairs which are 2 bytes each.
+ let amt = (len / 2) as usize;
+ let mut buf = vec![0u16; amt];
+ let ret = Cryptography::CertGetCertificateContextProperty(
+ self.0,
+ prop,
+ buf.as_mut_ptr() as *mut c_void,
+ &mut len,
+ );
+ if ret == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ // Chop off the trailing nul byte
+ Ok(OsString::from_wide(&buf[..amt - 1]).into_string().unwrap())
+ }
+ }
+
+ fn set_string(&self, prop: u32, s: &str) -> io::Result<()> {
+ unsafe {
+ let data = s.encode_utf16().chain(Some(0)).collect::<Vec<_>>();
+ let data = Cryptography::CRYPT_INTEGER_BLOB {
+ cbData: (data.len() * 2) as u32,
+ pbData: data.as_ptr() as *mut _,
+ };
+ let ret = Cryptography::CertSetCertificateContextProperty(
+ self.0,
+ prop,
+ 0,
+ &data as *const _ as *const _,
+ );
+ if ret == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(())
+ }
+ }
+ }
+}
+
+impl PartialEq for CertContext {
+ fn eq(&self, other: &CertContext) -> bool {
+ self.get_encoded_bytes() == other.get_encoded_bytes()
+ }
+}
+
+/// A builder type for certificate private key lookup.
+pub struct AcquirePrivateKeyOptions<'a> {
+ cert: &'a CertContext,
+ flags: u32,
+}
+
+impl<'a> AcquirePrivateKeyOptions<'a> {
+ /// If set, the certificate's public key will be compared with the private key to ensure a
+ /// match.
+ pub fn compare_key(&mut self, compare_key: bool) -> &mut AcquirePrivateKeyOptions<'a> {
+ self.flag(Cryptography::CRYPT_ACQUIRE_COMPARE_KEY_FLAG, compare_key)
+ }
+
+ /// If set, the lookup will not display any user interface, even if that causes the lookup to
+ /// fail.
+ pub fn silent(&mut self, silent: bool) -> &mut AcquirePrivateKeyOptions<'a> {
+ self.flag(Cryptography::CRYPT_ACQUIRE_SILENT_FLAG, silent)
+ }
+
+ fn flag(&mut self, flag: u32, set: bool) -> &mut AcquirePrivateKeyOptions<'a> {
+ if set {
+ self.flags |= flag;
+ } else {
+ self.flags &= !flag;
+ }
+ self
+ }
+
+ /// Acquires the private key handle.
+ pub fn acquire(&self) -> io::Result<PrivateKey> {
+ unsafe {
+ let flags = self.flags | Cryptography::CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG;
+ let mut handle = Cryptography::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE::default();
+ let mut spec = Cryptography::CERT_KEY_SPEC::default();
+ let mut free = Foundation::BOOL::default();
+ let res = Cryptography::CryptAcquireCertificatePrivateKey(
+ self.cert.0,
+ flags,
+ ptr::null_mut(),
+ &mut handle,
+ &mut spec,
+ &mut free,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ assert_ne!(free, 0);
+ if spec & Cryptography::CERT_NCRYPT_KEY_SPEC != 0 {
+ Ok(PrivateKey::NcryptKey(NcryptKey::from_inner(handle)))
+ } else {
+ Ok(PrivateKey::CryptProv(CryptProv::from_inner(handle)))
+ }
+ }
+ }
+}
+
+/// The private key associated with a certificate context.
+pub enum PrivateKey {
+ /// A CryptoAPI provider.
+ CryptProv(CryptProv),
+ /// A CNG provider.
+ NcryptKey(NcryptKey),
+}
+
+/// A builder used to set the private key associated with a certificate.
+pub struct SetKeyProvInfo<'a> {
+ cert: &'a CertContext,
+ container: Option<Vec<u16>>,
+ provider: Option<Vec<u16>>,
+ type_: u32,
+ flags: u32,
+ key_spec: u32,
+}
+
+impl<'a> SetKeyProvInfo<'a> {
+ /// The name of the key container.
+ ///
+ /// If `type_` is not provided, this specifies the name of the key withing
+ /// the CNG key storage provider.
+ pub fn container(&mut self, container: &str) -> &mut SetKeyProvInfo<'a> {
+ self.container = Some(container.encode_utf16().chain(Some(0)).collect());
+ self
+ }
+
+ /// The name of the CSP.
+ ///
+ /// If `type_` is not provided, this contains the name of the CNG key
+ /// storage provider.
+ pub fn provider(&mut self, provider: &str) -> &mut SetKeyProvInfo<'a> {
+ self.provider = Some(provider.encode_utf16().chain(Some(0)).collect());
+ self
+ }
+
+ /// Sets the CSP type.
+ ///
+ /// If not provided, the key container is one of the CNG key storage
+ /// providers.
+ pub fn type_(&mut self, type_: ProviderType) -> &mut SetKeyProvInfo<'a> {
+ self.type_ = type_.as_raw();
+ self
+ }
+
+ /// If set, the handle to the key provider can be kept open for subsequent
+ /// calls to cryptographic functions.
+ pub fn keep_open(&mut self, keep_open: bool) -> &mut SetKeyProvInfo<'a> {
+ self.flag(Cryptography::CERT_SET_KEY_PROV_HANDLE_PROP_ID, keep_open)
+ }
+
+ /// If set, the key container contains machine keys.
+ pub fn machine_keyset(&mut self, machine_keyset: bool) -> &mut SetKeyProvInfo<'a> {
+ self.flag(Cryptography::CRYPT_MACHINE_KEYSET, machine_keyset)
+ }
+
+ /// If set, the key container will attempt to open keys without any user
+ /// interface prompts.
+ pub fn silent(&mut self, silent: bool) -> &mut SetKeyProvInfo<'a> {
+ self.flag(Cryptography::CRYPT_SILENT, silent)
+ }
+
+ fn flag(&mut self, flag: u32, on: bool) -> &mut SetKeyProvInfo<'a> {
+ if on {
+ self.flags |= flag;
+ } else {
+ self.flags &= !flag;
+ }
+ self
+ }
+
+ /// The specification of the private key to retrieve.
+ pub fn key_spec(&mut self, key_spec: KeySpec) -> &mut SetKeyProvInfo<'a> {
+ self.key_spec = key_spec.0;
+ self
+ }
+
+ /// Sets the private key for this certificate.
+ pub fn set(&mut self) -> io::Result<()> {
+ 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 info = Cryptography::CRYPT_KEY_PROV_INFO {
+ pwszContainerName: container as *mut _,
+ pwszProvName: provider as *mut _,
+ dwProvType: self.type_,
+ dwFlags: self.flags,
+ cProvParam: 0,
+ rgProvParam: ptr::null_mut(),
+ dwKeySpec: self.key_spec,
+ };
+
+ let res = Cryptography::CertSetCertificateContextProperty(
+ self.cert.0,
+ Cryptography::CERT_KEY_PROV_INFO_PROP_ID,
+ 0,
+ &info as *const _ as *const _,
+ );
+ if res != 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+}
+
+/// The specification of a private key.
+#[derive(Copy, Clone)]
+pub struct KeySpec(u32);
+
+impl KeySpec {
+ /// A key used to encrypt/decrypt session keys.
+ pub fn key_exchange() -> KeySpec {
+ KeySpec(Cryptography::AT_KEYEXCHANGE)
+ }
+
+ /// A key used to create and verify digital signatures.
+ pub fn signature() -> KeySpec {
+ KeySpec(Cryptography::AT_SIGNATURE)
+ }
+}
+
+/// Valid uses of a Certificate - All, or specific OIDs
+pub enum ValidUses {
+ /// Certificate is valid for all uses
+ All,
+
+ /// Certificate is valid for uses specified. No entries means that the certificate
+ /// has no valid uses.
+ Oids(Vec<String>),
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn decode() {
+ let der = include_bytes!("../test/cert.der");
+ let pem = include_str!("../test/cert.pem");
+
+ let der = CertContext::new(der).unwrap();
+ let pem = CertContext::from_pem(pem).unwrap();
+ assert_eq!(der, pem);
+ }
+
+ #[test]
+ fn certcontext_to_der() {
+ let der = include_bytes!("../test/cert.der");
+ let cert = CertContext::new(der).unwrap();
+ let der2 = CertContext::to_der(&cert);
+ assert_eq!(der as &[u8], der2);
+ }
+
+ #[test]
+ fn certcontext_to_pem() {
+ let der = include_bytes!("../test/cert.der");
+ let pem1 = include_str!("../test/cert.pem").replace('\r', "");
+
+ let der = CertContext::new(der).unwrap();
+ let pem2 = CertContext::to_pem(&der).unwrap().replace('\r', "");
+ assert_eq!(pem1, pem2);
+ }
+
+ #[test]
+ fn fingerprint() {
+ let der = include_bytes!("../test/cert.der");
+ let pem = include_str!("../test/cert.pem");
+
+ let der = CertContext::new(der).unwrap();
+ let pem = CertContext::from_pem(pem).unwrap();
+
+ let hash = der.fingerprint(HashAlgorithm::sha1()).unwrap();
+ assert_eq!(
+ hash,
+ vec![
+ 0x59, 0x17, 0x2D, 0x93, 0x13, 0xE8, 0x44, 0x59, 0xBC, 0xFF, 0x27, 0xF9, 0x67, 0xE7,
+ 0x9E, 0x6E, 0x92, 0x17, 0xE5, 0x84
+ ]
+ );
+ assert_eq!(hash, pem.fingerprint(HashAlgorithm::sha1()).unwrap());
+
+ let hash = der.fingerprint(HashAlgorithm::sha256()).unwrap();
+ assert_eq!(
+ hash,
+ vec![
+ 0x47, 0x12, 0xB9, 0x39, 0xFB, 0xCB, 0x42, 0xA6, 0xB5, 0x10, 0x1B, 0x42, 0x13, 0x9A,
+ 0x25, 0xB1, 0x4F, 0x81, 0xB4, 0x18, 0xFA, 0xCA, 0xBD, 0x37, 0x87, 0x46, 0xF1, 0x2F,
+ 0x85, 0xCC, 0x65, 0x44
+ ]
+ );
+ assert_eq!(hash, pem.fingerprint(HashAlgorithm::sha256()).unwrap());
+ }
+}
diff --git a/vendor/schannel/src/cert_store.rs b/vendor/schannel/src/cert_store.rs
new file mode 100644
index 0000000..484eaab
--- /dev/null
+++ b/vendor/schannel/src/cert_store.rs
@@ -0,0 +1,471 @@
+//! Bindings to winapi's certificate-store related APIs.
+
+use std::cmp;
+use std::ffi::OsStr;
+use std::fmt;
+use std::io;
+use std::mem;
+use std::os::windows::prelude::*;
+use std::ptr;
+
+use windows_sys::Win32::Security::Cryptography;
+
+use crate::cert_context::CertContext;
+use crate::ctl_context::CtlContext;
+use crate::Inner;
+
+/// Representation of certificate store on Windows, wrapping a `HCERTSTORE`.
+pub struct CertStore(Cryptography::HCERTSTORE);
+
+unsafe impl Sync for CertStore {}
+unsafe impl Send for CertStore {}
+
+impl fmt::Debug for CertStore {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("CertStore").finish()
+ }
+}
+
+impl Drop for CertStore {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::CertCloseStore(self.0, 0);
+ }
+ }
+}
+
+impl Clone for CertStore {
+ fn clone(&self) -> CertStore {
+ unsafe { CertStore(Cryptography::CertDuplicateStore(self.0)) }
+ }
+}
+
+inner!(CertStore, Cryptography::HCERTSTORE);
+
+/// Argument to the `add_cert` function indicating how a certificate should be
+/// added to a `CertStore`.
+pub enum CertAdd {
+ /// The function makes no check for an existing matching certificate or link
+ /// to a matching certificate. A new certificate is always added to the
+ /// store. This can lead to duplicates in a store.
+ Always = Cryptography::CERT_STORE_ADD_ALWAYS as isize,
+
+ /// If a matching certificate or a link to a matching certificate exists,
+ /// the operation fails.
+ New = Cryptography::CERT_STORE_ADD_NEW as isize,
+
+ /// If a matching certificate or a link to a matching certificate exists and
+ /// the NotBefore time of the existing context is equal to or greater than
+ /// the NotBefore time of the new context being added, the operation fails.
+ ///
+ /// If the NotBefore time of the existing context is less than the NotBefore
+ /// time of the new context being added, the existing certificate or link is
+ /// deleted and a new certificate is created and added to the store. If a
+ /// matching certificate or a link to a matching certificate does not exist,
+ /// a new link is added.
+ Newer = Cryptography::CERT_STORE_ADD_NEWER as isize,
+
+ /// If a matching certificate or a link to a matching certificate exists and
+ /// the NotBefore time of the existing context is equal to or greater than
+ /// the NotBefore time of the new context being added, the operation fails.
+ ///
+ /// If the NotBefore time of the existing context is less than the NotBefore
+ /// time of the new context being added, the existing context is deleted
+ /// before creating and adding the new context. The new added context
+ /// inherits properties from the existing certificate.
+ NewerInheritProperties = Cryptography::CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES as isize,
+
+ /// If a link to a matching certificate exists, that existing certificate or
+ /// link is deleted and a new certificate is created and added to the store.
+ /// If a matching certificate or a link to a matching certificate does not
+ /// exist, a new link is added.
+ ReplaceExisting = Cryptography::CERT_STORE_ADD_REPLACE_EXISTING as isize,
+
+ /// If a matching certificate exists in the store, the existing context is
+ /// not replaced. The existing context inherits properties from the new
+ /// certificate.
+ ReplaceExistingInheritProperties =
+ Cryptography::CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES as isize,
+
+ /// If a matching certificate or a link to a matching certificate exists,
+ /// that existing certificate or link is used and properties from the
+ /// new certificate are added. The function does not fail, but it does
+ /// not add a new context. The existing context is duplicated and returned.
+ ///
+ /// If a matching certificate or a link to a matching certificate does
+ /// not exist, a new certificate is added.
+ UseExisting = Cryptography::CERT_STORE_ADD_USE_EXISTING as isize,
+}
+
+impl CertStore {
+ /// Opens up the specified key store within the context of the current user.
+ ///
+ /// Common valid values for `which` are "My", "Root", "Trust", "CA".
+ /// Additonal MSDN docs https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore#remarks
+ pub fn open_current_user(which: &str) -> io::Result<CertStore> {
+ unsafe {
+ let data = OsStr::new(which)
+ .encode_wide()
+ .chain(Some(0))
+ .collect::<Vec<_>>();
+ let store = Cryptography::CertOpenStore(
+ Cryptography::CERT_STORE_PROV_SYSTEM_W,
+ Cryptography::CERT_QUERY_ENCODING_TYPE::default(),
+ Cryptography::HCRYPTPROV_LEGACY::default(),
+ Cryptography::CERT_SYSTEM_STORE_CURRENT_USER_ID
+ << Cryptography::CERT_SYSTEM_STORE_LOCATION_SHIFT,
+ data.as_ptr() as *mut _,
+ );
+ if !store.is_null() {
+ Ok(CertStore(store))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Opens up the specified key store within the context of the local machine.
+ ///
+ /// Common valid values for `which` are "My", "Root", "Trust", "CA".
+ /// Additonal MSDN docs https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certopenstore#remarks
+ pub fn open_local_machine(which: &str) -> io::Result<CertStore> {
+ unsafe {
+ let data = OsStr::new(which)
+ .encode_wide()
+ .chain(Some(0))
+ .collect::<Vec<_>>();
+ let store = Cryptography::CertOpenStore(
+ Cryptography::CERT_STORE_PROV_SYSTEM_W,
+ Cryptography::CERT_QUERY_ENCODING_TYPE::default(),
+ Cryptography::HCRYPTPROV_LEGACY::default(),
+ Cryptography::CERT_SYSTEM_STORE_LOCAL_MACHINE_ID
+ << Cryptography::CERT_SYSTEM_STORE_LOCATION_SHIFT,
+ data.as_ptr() as *mut _,
+ );
+ if !store.is_null() {
+ Ok(CertStore(store))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Imports a PKCS#12-encoded key/certificate pair, returned as a
+ /// `CertStore` instance.
+ ///
+ /// The password must also be provided to decrypt the encoded data.
+ pub fn import_pkcs12(data: &[u8], password: Option<&str>) -> io::Result<CertStore> {
+ unsafe {
+ let blob = Cryptography::CRYPT_INTEGER_BLOB {
+ cbData: data.len() as u32,
+ pbData: data.as_ptr() as *mut u8,
+ };
+ let password = password.map(|s| {
+ OsStr::new(s)
+ .encode_wide()
+ .chain(Some(0))
+ .collect::<Vec<_>>()
+ });
+ let password = password.as_ref().map(|s| s.as_ptr());
+ let password = password.unwrap_or(ptr::null());
+ let res = Cryptography::PFXImportCertStore(
+ &blob,
+ password,
+ Cryptography::CRYPT_KEY_FLAGS::default(),
+ );
+ if !res.is_null() {
+ Ok(CertStore(res))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Returns an iterator over the certificates in this certificate store.
+ pub fn certs(&self) -> Certs {
+ Certs {
+ store: self,
+ cur: None,
+ }
+ }
+
+ /// Adds a certificate context to this store.
+ ///
+ /// This function will add the certificate specified in `cx` to this store.
+ /// A copy of the added certificate is returned.
+ pub fn add_cert(&mut self, cx: &CertContext, how: CertAdd) -> io::Result<CertContext> {
+ unsafe {
+ let how = how as u32;
+ let mut ret = ptr::null_mut();
+ let res = Cryptography::CertAddCertificateContextToStore(
+ self.0,
+ cx.as_inner(),
+ how,
+ &mut ret,
+ );
+ if res == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(CertContext::from_inner(ret))
+ }
+ }
+ }
+
+ /// Exports this certificate store as a PKCS#12-encoded blob.
+ ///
+ /// The password specified will be the password used to unlock the returned
+ /// data.
+ pub fn export_pkcs12(&self, password: &str) -> io::Result<Vec<u8>> {
+ unsafe {
+ let password = password.encode_utf16().chain(Some(0)).collect::<Vec<_>>();
+ let mut blob = mem::zeroed();
+ let res = Cryptography::PFXExportCertStore(
+ self.0,
+ &mut blob,
+ password.as_ptr(),
+ Cryptography::EXPORT_PRIVATE_KEYS,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ let mut ret = Vec::with_capacity(blob.cbData as usize);
+ blob.pbData = ret.as_mut_ptr();
+ let res = Cryptography::PFXExportCertStore(
+ self.0,
+ &mut blob,
+ password.as_ptr(),
+ Cryptography::EXPORT_PRIVATE_KEYS,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ ret.set_len(blob.cbData as usize);
+ Ok(ret)
+ }
+ }
+}
+
+/// An iterator over the certificates contained in a `CertStore`, returned by
+/// `CertStore::iter`
+pub struct Certs<'a> {
+ store: &'a CertStore,
+ cur: Option<CertContext>,
+}
+
+impl<'a> Iterator for Certs<'a> {
+ type Item = CertContext;
+
+ fn next(&mut self) -> Option<CertContext> {
+ unsafe {
+ let cur = self.cur.take().map(|p| {
+ let ptr = p.as_inner();
+ mem::forget(p);
+ ptr
+ });
+ let cur = cur.unwrap_or(ptr::null_mut());
+ let next = Cryptography::CertEnumCertificatesInStore(self.store.0, cur);
+
+ if next.is_null() {
+ self.cur = None;
+ None
+ } else {
+ let next = CertContext::from_inner(next);
+ self.cur = Some(next.clone());
+ Some(next)
+ }
+ }
+ }
+}
+
+/// A builder type for imports of PKCS #12 archives.
+#[derive(Default)]
+pub struct PfxImportOptions {
+ password: Option<Vec<u16>>,
+ flags: u32,
+}
+
+impl PfxImportOptions {
+ /// Returns a new `PfxImportOptions` with default settings.
+ pub fn new() -> PfxImportOptions {
+ PfxImportOptions::default()
+ }
+
+ /// Sets the password to be used to decrypt the archive.
+ pub fn password(&mut self, password: &str) -> &mut PfxImportOptions {
+ self.password = Some(password.encode_utf16().chain(Some(0)).collect());
+ self
+ }
+
+ /// If set, the private key in the archive will not be persisted.
+ ///
+ /// If not set, private keys are persisted on disk and must be manually deleted.
+ pub fn no_persist_key(&mut self, no_persist_key: bool) -> &mut PfxImportOptions {
+ self.flag(Cryptography::PKCS12_NO_PERSIST_KEY, no_persist_key)
+ }
+
+ /// If set, all extended properties of the certificate will be imported.
+ pub fn include_extended_properties(
+ &mut self,
+ include_extended_properties: bool,
+ ) -> &mut PfxImportOptions {
+ self.flag(
+ Cryptography::PKCS12_INCLUDE_EXTENDED_PROPERTIES,
+ include_extended_properties,
+ )
+ }
+
+ fn flag(&mut self, flag: u32, set: bool) -> &mut PfxImportOptions {
+ if set {
+ self.flags |= flag;
+ } else {
+ self.flags &= !flag;
+ }
+ self
+ }
+
+ /// Imports certificates from a PKCS #12 archive, returning a `CertStore` containing them.
+ pub fn import(&self, data: &[u8]) -> io::Result<CertStore> {
+ unsafe {
+ let blob = Cryptography::CRYPT_INTEGER_BLOB {
+ cbData: cmp::min(data.len(), u32::max_value() as usize) as u32,
+ pbData: data.as_ptr() as *mut _,
+ };
+ let password = self.password.as_ref().map_or(ptr::null(), |p| p.as_ptr());
+
+ let store = Cryptography::PFXImportCertStore(&blob, password, self.flags);
+ if !store.is_null() {
+ Ok(CertStore(store))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+}
+
+/// Representation of an in-memory certificate store.
+///
+/// Internally this contains a `CertStore` which this type can be converted to.
+pub struct Memory(CertStore);
+
+impl Memory {
+ /// Creates a new in-memory certificate store which certificates and CTLs
+ /// can be added to.
+ ///
+ /// Initially the returned certificate store contains no certificates.
+ pub fn new() -> io::Result<Memory> {
+ unsafe {
+ let store = Cryptography::CertOpenStore(
+ Cryptography::CERT_STORE_PROV_MEMORY,
+ Cryptography::CERT_QUERY_ENCODING_TYPE::default(),
+ Cryptography::HCRYPTPROV_LEGACY::default(),
+ Cryptography::CERT_OPEN_STORE_FLAGS::default(),
+ ptr::null_mut(),
+ );
+ if !store.is_null() {
+ Ok(Memory(CertStore(store)))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Adds a new certificate to this memory store.
+ ///
+ /// For example the bytes could be a DER-encoded certificate.
+ pub fn add_encoded_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext> {
+ unsafe {
+ let mut cert_context = ptr::null_mut();
+
+ let res = Cryptography::CertAddEncodedCertificateToStore(
+ (self.0).0,
+ Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING,
+ cert.as_ptr(),
+ cert.len() as u32,
+ Cryptography::CERT_STORE_ADD_ALWAYS,
+ &mut cert_context,
+ );
+ if res != 0 {
+ Ok(CertContext::from_inner(cert_context))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Adds a new CTL to this memory store, in its encoded form.
+ ///
+ /// This can be created through the `ctl_context::Builder` type.
+ pub fn add_encoded_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext> {
+ unsafe {
+ let mut ctl_context = ptr::null_mut();
+
+ let res = Cryptography::CertAddEncodedCTLToStore(
+ (self.0).0,
+ Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING,
+ ctl.as_ptr(),
+ ctl.len() as u32,
+ Cryptography::CERT_STORE_ADD_ALWAYS,
+ &mut ctl_context,
+ );
+ if res != 0 {
+ Ok(CtlContext::from_inner(ctl_context))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Consumes this memory store, returning the underlying `CertStore`.
+ pub fn into_store(self) -> CertStore {
+ self.0
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::ctl_context::CtlContext;
+
+ use super::*;
+
+ #[test]
+ fn load() {
+ let cert = include_bytes!("../test/cert.der");
+ let mut store = Memory::new().unwrap();
+ store.add_encoded_certificate(cert).unwrap();
+ }
+
+ #[test]
+ fn create_ctl() {
+ let cert = include_bytes!("../test/self-signed.badssl.com.cer");
+ let mut store = Memory::new().unwrap();
+ let cert = store.add_encoded_certificate(cert).unwrap();
+
+ CtlContext::builder()
+ .certificate(cert)
+ .usage("1.3.6.1.4.1.311.2.2.2")
+ .encode_and_sign()
+ .unwrap();
+ }
+
+ #[test]
+ fn pfx_import() {
+ let pfx = include_bytes!("../test/identity.p12");
+ let store = PfxImportOptions::new()
+ .include_extended_properties(true)
+ .password("mypass")
+ .import(pfx)
+ .unwrap();
+ assert_eq!(store.certs().count(), 2);
+ let pkeys = store
+ .certs()
+ .filter(|c| {
+ c.private_key()
+ .compare_key(true)
+ .silent(true)
+ .acquire()
+ .is_ok()
+ })
+ .count();
+ assert_eq!(pkeys, 1);
+ }
+}
diff --git a/vendor/schannel/src/context_buffer.rs b/vendor/schannel/src/context_buffer.rs
new file mode 100644
index 0000000..99a8f59
--- /dev/null
+++ b/vendor/schannel/src/context_buffer.rs
@@ -0,0 +1,26 @@
+use std::ops::Deref;
+use std::slice;
+
+use windows_sys::Win32::Security::Authentication::Identity;
+
+pub struct ContextBuffer(pub Identity::SecBuffer);
+
+impl Drop for ContextBuffer {
+ fn drop(&mut self) {
+ unsafe {
+ Identity::FreeContextBuffer(self.0.pvBuffer);
+ }
+ }
+}
+
+impl Deref for ContextBuffer {
+ type Target = [u8];
+
+ fn deref(&self) -> &[u8] {
+ if self.0.cbBuffer == 0 {
+ return &[];
+ }
+
+ unsafe { slice::from_raw_parts(self.0.pvBuffer as *const _, self.0.cbBuffer as usize) }
+ }
+}
diff --git a/vendor/schannel/src/crypt_key.rs b/vendor/schannel/src/crypt_key.rs
new file mode 100644
index 0000000..4da97e5
--- /dev/null
+++ b/vendor/schannel/src/crypt_key.rs
@@ -0,0 +1,16 @@
+//! CryptoAPI private keys.
+
+use windows_sys::Win32::Security::Cryptography;
+
+/// A handle to a key.
+pub struct CryptKey(usize);
+
+impl Drop for CryptKey {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::CryptDestroyKey(self.0);
+ }
+ }
+}
+
+inner!(CryptKey, usize);
diff --git a/vendor/schannel/src/crypt_prov.rs b/vendor/schannel/src/crypt_prov.rs
new file mode 100644
index 0000000..9e5cd0c
--- /dev/null
+++ b/vendor/schannel/src/crypt_prov.rs
@@ -0,0 +1,420 @@
+//! CryptoAPI key providers.
+use std::ffi::c_void;
+use std::io;
+use std::ptr;
+use std::slice;
+
+use windows_sys::Win32::Security::Cryptography;
+use windows_sys::Win32::System::Memory;
+
+use crate::crypt_key::CryptKey;
+use crate::Inner;
+
+/// A CryptoAPI handle to a provider of a key.
+pub struct CryptProv(Cryptography::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE);
+
+impl Drop for CryptProv {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::CryptReleaseContext(self.0, 0);
+ }
+ }
+}
+
+inner!(CryptProv, Cryptography::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE);
+
+impl CryptProv {
+ /// Imports a key into this provider.
+ pub fn import(&mut self) -> ImportOptions {
+ ImportOptions {
+ prov: self,
+ flags: 0,
+ }
+ }
+}
+
+/// A builder for `CryptProv`s.
+#[derive(Default)]
+pub struct AcquireOptions {
+ container: Option<Vec<u16>>,
+ provider: Option<Vec<u16>>,
+ flags: u32,
+}
+
+impl AcquireOptions {
+ /// Returns a new builder with default settings.
+ pub fn new() -> AcquireOptions {
+ AcquireOptions::default()
+ }
+
+ /// Sets the name for this key container.
+ ///
+ /// This should not be set if `verify_context` is set.
+ pub fn container(&mut self, container: &str) -> &mut AcquireOptions {
+ self.container = Some(container.encode_utf16().chain(Some(0)).collect());
+ self
+ }
+
+ /// Sets the name of the CSP to be used.
+ pub fn provider(&mut self, provider: &str) -> &mut AcquireOptions {
+ self.provider = Some(provider.encode_utf16().chain(Some(0)).collect());
+ self
+ }
+
+ /// If set, private keys will not be accessible or persisted.
+ pub fn verify_context(&mut self, verify_context: bool) -> &mut AcquireOptions {
+ self.flag(Cryptography::CRYPT_VERIFYCONTEXT, verify_context)
+ }
+
+ /// If set, the container will be created.
+ pub fn new_keyset(&mut self, new_keyset: bool) -> &mut AcquireOptions {
+ self.flag(Cryptography::CRYPT_NEWKEYSET, new_keyset)
+ }
+
+ /// If set, the container will be stored as a machine rather than user keys.
+ pub fn machine_keyset(&mut self, machine_keyset: bool) -> &mut AcquireOptions {
+ self.flag(Cryptography::CRYPT_MACHINE_KEYSET, machine_keyset)
+ }
+
+ /// If set, an error will be returned if user intervention is required
+ /// rather than displaying a dialog.
+ pub fn silent(&mut self, silent: bool) -> &mut AcquireOptions {
+ self.flag(Cryptography::CRYPT_SILENT, silent)
+ }
+
+ fn flag(&mut self, flag: u32, on: bool) -> &mut AcquireOptions {
+ if on {
+ self.flags |= flag;
+ } else {
+ self.flags &= !flag;
+ }
+
+ self
+ }
+
+ /// 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 mut prov = 0;
+ let res = Cryptography::CryptAcquireContextW(
+ &mut prov,
+ container as *mut _,
+ provider as *mut _,
+ type_.0,
+ self.flags,
+ );
+ if res != 0 {
+ Ok(CryptProv(prov))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+}
+
+/// An identifier of the type of cryptography provider to be used with a
+/// container.
+#[derive(Copy, Clone)]
+pub struct ProviderType(u32);
+
+#[allow(missing_docs)]
+impl ProviderType {
+ pub fn rsa_full() -> ProviderType {
+ ProviderType(Cryptography::PROV_RSA_FULL)
+ }
+
+ pub fn rsa_aes() -> ProviderType {
+ ProviderType(Cryptography::PROV_RSA_AES)
+ }
+
+ pub fn rsa_sig() -> ProviderType {
+ ProviderType(Cryptography::PROV_RSA_SIG)
+ }
+
+ pub fn rsa_schannel() -> ProviderType {
+ ProviderType(Cryptography::PROV_RSA_SCHANNEL)
+ }
+
+ pub fn dss() -> ProviderType {
+ ProviderType(Cryptography::PROV_DSS)
+ }
+
+ pub fn dss_dh() -> ProviderType {
+ ProviderType(Cryptography::PROV_DSS_DH)
+ }
+
+ pub fn dh_schannel() -> ProviderType {
+ ProviderType(Cryptography::PROV_DH_SCHANNEL)
+ }
+
+ pub fn fortezza() -> ProviderType {
+ ProviderType(Cryptography::PROV_FORTEZZA)
+ }
+
+ pub fn ms_exchange() -> ProviderType {
+ ProviderType(Cryptography::PROV_MS_EXCHANGE)
+ }
+
+ pub fn ssl() -> ProviderType {
+ ProviderType(Cryptography::PROV_SSL)
+ }
+
+ pub fn as_raw(&self) -> u32 {
+ self.0
+ }
+}
+
+/// A builder for key imports.
+pub struct ImportOptions<'a> {
+ prov: &'a mut CryptProv,
+ flags: u32,
+}
+
+impl<'a> ImportOptions<'a> {
+ /// Imports a DER-encoded PKCS1 private key.
+ pub fn import(&mut self, der: &[u8]) -> io::Result<CryptKey> {
+ unsafe {
+ assert!(der.len() <= u32::max_value() as usize);
+ let mut buf = ptr::null_mut();
+ let mut len = 0;
+ let res = Cryptography::CryptDecodeObjectEx(
+ Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING,
+ Cryptography::PKCS_RSA_PRIVATE_KEY,
+ der.as_ptr(),
+ der.len() as u32,
+ Cryptography::CRYPT_DECODE_ALLOC_FLAG,
+ ptr::null_mut(),
+ &mut buf as *mut _ as *mut c_void,
+ &mut len,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut key = 0;
+ let res = Cryptography::CryptImportKey(self.prov.0, buf, len, 0, self.flags, &mut key);
+ Memory::LocalFree(buf as isize);
+
+ if res != 0 {
+ Ok(CryptKey::from_inner(key))
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+
+ /// Imports a DER-encoded PKCS8 private key.
+ pub fn import_pkcs8(&mut self, der: &[u8]) -> io::Result<CryptKey> {
+ unsafe {
+ assert!(der.len() <= u32::max_value() as usize);
+
+ // Decode the der format into a CRYPT_PRIVATE_KEY_INFO struct
+ let mut buf = ptr::null_mut();
+ let mut len = 0;
+ let res = Cryptography::CryptDecodeObjectEx(
+ Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING,
+ Cryptography::PKCS_PRIVATE_KEY_INFO,
+ der.as_ptr(),
+ der.len() as u32,
+ Cryptography::CRYPT_DECODE_ALLOC_FLAG,
+ ptr::null_mut(),
+ &mut buf as *mut _ as *mut c_void,
+ &mut len,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ let pkey: Cryptography::CRYPT_PRIVATE_KEY_INFO = *buf;
+ let pkey = pkey.PrivateKey;
+
+ let res = self.import(slice::from_raw_parts(pkey.pbData, pkey.cbData as usize));
+ Memory::LocalFree(buf as isize);
+ res
+ }
+ }
+
+ /// Imports a PEM-encoded PKCS8 private key.
+ /// This functions decodes PEM blocks with or without "-----BEGIN PRIVATE KEY-----"
+ /// and "-----END PRIVATE KEY-----" headers, but if PEM guards are present they must be exactly
+ /// these.
+ pub fn import_pkcs8_pem(&mut self, pem: &[u8]) -> io::Result<CryptKey> {
+ let pem_str = std::str::from_utf8(pem)
+ .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid utf-8"))?
+ .trim();
+
+ if pem_str.starts_with("-----")
+ && (!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 {
+ assert!(pem.len() <= u32::max_value() as usize);
+
+ // 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 = Cryptography::CryptStringToBinaryA(
+ pem.as_ptr(),
+ pem.len() as u32,
+ Cryptography::CRYPT_STRING_BASE64_ANY,
+ ptr::null_mut(),
+ &mut len,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ );
+ if res == 0 {
+ 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 = Cryptography::CryptStringToBinaryA(
+ pem.as_ptr(),
+ pem.len() as u32,
+ Cryptography::CRYPT_STRING_BASE64_ANY,
+ der_buf.as_mut_ptr(),
+ &mut len,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ self.import_pkcs8(&der_buf)
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use windows_sys::Win32::Security::Cryptography::CRYPT_STRING_BASE64HEADER;
+
+ use super::*;
+
+ #[test]
+ fn rsa_key() {
+ let key = include_bytes!("../test/key.key");
+
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .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_ne!(
+ Cryptography::CryptStringToBinaryA(
+ key.as_ptr(),
+ key.len() as u32,
+ CRYPT_STRING_BASE64HEADER,
+ ptr::null_mut(),
+ &mut len,
+ ptr::null_mut(),
+ ptr::null_mut()
+ ),
+ 0
+ );
+ let mut buf = vec![0; len as usize];
+ assert_ne!(
+ Cryptography::CryptStringToBinaryA(
+ key.as_ptr(),
+ key.len() as u32,
+ CRYPT_STRING_BASE64HEADER,
+ buf.as_mut_ptr(),
+ &mut len,
+ ptr::null_mut(),
+ ptr::null_mut()
+ ),
+ 0
+ );
+ buf
+ };
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ context.import().import_pkcs8(&der).unwrap();
+ }
+
+ #[test]
+ // this also covers rejecting a pkcs1 key through import_pkcs8_pem
+ fn pkcs8_key_reject_pkcs1() {
+ let key = include_bytes!("../test/key.key");
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ assert!(context.import().import_pkcs8(&key[..]).is_err());
+ }
+
+ #[test]
+ fn pkcs8_key_pem() {
+ let key = include_bytes!("../test/key.pem");
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ context.import().import_pkcs8_pem(key).unwrap();
+ }
+
+ #[test]
+ fn pkcs8_key_pem_no_headers() {
+ let key = include_bytes!("../test/key_no_headers.pem");
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ context.import().import_pkcs8_pem(key).unwrap();
+ }
+
+ #[test]
+ fn pkcs8_key_pem_no_end_header() {
+ let key = include_bytes!("../test/key_no_end_header.pem");
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ assert!(context.import().import_pkcs8_pem(key).is_err());
+ }
+
+ #[test]
+ fn pkcs8_key_pem_wrong_header() {
+ let key = include_bytes!("../test/key_wrong_header.pem");
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ assert!(context.import().import_pkcs8_pem(key).is_err());
+ }
+
+ #[test]
+ fn pkcs8_key_pem_invalid_header() {
+ let key = include_bytes!("../test/key_invalid_header.pem");
+ let mut context = AcquireOptions::new()
+ .verify_context(true)
+ .acquire(ProviderType::rsa_full())
+ .unwrap();
+ assert!(context.import().import_pkcs8_pem(key).is_err());
+ }
+}
diff --git a/vendor/schannel/src/ctl_context.rs b/vendor/schannel/src/ctl_context.rs
new file mode 100644
index 0000000..f73f45c
--- /dev/null
+++ b/vendor/schannel/src/ctl_context.rs
@@ -0,0 +1,187 @@
+//! Bindings to Certificate Trust Lists (CTL) in winapi.
+
+#![allow(dead_code)]
+
+use std::io;
+use std::mem;
+use std::ptr;
+
+use windows_sys::Win32::Security::Cryptography;
+
+use crate::cert_context::CertContext;
+use crate::Inner;
+
+/// Wrapped `PCCTL_CONTEXT` which represents a certificate trust list to
+/// Windows.
+pub struct CtlContext(*const Cryptography::CTL_CONTEXT);
+
+unsafe impl Send for CtlContext {}
+unsafe impl Sync for CtlContext {}
+
+impl Drop for CtlContext {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::CertFreeCTLContext(self.0);
+ }
+ }
+}
+
+impl Inner<*const Cryptography::CTL_CONTEXT> for CtlContext {
+ unsafe fn from_inner(t: *const Cryptography::CTL_CONTEXT) -> CtlContext {
+ CtlContext(t)
+ }
+
+ fn as_inner(&self) -> *const Cryptography::CTL_CONTEXT {
+ self.0
+ }
+
+ fn get_mut(&mut self) -> &mut *const Cryptography::CTL_CONTEXT {
+ &mut self.0
+ }
+}
+
+impl CtlContext {
+ /// Returns a builder reader to create an encoded `CtlContext`.
+ pub fn builder() -> Builder {
+ Builder {
+ certificates: vec![],
+ usages: vec![],
+ }
+ }
+}
+
+/// Used to build an encoded `CtlContext` which can be added to a `Memory` store
+/// to get back the actual `CtlContext`.
+pub struct Builder {
+ certificates: Vec<CertContext>,
+ usages: Vec<Vec<u8>>,
+}
+
+impl Builder {
+ /// Adds a certificate to be passed to `CryptMsgEncodeAndSignCTL` later on.
+ pub fn certificate(&mut self, cert: CertContext) -> &mut Builder {
+ self.certificates.push(cert);
+ self
+ }
+
+ /// Adds a usage string to be passed in the `SubjectUsage` field to
+ /// `CryptMsgEncodeAndSignCTL` later on.
+ pub fn usage(&mut self, usage: &str) -> &mut Builder {
+ let mut usage = usage.as_bytes().to_owned();
+ usage.push(0);
+ self.usages.push(usage);
+ self
+ }
+
+ /// Calls `CryptMsgEncodeAndSignCTL` to encode this list of certificates
+ /// into a CTL.
+ ///
+ /// This can later be passed to `Memory::add_encoded_ctl`.
+ pub fn encode_and_sign(&self) -> io::Result<Vec<u8>> {
+ unsafe {
+ let encoding = Cryptography::X509_ASN_ENCODING | Cryptography::PKCS_7_ASN_ENCODING;
+
+ let mut usages = self
+ .usages
+ .iter()
+ .map(|u| u.as_ptr() as _)
+ .collect::<Vec<_>>();
+ let mut entry_data = vec![];
+ let mut entries = vec![];
+ for certificate in &self.certificates {
+ let data = cert_entry(certificate)?;
+ entries.push(*(data.as_ptr() as *const Cryptography::CTL_ENTRY));
+ entry_data.push(data);
+ }
+
+ let mut ctl_info: Cryptography::CTL_INFO = mem::zeroed();
+ ctl_info.dwVersion = Cryptography::CTL_V1;
+ ctl_info.SubjectUsage.cUsageIdentifier = usages.len() as u32;
+ ctl_info.SubjectUsage.rgpszUsageIdentifier = usages.as_mut_ptr();
+ ctl_info.SubjectAlgorithm.pszObjId = Cryptography::szOID_OIWSEC_sha1 as _;
+ ctl_info.cCTLEntry = entries.len() as u32;
+ ctl_info.rgCTLEntry = entries.as_mut_ptr();
+
+ let mut sign_info: Cryptography::CMSG_SIGNED_ENCODE_INFO = mem::zeroed();
+ sign_info.cbSize = mem::size_of_val(&sign_info) as u32;
+ let mut encoded_certs = self
+ .certificates
+ .iter()
+ .map(|c| Cryptography::CRYPT_INTEGER_BLOB {
+ cbData: (*c.as_inner()).cbCertEncoded,
+ pbData: (*c.as_inner()).pbCertEncoded,
+ })
+ .collect::<Vec<_>>();
+ sign_info.rgCertEncoded = encoded_certs.as_mut_ptr();
+ sign_info.cCertEncoded = encoded_certs.len() as u32;
+
+ let flags = Cryptography::CMSG_ENCODE_SORTED_CTL_FLAG
+ | Cryptography::CMSG_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG;
+
+ let mut size = 0;
+
+ let res = Cryptography::CryptMsgEncodeAndSignCTL(
+ encoding,
+ &ctl_info,
+ &sign_info,
+ flags,
+ ptr::null_mut(),
+ &mut size,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut encoded = vec![0; size as usize];
+
+ let res = Cryptography::CryptMsgEncodeAndSignCTL(
+ encoding,
+ &ctl_info,
+ &sign_info,
+ flags,
+ encoded.as_mut_ptr() as *mut u8,
+ &mut size,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ Ok(encoded)
+ }
+ }
+}
+
+fn cert_entry(cert: &CertContext) -> io::Result<Vec<u8>> {
+ unsafe {
+ let mut size: u32 = 0;
+
+ let res = Cryptography::CertCreateCTLEntryFromCertificateContextProperties(
+ cert.as_inner(),
+ 0,
+ ptr::null(),
+ Cryptography::CTL_ENTRY_FROM_PROP_CHAIN_FLAG,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ &mut size,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut entry = vec![0u8; size as usize];
+ let res = Cryptography::CertCreateCTLEntryFromCertificateContextProperties(
+ cert.as_inner(),
+ 0,
+ ptr::null(),
+ Cryptography::CTL_ENTRY_FROM_PROP_CHAIN_FLAG,
+ ptr::null_mut(),
+ entry.as_mut_ptr() as *mut Cryptography::CTL_ENTRY,
+ &mut size,
+ );
+ if res == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(entry)
+ }
+ }
+}
diff --git a/vendor/schannel/src/key_handle.rs b/vendor/schannel/src/key_handle.rs
new file mode 100644
index 0000000..86f25b9
--- /dev/null
+++ b/vendor/schannel/src/key_handle.rs
@@ -0,0 +1,3 @@
+//! Deprecated.
+#[deprecated(note = "use cert_context::PrivateKey", since = "0.1.5")]
+pub use crate::cert_context::PrivateKey as KeyHandle;
diff --git a/vendor/schannel/src/lib.rs b/vendor/schannel/src/lib.rs
new file mode 100644
index 0000000..d071a69
--- /dev/null
+++ b/vendor/schannel/src/lib.rs
@@ -0,0 +1,114 @@
+//! Bindings to the Windows SChannel APIs.
+#![cfg(windows)]
+#![warn(missing_docs)]
+#![allow(non_upper_case_globals)]
+
+use std::ffi::c_void;
+use std::ptr;
+
+use windows_sys::Win32::Security::Authentication::Identity;
+
+macro_rules! inner {
+ ($t:path, $raw:ty) => {
+ impl crate::Inner<$raw> for $t {
+ unsafe fn from_inner(t: $raw) -> Self {
+ $t(t)
+ }
+
+ fn as_inner(&self) -> $raw {
+ self.0
+ }
+
+ fn get_mut(&mut self) -> &mut $raw {
+ &mut self.0
+ }
+ }
+
+ impl crate::RawPointer for $t {
+ unsafe fn from_ptr(t: *mut ::std::os::raw::c_void) -> $t {
+ $t(t as _)
+ }
+
+ unsafe fn as_ptr(&self) -> *mut ::std::os::raw::c_void {
+ self.0 as *mut _
+ }
+ }
+ };
+}
+
+/// Allows access to the underlying schannel API representation of a wrapped data type
+///
+/// Performing actions with internal handles might lead to the violation of internal assumptions
+/// and therefore is inherently unsafe.
+pub trait RawPointer {
+ /// Constructs an instance of this type from its handle / pointer.
+ /// # Safety
+ /// This function is unsafe
+ unsafe fn from_ptr(t: *mut ::std::os::raw::c_void) -> Self;
+
+ /// Get a raw pointer from the underlying handle / pointer.
+ /// # Safety
+ /// This function is unsafe
+ unsafe fn as_ptr(&self) -> *mut ::std::os::raw::c_void;
+}
+
+pub mod cert_chain;
+pub mod cert_context;
+pub mod cert_store;
+pub mod crypt_key;
+pub mod crypt_prov;
+/* pub */ mod ctl_context;
+pub mod key_handle;
+pub mod ncrypt_key;
+pub mod schannel_cred;
+pub mod tls_stream;
+
+mod alpn_list;
+mod context_buffer;
+mod security_context;
+
+#[cfg(test)]
+mod test;
+
+const ACCEPT_REQUESTS: u32 = Identity::ASC_REQ_ALLOCATE_MEMORY
+ | Identity::ASC_REQ_CONFIDENTIALITY
+ | Identity::ASC_REQ_SEQUENCE_DETECT
+ | Identity::ASC_REQ_STREAM
+ | Identity::ASC_REQ_REPLAY_DETECT;
+
+const INIT_REQUESTS: u32 = Identity::ISC_REQ_CONFIDENTIALITY
+ | Identity::ISC_REQ_INTEGRITY
+ | Identity::ISC_REQ_REPLAY_DETECT
+ | Identity::ISC_REQ_SEQUENCE_DETECT
+ | Identity::ISC_REQ_MANUAL_CRED_VALIDATION
+ | Identity::ISC_REQ_ALLOCATE_MEMORY
+ | Identity::ISC_REQ_STREAM
+ | Identity::ISC_REQ_USE_SUPPLIED_CREDS;
+
+trait Inner<T> {
+ unsafe fn from_inner(t: T) -> Self;
+
+ fn as_inner(&self) -> T;
+
+ fn get_mut(&mut self) -> &mut T;
+}
+
+unsafe fn secbuf(buftype: u32, bytes: Option<&mut [u8]>) -> Identity::SecBuffer {
+ let (ptr, len) = match bytes {
+ Some(bytes) => (bytes.as_mut_ptr(), bytes.len() as u32),
+ None => (ptr::null_mut(), 0),
+ };
+ Identity::SecBuffer {
+ BufferType: buftype,
+ cbBuffer: len,
+ pvBuffer: ptr as *mut c_void,
+ }
+}
+
+unsafe fn secbuf_desc(bufs: &mut [Identity::SecBuffer]) -> Identity::SecBufferDesc {
+ Identity::SecBufferDesc {
+ ulVersion: Identity::SECBUFFER_VERSION,
+ cBuffers: bufs.len() as u32,
+ pBuffers: bufs.as_mut_ptr(),
+ }
+}
diff --git a/vendor/schannel/src/ncrypt_key.rs b/vendor/schannel/src/ncrypt_key.rs
new file mode 100644
index 0000000..e4104fc
--- /dev/null
+++ b/vendor/schannel/src/ncrypt_key.rs
@@ -0,0 +1,16 @@
+//! CNG private keys.
+
+use windows_sys::Win32::Security::Cryptography;
+
+/// A CNG handle to a key.
+pub struct NcryptKey(Cryptography::NCRYPT_KEY_HANDLE);
+
+impl Drop for NcryptKey {
+ fn drop(&mut self) {
+ unsafe {
+ Cryptography::NCryptFreeObject(self.0);
+ }
+ }
+}
+
+inner!(NcryptKey, Cryptography::NCRYPT_KEY_HANDLE);
diff --git a/vendor/schannel/src/schannel_cred.rs b/vendor/schannel/src/schannel_cred.rs
new file mode 100644
index 0000000..c4b21d1
--- /dev/null
+++ b/vendor/schannel/src/schannel_cred.rs
@@ -0,0 +1,292 @@
+//! Schannel credentials.
+use std::ptr;
+use std::sync::Arc;
+use std::{io, mem};
+
+use windows_sys::Win32::Foundation;
+use windows_sys::Win32::Security::Authentication::Identity;
+use windows_sys::Win32::Security::{Credentials, Cryptography};
+
+use crate::cert_context::CertContext;
+use crate::Inner;
+
+/// The communication direction that an `SchannelCred` will support.
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub enum Direction {
+ /// Server-side, inbound connections.
+ Inbound,
+ /// Client-side, outbound connections.
+ Outbound,
+}
+
+/// Algorithms supported by Schannel.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
+#[derive(Debug, Copy, Clone)]
+#[repr(u32)]
+#[non_exhaustive]
+pub enum Algorithm {
+ /// Advanced Encryption Standard (AES).
+ Aes = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_AES,
+ /// 128 bit AES.
+ Aes128 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_AES_128,
+ /// 192 bit AES.
+ Aes192 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_AES_192,
+ /// 256 bit AES.
+ Aes256 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_AES_256,
+ /// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
+ AgreedkeyAny = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_DH
+ | Cryptography::ALG_SID_AGREED_KEY_ANY,
+ /// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make
+ /// its key length 64 bits.
+ CylinkMek = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_CYLINK_MEK,
+ /// DES encryption algorithm.
+ Des = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_DES,
+ /// DESX encryption algorithm.
+ Desx = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_DESX,
+ /// Diffie-Hellman ephemeral key exchange algorithm.
+ DhEphem = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_DH
+ | Cryptography::ALG_SID_DH_EPHEM,
+ /// Diffie-Hellman store and forward key exchange algorithm.
+ DhSf = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_DH
+ | Cryptography::ALG_SID_DH_SANDF,
+ /// DSA public key signature algorithm.
+ DssSign = Cryptography::ALG_CLASS_SIGNATURE
+ | Cryptography::ALG_TYPE_DSS
+ | Cryptography::ALG_SID_DSS_ANY,
+ /// Elliptic curve Diffie-Hellman key exchange algorithm.
+ Ecdh = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_DH
+ | Cryptography::ALG_SID_ECDH,
+ /// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
+ EcdhEphem = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_ECDH
+ | Cryptography::ALG_SID_ECDH_EPHEM,
+ /// Elliptic curve digital signature algorithm.
+ Ecdsa = Cryptography::ALG_CLASS_SIGNATURE
+ | Cryptography::ALG_TYPE_DSS
+ | Cryptography::ALG_SID_ECDSA,
+ /// One way function hashing algorithm.
+ HashReplaceOwf = Cryptography::ALG_CLASS_HASH
+ | Cryptography::ALG_TYPE_ANY
+ | Cryptography::ALG_SID_HASH_REPLACE_OWF,
+ /// Hughes MD5 hashing algorithm.
+ HughesMd5 = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_ANY
+ | Cryptography::ALG_SID_MD5,
+ /// HMAC keyed hash algorithm.
+ Hmac = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_HMAC,
+ /// MAC keyed hash algorithm.
+ Mac = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MAC,
+ /// MD2 hashing algorithm.
+ Md2 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD2,
+ /// MD4 hashing algorithm.
+ Md4 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD4,
+ /// MD5 hashing algorithm.
+ Md5 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_MD5,
+ /// No signature algorithm..
+ NoSign =
+ Cryptography::ALG_CLASS_SIGNATURE | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_ANY,
+ /// RC2 block encryption algorithm.
+ Rc2 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_RC2,
+ /// RC4 stream encryption algorithm.
+ Rc4 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_STREAM
+ | Cryptography::ALG_SID_RC4,
+ /// RC5 block encryption algorithm.
+ Rc5 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_RC5,
+ /// RSA public key exchange algorithm.
+ RsaKeyx = Cryptography::ALG_CLASS_KEY_EXCHANGE
+ | Cryptography::ALG_TYPE_RSA
+ | Cryptography::ALG_SID_RSA_ANY,
+ /// RSA public key signature algorithm.
+ RsaSign = Cryptography::ALG_CLASS_SIGNATURE
+ | Cryptography::ALG_TYPE_RSA
+ | Cryptography::ALG_SID_RSA_ANY,
+ /// SHA hashing algorithm.
+ Sha1 = Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA1,
+ /// 256 bit SHA hashing algorithm.
+ Sha256 =
+ Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA_256,
+ /// 384 bit SHA hashing algorithm.
+ Sha384 =
+ Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA_384,
+ /// 512 bit SHA hashing algorithm.
+ Sha512 =
+ Cryptography::ALG_CLASS_HASH | Cryptography::ALG_TYPE_ANY | Cryptography::ALG_SID_SHA_512,
+ /// Triple DES encryption algorithm.
+ TripleDes = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_3DES,
+ /// Two-key triple DES encryption with effective key length equal to 112 bits.
+ TripleDes112 = Cryptography::ALG_CLASS_DATA_ENCRYPT
+ | Cryptography::ALG_TYPE_BLOCK
+ | Cryptography::ALG_SID_3DES_112,
+}
+
+/// Protocols supported by Schannel.
+#[derive(Debug, Copy, Clone)]
+#[non_exhaustive]
+pub enum Protocol {
+ /// Secure Sockets Layer 3.0
+ Ssl3,
+ /// Transport Layer Security 1.0
+ Tls10,
+ /// Transport Layer Security 1.1
+ Tls11,
+ /// Transport Layer Security 1.2
+ Tls12,
+ /// Transport Layer Security 1.3
+ Tls13,
+}
+
+impl Protocol {
+ fn dword(self, direction: Direction) -> u32 {
+ match (self, direction) {
+ (Protocol::Ssl3, Direction::Inbound) => Identity::SP_PROT_SSL3_SERVER,
+ (Protocol::Tls10, Direction::Inbound) => Identity::SP_PROT_TLS1_0_SERVER,
+ (Protocol::Tls11, Direction::Inbound) => Identity::SP_PROT_TLS1_1_SERVER,
+ (Protocol::Tls12, Direction::Inbound) => Identity::SP_PROT_TLS1_2_SERVER,
+ (Protocol::Tls13, Direction::Inbound) => Identity::SP_PROT_TLS1_3_SERVER,
+ (Protocol::Ssl3, Direction::Outbound) => Identity::SP_PROT_SSL3_CLIENT,
+ (Protocol::Tls10, Direction::Outbound) => Identity::SP_PROT_TLS1_0_CLIENT,
+ (Protocol::Tls11, Direction::Outbound) => Identity::SP_PROT_TLS1_1_CLIENT,
+ (Protocol::Tls12, Direction::Outbound) => Identity::SP_PROT_TLS1_2_CLIENT,
+ (Protocol::Tls13, Direction::Outbound) => Identity::SP_PROT_TLS1_3_CLIENT,
+ }
+ }
+}
+
+/// A builder type for `SchannelCred`s.
+#[derive(Default, Debug)]
+pub struct Builder {
+ supported_algorithms: Option<Vec<Algorithm>>,
+ enabled_protocols: Option<Vec<Protocol>>,
+ certs: Vec<CertContext>,
+}
+
+impl Builder {
+ /// Returns a new `Builder`.
+ pub fn new() -> Builder {
+ Builder::default()
+ }
+
+ /// Sets the algorithms supported for credentials created from this builder.
+ pub fn supported_algorithms(&mut self, supported_algorithms: &[Algorithm]) -> &mut Builder {
+ self.supported_algorithms = Some(supported_algorithms.to_owned());
+ self
+ }
+
+ /// Sets the protocols enabled for credentials created from this builder.
+ pub fn enabled_protocols(&mut self, enabled_protocols: &[Protocol]) -> &mut Builder {
+ self.enabled_protocols = Some(enabled_protocols.to_owned());
+ self
+ }
+
+ /// Add a certificate to get passed down when the credentials are acquired.
+ ///
+ /// Certificates passed here may specify a certificate that contains a
+ /// private key to be used in authenticating the application. Typically,
+ /// this is called once for each key exchange method supported by
+ /// servers.
+ ///
+ /// Clients often do not call this function and either depend on Schannel to
+ /// find an appropriate certificate or create a certificate later if needed.
+ pub fn cert(&mut self, cx: CertContext) -> &mut Builder {
+ self.certs.push(cx);
+ self
+ }
+
+ /// Creates a new `SchannelCred`.
+ pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> {
+ unsafe {
+ let mut handle: Credentials::SecHandle = mem::zeroed();
+ let mut cred_data: Identity::SCHANNEL_CRED = mem::zeroed();
+ cred_data.dwVersion = Identity::SCHANNEL_CRED_VERSION;
+ cred_data.dwFlags =
+ Identity::SCH_USE_STRONG_CRYPTO | Identity::SCH_CRED_NO_DEFAULT_CREDS;
+ if let Some(ref supported_algorithms) = self.supported_algorithms {
+ cred_data.cSupportedAlgs = supported_algorithms.len() as u32;
+ cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _;
+ }
+ if let Some(ref enabled_protocols) = self.enabled_protocols {
+ cred_data.grbitEnabledProtocols = enabled_protocols
+ .iter()
+ .map(|p| p.dword(direction))
+ .fold(0, |acc, p| acc | p);
+ }
+ let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>();
+ cred_data.cCreds = certs.len() as u32;
+ cred_data.paCred = certs.as_mut_ptr() as _;
+
+ let direction = match direction {
+ Direction::Inbound => Identity::SECPKG_CRED_INBOUND,
+ Direction::Outbound => Identity::SECPKG_CRED_OUTBOUND,
+ };
+
+ match Identity::AcquireCredentialsHandleA(
+ ptr::null(),
+ Identity::UNISP_NAME_A,
+ direction,
+ ptr::null_mut(),
+ &mut cred_data as *const _ as *const _,
+ None,
+ ptr::null_mut(),
+ &mut handle,
+ ptr::null_mut(),
+ ) {
+ Foundation::SEC_E_OK => Ok(SchannelCred::from_inner(handle)),
+ err => Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+ }
+}
+
+/// An SChannel credential.
+#[derive(Clone)]
+pub struct SchannelCred(Arc<RawCredHandle>);
+
+struct RawCredHandle(Credentials::SecHandle);
+
+impl Drop for RawCredHandle {
+ fn drop(&mut self) {
+ unsafe {
+ Identity::FreeCredentialsHandle(&self.0);
+ }
+ }
+}
+
+impl SchannelCred {
+ /// Returns a builder.
+ pub fn builder() -> Builder {
+ Builder::new()
+ }
+
+ unsafe fn from_inner(inner: Credentials::SecHandle) -> SchannelCred {
+ SchannelCred(Arc::new(RawCredHandle(inner)))
+ }
+
+ pub(crate) fn as_inner(&self) -> Credentials::SecHandle {
+ self.0.as_ref().0
+ }
+}
diff --git a/vendor/schannel/src/security_context.rs b/vendor/schannel/src/security_context.rs
new file mode 100644
index 0000000..e5153b3
--- /dev/null
+++ b/vendor/schannel/src/security_context.rs
@@ -0,0 +1,134 @@
+use std::io;
+use std::mem;
+use std::ptr;
+
+use windows_sys::Win32::Foundation;
+use windows_sys::Win32::Security::Authentication::Identity;
+use windows_sys::Win32::Security::Credentials;
+
+use crate::alpn_list::AlpnList;
+use crate::cert_context::CertContext;
+use crate::context_buffer::ContextBuffer;
+use crate::schannel_cred::SchannelCred;
+use crate::{secbuf, secbuf_desc, Inner, INIT_REQUESTS};
+
+pub struct SecurityContext(Credentials::SecHandle);
+
+impl Drop for SecurityContext {
+ fn drop(&mut self) {
+ unsafe {
+ Identity::DeleteSecurityContext(&self.0);
+ }
+ }
+}
+
+impl Inner<Credentials::SecHandle> for SecurityContext {
+ unsafe fn from_inner(inner: Credentials::SecHandle) -> SecurityContext {
+ SecurityContext(inner)
+ }
+
+ fn as_inner(&self) -> Credentials::SecHandle {
+ self.0
+ }
+
+ fn get_mut(&mut self) -> &mut Credentials::SecHandle {
+ &mut self.0
+ }
+}
+
+impl SecurityContext {
+ pub fn initialize(
+ cred: &mut SchannelCred,
+ accept: bool,
+ domain: Option<&[u16]>,
+ requested_application_protocols: &Option<Vec<Vec<u8>>>,
+ ) -> io::Result<(SecurityContext, Option<ContextBuffer>)> {
+ unsafe {
+ let mut ctxt = mem::zeroed();
+
+ if accept {
+ // If we're performing an accept then we need to wait to call
+ // `AcceptSecurityContext` until we've actually read some data.
+ return Ok((SecurityContext(ctxt), None));
+ }
+
+ let domain = domain.map(|b| b.as_ptr()).unwrap_or(ptr::null_mut());
+
+ let mut inbufs = vec![];
+
+ // Make sure `AlpnList` is kept alive for the duration of this function.
+ let mut alpns = requested_application_protocols
+ .as_ref()
+ .map(|alpn| AlpnList::new(alpn));
+ if let Some(ref mut alpns) = alpns {
+ inbufs.push(secbuf(
+ Identity::SECBUFFER_APPLICATION_PROTOCOLS,
+ Some(&mut alpns[..]),
+ ));
+ };
+
+ let inbuf_desc = secbuf_desc(&mut inbufs[..]);
+
+ let mut outbuf = [secbuf(Identity::SECBUFFER_EMPTY, None)];
+ let mut outbuf_desc = secbuf_desc(&mut outbuf);
+
+ let mut attributes = 0;
+
+ match Identity::InitializeSecurityContextW(
+ &cred.as_inner(),
+ ptr::null_mut(),
+ domain,
+ INIT_REQUESTS,
+ 0,
+ 0,
+ &inbuf_desc,
+ 0,
+ &mut ctxt,
+ &mut outbuf_desc,
+ &mut attributes,
+ ptr::null_mut(),
+ ) {
+ Foundation::SEC_I_CONTINUE_NEEDED => {
+ Ok((SecurityContext(ctxt), Some(ContextBuffer(outbuf[0]))))
+ }
+ err => Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+ }
+
+ unsafe fn attribute<T>(&self, attr: Identity::SECPKG_ATTR) -> io::Result<T> {
+ let mut value = mem::zeroed();
+ let status =
+ Identity::QueryContextAttributesW(&self.0, attr, &mut value as *mut _ as *mut _);
+ match status {
+ Foundation::SEC_E_OK => Ok(value),
+ err => Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+
+ pub fn application_protocol(&self) -> io::Result<Identity::SecPkgContext_ApplicationProtocol> {
+ unsafe { self.attribute(Identity::SECPKG_ATTR_APPLICATION_PROTOCOL) }
+ }
+
+ pub fn session_info(&self) -> io::Result<Identity::SecPkgContext_SessionInfo> {
+ unsafe { self.attribute(Identity::SECPKG_ATTR_SESSION_INFO) }
+ }
+
+ pub fn stream_sizes(&self) -> io::Result<Identity::SecPkgContext_StreamSizes> {
+ unsafe { self.attribute(Identity::SECPKG_ATTR_STREAM_SIZES) }
+ }
+
+ pub fn remote_cert(&self) -> io::Result<CertContext> {
+ unsafe {
+ self.attribute(Identity::SECPKG_ATTR_REMOTE_CERT_CONTEXT)
+ .map(|p| CertContext::from_inner(p))
+ }
+ }
+
+ pub fn local_cert(&self) -> io::Result<CertContext> {
+ unsafe {
+ self.attribute(Identity::SECPKG_ATTR_LOCAL_CERT_CONTEXT)
+ .map(|p| CertContext::from_inner(p))
+ }
+ }
+}
diff --git a/vendor/schannel/src/test.rs b/vendor/schannel/src/test.rs
new file mode 100644
index 0000000..5ea1899
--- /dev/null
+++ b/vendor/schannel/src/test.rs
@@ -0,0 +1,921 @@
+use std::env;
+use std::io::{self, Error, Read, Write};
+use std::mem;
+use std::net::{TcpListener, TcpStream};
+use std::ptr;
+use std::sync::Once;
+use std::thread;
+
+use windows_sys::Win32::Foundation;
+use windows_sys::Win32::Security::Cryptography;
+use windows_sys::Win32::System::{SystemInformation, Time};
+
+use crate::alpn_list::AlpnList;
+use crate::cert_context::{CertContext, HashAlgorithm, KeySpec};
+use crate::cert_store::{CertAdd, CertStore, Memory};
+use crate::crypt_prov::{AcquireOptions, ProviderType};
+use crate::schannel_cred::{Algorithm, Direction, Protocol, SchannelCred};
+use crate::tls_stream::{self, HandshakeError};
+use crate::Inner;
+
+#[test]
+fn basic() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+ let mut out = vec![];
+ stream.read_to_end(&mut out).unwrap();
+ assert!(out.starts_with(b"HTTP/1.0 200 OK") || out.starts_with(b"HTTP/1.0 302 Found"));
+ assert!(out.ends_with(b"</html>") || out.ends_with(b"</HTML>\r\n"));
+}
+
+#[test]
+fn invalid_algorithms() {
+ let creds = SchannelCred::builder()
+ .supported_algorithms(&[Algorithm::Rc2, Algorithm::Ecdsa])
+ .acquire(Direction::Outbound);
+ assert_eq!(
+ creds.err().unwrap().raw_os_error().unwrap(),
+ Foundation::SEC_E_ALGORITHM_MISMATCH as i32
+ );
+}
+
+#[test]
+fn valid_algorithms() {
+ let creds = SchannelCred::builder()
+ .supported_algorithms(&[Algorithm::Aes128, Algorithm::Ecdsa])
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+ let mut out = vec![];
+ stream.read_to_end(&mut out).unwrap();
+ assert!(out.starts_with(b"HTTP/1.0 200 OK") || out.starts_with(b"HTTP/1.0 302 Found"));
+ assert!(out.ends_with(b"</html>") || out.ends_with(b"</HTML>\r\n"));
+}
+
+fn unwrap_handshake<S>(e: HandshakeError<S>) -> io::Error {
+ match e {
+ HandshakeError::Failure(e) => e,
+ HandshakeError::Interrupted(_) => panic!("not an I/O error"),
+ }
+}
+
+#[test]
+#[ignore] // google's inconsistent about disallowing sslv3
+fn invalid_protocol() {
+ let creds = SchannelCred::builder()
+ .enabled_protocols(&[Protocol::Ssl3])
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let err = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .err()
+ .unwrap();
+ let err = unwrap_handshake(err);
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::SEC_E_UNSUPPORTED_FUNCTION as i32
+ );
+}
+
+#[test]
+fn valid_protocol() {
+ let creds = SchannelCred::builder()
+ .enabled_protocols(&[Protocol::Tls12])
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+ let mut out = vec![];
+ stream.read_to_end(&mut out).unwrap();
+ assert!(out.starts_with(b"HTTP/1.0 200 OK") || out.starts_with(b"HTTP/1.0 302 Found"));
+ assert!(out.ends_with(b"</html>") || out.ends_with(b"</HTML>\r\n"));
+}
+
+#[test]
+fn valid_protocol_with_intermediate_certs() {
+ let creds = SchannelCred::builder()
+ .enabled_protocols(&[Protocol::Tls12])
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("lh3.googleusercontent.com:443").unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("lh3.googleusercontent.com")
+ .connect(creds, stream)
+ .unwrap();
+ stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
+ let mut out = vec![];
+ stream.read_to_end(&mut out).unwrap();
+ assert!(out.starts_with(b"HTTP/1.0 200 OK") || out.starts_with(b"HTTP/1.0 302 Found"));
+ assert!(out.ends_with(b"</html>") || out.ends_with(b"</HTML>\r\n"));
+}
+
+#[test]
+fn expired_cert() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("expired.badssl.com:443").unwrap();
+ let err = tls_stream::Builder::new()
+ .domain("expired.badssl.com")
+ .connect(creds, stream)
+ .err()
+ .unwrap();
+ let err = unwrap_handshake(err);
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::CERT_E_EXPIRED as i32
+ );
+}
+
+#[test]
+fn self_signed_cert() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("self-signed.badssl.com:443").unwrap();
+ let err = tls_stream::Builder::new()
+ .domain("self-signed.badssl.com")
+ .connect(creds, stream)
+ .err()
+ .unwrap();
+ let err = unwrap_handshake(err);
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::CERT_E_UNTRUSTEDROOT as i32
+ );
+}
+
+#[test]
+fn self_signed_cert_manual_trust() {
+ let cert = include_bytes!("../test/self-signed.badssl.com.cer");
+ let mut store = Memory::new().unwrap();
+ store.add_encoded_certificate(cert).unwrap();
+
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("self-signed.badssl.com:443").unwrap();
+ tls_stream::Builder::new()
+ .domain("self-signed.badssl.com")
+ .cert_store(store.into_store())
+ .connect(creds, stream)
+ .unwrap();
+}
+
+#[test]
+fn wrong_host_cert() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("wrong.host.badssl.com:443").unwrap();
+ let err = tls_stream::Builder::new()
+ .domain("wrong.host.badssl.com")
+ .connect(creds, stream)
+ .err()
+ .unwrap();
+ let err = unwrap_handshake(err);
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::CERT_E_CN_NO_MATCH as i32
+ );
+}
+
+#[test]
+fn wrong_host_cert_ignored() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("wrong.host.badssl.com:443").unwrap();
+ tls_stream::Builder::new()
+ .domain("wrong.host.badssl.com")
+ .accept_invalid_hostnames(true)
+ .connect(creds, stream)
+ .unwrap();
+}
+
+#[test]
+fn shutdown() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ stream.shutdown().unwrap();
+}
+
+#[test]
+fn validation_failure_is_permanent() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("self-signed.badssl.com:443").unwrap();
+ // temporarily switch to nonblocking to allow us to construct the stream
+ // without validating
+ stream.set_nonblocking(true).unwrap();
+ let stream = tls_stream::Builder::new()
+ .domain("self-signed.badssl.com")
+ .connect(creds, stream);
+ let stream = match stream {
+ Err(HandshakeError::Interrupted(s)) => s,
+ _ => panic!(),
+ };
+ stream.get_ref().set_nonblocking(false).unwrap();
+ let err = unwrap_handshake(stream.handshake().err().unwrap());
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::CERT_E_UNTRUSTEDROOT as i32
+ );
+}
+
+#[test]
+fn verify_callback_success() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("self-signed.badssl.com:443").unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("self-signed.badssl.com")
+ .verify_callback(|validation_result| {
+ assert!(validation_result.result().is_err());
+ Ok(())
+ })
+ .connect(creds, stream)
+ .unwrap();
+ stream
+ .write_all(b"GET / HTTP/1.0\r\nHost: self-signed.badssl.com\r\n\r\n")
+ .unwrap();
+ let mut out = vec![];
+ stream.read_to_end(&mut out).unwrap();
+ assert!(out.starts_with(b"HTTP/1.1 200 OK"));
+ assert!(out.ends_with(b"</html>\n"));
+}
+
+#[test]
+fn verify_callback_error() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let err = tls_stream::Builder::new()
+ .domain("google.com")
+ .verify_callback(|validation_result| {
+ assert!(validation_result.result().is_ok());
+ Err(io::Error::from_raw_os_error(
+ Foundation::CERT_E_UNTRUSTEDROOT,
+ ))
+ })
+ .connect(creds, stream)
+ .err()
+ .unwrap();
+ let err = unwrap_handshake(err);
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::CERT_E_UNTRUSTEDROOT as i32
+ );
+}
+
+#[test]
+fn verify_callback_gives_failed_cert() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("self-signed.badssl.com:443").unwrap();
+ let err = tls_stream::Builder::new()
+ .domain("self-signed.badssl.com")
+ .verify_callback(|validation_result| {
+ let expected_finger = include_bytes!("../test/self-signed.badssl.com.cer.sha1").to_vec();
+ assert_eq!(
+ validation_result
+ .failed_certificate()
+ .unwrap()
+ .fingerprint(HashAlgorithm::sha1())
+ .unwrap(),
+ expected_finger
+ );
+ Err(io::Error::from_raw_os_error(
+ Foundation::CERT_E_UNTRUSTEDROOT,
+ ))
+ })
+ .connect(creds, stream)
+ .err()
+ .unwrap();
+ let err = unwrap_handshake(err);
+ assert_eq!(
+ err.raw_os_error().unwrap(),
+ Foundation::CERT_E_UNTRUSTEDROOT as i32
+ );
+}
+
+#[test]
+fn no_session_resumed() {
+ for _ in 0..2 {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ assert!(!stream.session_resumed().unwrap());
+ }
+}
+
+#[test]
+fn basic_session_resumed() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let creds_copy = creds.clone();
+
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds_copy, stream)
+ .unwrap();
+ assert!(!stream.session_resumed().unwrap());
+
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ assert!(stream.session_resumed().unwrap());
+}
+
+#[test]
+fn session_resumption_thread_safety() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+
+ // Connect once so that the session ticket is cached.
+ let creds_copy = creds.clone();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds_copy, stream)
+ .unwrap();
+ assert!(!stream.session_resumed().unwrap());
+
+ let mut threads = vec![];
+ for _ in 0..4 {
+ let creds_copy = creds.clone();
+ threads.push(thread::spawn(move || {
+ for _ in 0..10 {
+ let creds = creds_copy.clone();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let stream = tls_stream::Builder::new()
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ assert!(stream.session_resumed().unwrap());
+ }
+ }));
+ }
+
+ for thread in threads.into_iter() {
+ thread.join().unwrap()
+ }
+}
+
+const FRIENDLY_NAME: &str = "schannel-rs localhost testing cert";
+
+fn install_certificate() -> io::Result<CertContext> {
+ unsafe {
+ let mut provider = 0;
+ let mut hkey = 0;
+
+ let mut buffer = "schannel-rs test suite"
+ .encode_utf16()
+ .chain(Some(0))
+ .collect::<Vec<_>>();
+ let res = Cryptography::CryptAcquireContextW(
+ &mut provider,
+ buffer.as_ptr(),
+ ptr::null(),
+ Cryptography::PROV_RSA_FULL,
+ Cryptography::CRYPT_MACHINE_KEYSET,
+ );
+ if res == 0 {
+ // create a new key container (since it does not exist)
+ let res = Cryptography::CryptAcquireContextW(
+ &mut provider,
+ buffer.as_ptr(),
+ ptr::null(),
+ Cryptography::PROV_RSA_FULL,
+ Cryptography::CRYPT_NEWKEYSET | Cryptography::CRYPT_MACHINE_KEYSET,
+ );
+ if res == 0 {
+ return Err(Error::last_os_error());
+ }
+ }
+
+ // create a new keypair (RSA-2048)
+ let res = Cryptography::CryptGenKey(
+ provider,
+ Cryptography::AT_SIGNATURE,
+ 0x0800 << 16 | Cryptography::CRYPT_EXPORTABLE,
+ &mut hkey,
+ );
+ if res == 0 {
+ return Err(Error::last_os_error());
+ }
+
+ // start creating the certificate
+ let name = "CN=localhost,O=schannel-rs,OU=schannel-rs,G=schannel_rs"
+ .encode_utf16()
+ .chain(Some(0))
+ .collect::<Vec<_>>();
+ let mut cname_buffer: [u16; 257] = mem::zeroed();
+ let mut cname_len = cname_buffer.len() as u32;
+ let res = Cryptography::CertStrToNameW(
+ Cryptography::X509_ASN_ENCODING,
+ name.as_ptr(),
+ Cryptography::CERT_X500_NAME_STR,
+ ptr::null_mut(),
+ cname_buffer.as_mut_ptr() as *mut u8,
+ &mut cname_len,
+ ptr::null_mut(),
+ );
+ if res == 0 {
+ return Err(Error::last_os_error());
+ }
+
+ let subject_issuer = Cryptography::CRYPT_INTEGER_BLOB {
+ cbData: cname_len,
+ pbData: cname_buffer.as_ptr() as *mut u8,
+ };
+ let key_provider = Cryptography::CRYPT_KEY_PROV_INFO {
+ pwszContainerName: buffer.as_mut_ptr(),
+ pwszProvName: ptr::null_mut(),
+ dwProvType: Cryptography::PROV_RSA_FULL,
+ dwFlags: Cryptography::CRYPT_MACHINE_KEYSET,
+ cProvParam: 0,
+ rgProvParam: ptr::null_mut(),
+ dwKeySpec: Cryptography::AT_SIGNATURE,
+ };
+ let sig_algorithm = Cryptography::CRYPT_ALGORITHM_IDENTIFIER {
+ pszObjId: Cryptography::szOID_RSA_SHA256RSA as *mut _,
+ Parameters: mem::zeroed(),
+ };
+ let mut expiration_date: Foundation::SYSTEMTIME = mem::zeroed();
+ SystemInformation::GetSystemTime(&mut expiration_date);
+ let mut file_time: Foundation::FILETIME = mem::zeroed();
+ let res = Time::SystemTimeToFileTime(&expiration_date, &mut file_time);
+ if res == 0 {
+ return Err(Error::last_os_error());
+ }
+ let mut timestamp: u64 =
+ file_time.dwLowDateTime as u64 | (file_time.dwHighDateTime as u64) << 32;
+ // one day, timestamp unit is in 100 nanosecond intervals
+ timestamp += (1E9 as u64) / 100 * (60 * 60 * 24);
+ file_time.dwLowDateTime = timestamp as u32;
+ file_time.dwHighDateTime = (timestamp >> 32) as u32;
+ let res = Time::FileTimeToSystemTime(&file_time, &mut expiration_date);
+ if res == 0 {
+ return Err(Error::last_os_error());
+ }
+
+ // create a self signed certificate
+ let cert_context = Cryptography::CertCreateSelfSignCertificate(
+ Cryptography::HCRYPTPROV_OR_NCRYPT_KEY_HANDLE::default(),
+ &subject_issuer,
+ Cryptography::CERT_CREATE_SELFSIGN_FLAGS::default(),
+ &key_provider,
+ &sig_algorithm,
+ ptr::null_mut(),
+ &expiration_date,
+ ptr::null_mut(),
+ );
+ if cert_context.is_null() {
+ return Err(Error::last_os_error());
+ }
+ let cert_context = CertContext::from_inner(cert_context);
+ cert_context.set_friendly_name(FRIENDLY_NAME)?;
+
+ // install the certificate to the machine's local store
+ io::stdout()
+ .write_all(
+ br#"
+
+The schannel-rs test suite is about to add a certificate to your set of root
+and trusted certificates. This certificate should be for the domain "localhost"
+with the description related to "schannel". This certificate is only valid for
+one day and will be automatically deleted if you re-run the schannel-rs test
+suite later.
+
+If you would rather not do this please cancel the addition and re-run the
+test suite with SCHANNEL_RS_SKIP_SERVER_TESTS=1.
+
+"#,
+ )
+ .unwrap();
+ local_root_store().add_cert(&cert_context, CertAdd::ReplaceExisting)?;
+ Ok(cert_context)
+ }
+}
+
+fn local_root_store() -> CertStore {
+ if env::var("APPVEYOR").is_ok() || env::var("CI").is_ok() {
+ CertStore::open_local_machine("Root").unwrap()
+ } else {
+ CertStore::open_current_user("Root").unwrap()
+ }
+}
+
+fn localhost_cert() -> Option<CertContext> {
+ if env::var("SCHANNEL_RS_SKIP_SERVER_TESTS").is_ok() {
+ return None;
+ }
+
+ // Our tests need a certficiate that the system trusts to run with, and we
+ // do this by basically generating a certificate on the fly. This
+ // initialization block synchronizes tests to ensure that we only generate
+ // one certificate which is then used by all the tests.
+ //
+ // First we check to see if the root trust store already has one of our
+ // certificates, identified by the "friendly name" we set when the
+ // certificate was created. If it's expired, then we delete it and generate
+ // another. If none is found, we also generate another.
+ //
+ // Note that generating a certificate and adding it to the root trust store
+ // will likely trigger a prompt to the user asking if they want to do that,
+ // so we generate certificates that are valid for some amount of time so you
+ // don't have to hit the "OK" button each time you run `cargo test`.
+ //
+ // After the initialization is performed we just probe the root store again
+ // and find the certificate we added (or was already there).
+ static INIT: Once = Once::new();
+ INIT.call_once(|| {
+ for cert in local_root_store().certs() {
+ let name = match cert.friendly_name() {
+ Ok(name) => name,
+ Err(_) => continue,
+ };
+ if name != FRIENDLY_NAME {
+ continue;
+ }
+ if !cert.is_time_valid().unwrap() {
+ io::stdout()
+ .write_all(
+ br#"
+
+The schannel-rs test suite is about to delete an old copy of one of its
+certificates from your root trust store. This certificate was only valid for one
+day and it is no longer needed. The host should be "localhost" and the
+description should mention "schannel".
+
+"#,
+ )
+ .unwrap();
+ cert.delete().unwrap();
+ } else {
+ return;
+ }
+ }
+
+ install_certificate().unwrap();
+ });
+
+ for cert in local_root_store().certs() {
+ let name = match cert.friendly_name() {
+ Ok(name) => name,
+ Err(_) => continue,
+ };
+ if name == FRIENDLY_NAME {
+ return Some(cert);
+ }
+ }
+
+ panic!("couldn't find a cert");
+}
+
+#[test]
+fn accept_a_socket() {
+ let cert = match localhost_cert() {
+ Some(cert) => cert,
+ None => return,
+ };
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+ let t = thread::spawn(move || {
+ let stream = TcpStream::connect(&addr).unwrap();
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("localhost")
+ .connect(creds, stream)
+ .unwrap();
+ stream.write_all(&[1, 2, 3, 4]).unwrap();
+ stream.flush().unwrap();
+ assert_eq!(stream.read(&mut [0; 1024]).unwrap(), 4);
+ stream.shutdown().unwrap();
+ });
+
+ let stream = listener.accept().unwrap().0;
+ let creds = SchannelCred::builder()
+ .cert(cert)
+ .acquire(Direction::Inbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new().accept(creds, stream).unwrap();
+ assert_eq!(stream.read(&mut [0; 1024]).unwrap(), 4);
+ stream.write_all(&[1, 2, 3, 4]).unwrap();
+ stream.flush().unwrap();
+ let mut buf = [0; 1];
+ assert_eq!(stream.read(&mut buf).unwrap(), 0);
+
+ t.join().unwrap();
+}
+
+#[test]
+fn accept_one_byte_at_a_time() {
+ let cert = match localhost_cert() {
+ Some(cert) => cert,
+ None => return,
+ };
+
+ #[derive(Debug)]
+ struct OneByteAtATime<S> {
+ inner: S,
+ }
+
+ impl<S: Read> Read for OneByteAtATime<S> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.inner.read(&mut buf[..1])
+ }
+ }
+
+ impl<S: Write> Write for OneByteAtATime<S> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.inner.write(&buf[..1])
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.inner.flush()
+ }
+ }
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+ let t = thread::spawn(move || {
+ let stream = TcpStream::connect(&addr).unwrap();
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("localhost")
+ .connect(creds, OneByteAtATime { inner: stream })
+ .unwrap();
+ stream.write_all(&[1, 2, 3, 4]).unwrap();
+ stream.flush().unwrap();
+ assert_eq!(stream.read(&mut [0; 1024]).unwrap(), 4);
+ stream.shutdown().unwrap();
+ });
+
+ let stream = listener.accept().unwrap().0;
+ let creds = SchannelCred::builder()
+ .cert(cert)
+ .acquire(Direction::Inbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .accept(creds, OneByteAtATime { inner: stream })
+ .unwrap();
+ assert_eq!(stream.read(&mut [0; 1024]).unwrap(), 4);
+ stream.write_all(&[1, 2, 3, 4]).unwrap();
+ stream.flush().unwrap();
+ let mut buf = [0; 1];
+ assert_eq!(stream.read(&mut buf).unwrap(), 0);
+
+ t.join().unwrap();
+}
+
+#[test]
+fn split_cert_key() {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+ let t = thread::spawn(move || {
+ let cert = include_bytes!("../test/cert.der");
+ let mut store = Memory::new().unwrap();
+ store.add_encoded_certificate(cert).unwrap();
+ let store = store.into_store();
+
+ let stream = TcpStream::connect(&addr).unwrap();
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("foobar.com")
+ .cert_store(store)
+ .connect(creds, stream)
+ .unwrap();
+ stream.write_all(&[1, 2, 3, 4]).unwrap();
+ stream.flush().unwrap();
+ assert_eq!(stream.read(&mut [0; 1024]).unwrap(), 4);
+ stream.shutdown().unwrap();
+ });
+
+ let cert = include_bytes!("../test/cert.der");
+ let cert = CertContext::new(cert).unwrap();
+
+ let mut options = AcquireOptions::new();
+ options.container("schannel-test");
+ let type_ = ProviderType::rsa_full();
+
+ let mut container = match options.acquire(type_) {
+ Ok(container) => container,
+ Err(_) => options.new_keyset(true).acquire(type_).unwrap(),
+ };
+ let key = include_bytes!("../test/key.key");
+ container.import().import(key).unwrap();
+
+ cert.set_key_prov_info()
+ .container("schannel-test")
+ .type_(type_)
+ .keep_open(true)
+ .key_spec(KeySpec::key_exchange())
+ .set()
+ .unwrap();
+
+ let stream = listener.accept().unwrap().0;
+ let creds = SchannelCred::builder()
+ .cert(cert)
+ .acquire(Direction::Inbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new().accept(creds, stream).unwrap();
+ assert_eq!(stream.read(&mut [0; 1024]).unwrap(), 4);
+ stream.write_all(&[1, 2, 3, 4]).unwrap();
+ stream.flush().unwrap();
+ let mut buf = [0; 1];
+ assert_eq!(stream.read(&mut buf).unwrap(), 0);
+
+ t.join().unwrap();
+}
+
+#[test]
+fn test_loopback_alpn() {
+ let cert = match localhost_cert() {
+ Some(cert) => cert,
+ None => return,
+ };
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+ let t = thread::spawn(move || {
+ let stream = TcpStream::connect(&addr).unwrap();
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("localhost")
+ .request_application_protocols(&[b"h2"])
+ .connect(creds, stream)
+ .unwrap();
+ assert_eq!(
+ stream
+ .negotiated_application_protocol()
+ .expect("localhost unreachable"),
+ Some(b"h2".to_vec())
+ );
+
+ stream.shutdown().unwrap();
+ });
+
+ let stream = listener.accept().unwrap().0;
+ let creds = SchannelCred::builder()
+ .cert(cert)
+ .acquire(Direction::Inbound)
+ .unwrap();
+ let stream = tls_stream::Builder::new()
+ .request_application_protocols(&[b"h2"])
+ .accept(creds, stream)
+ .unwrap();
+ assert_eq!(
+ stream
+ .negotiated_application_protocol()
+ .expect("localhost unreachable"),
+ Some(b"h2".to_vec())
+ );
+
+ t.join().unwrap();
+}
+
+#[test]
+fn test_loopback_alpn_mismatch() {
+ let cert = match localhost_cert() {
+ Some(cert) => cert,
+ None => return,
+ };
+
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+ let t = thread::spawn(move || {
+ let stream = TcpStream::connect(&addr).unwrap();
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let mut stream = tls_stream::Builder::new()
+ .domain("localhost")
+ .connect(creds, stream)
+ .unwrap();
+ assert_eq!(
+ stream
+ .negotiated_application_protocol()
+ .expect("localhost unreachable"),
+ None
+ );
+
+ stream.shutdown().unwrap();
+ });
+
+ let stream = listener.accept().unwrap().0;
+ let creds = SchannelCred::builder()
+ .cert(cert)
+ .acquire(Direction::Inbound)
+ .unwrap();
+ let stream = tls_stream::Builder::new()
+ .request_application_protocols(&[b"h2"])
+ .accept(creds, stream)
+ .unwrap();
+ assert_eq!(
+ stream
+ .negotiated_application_protocol()
+ .expect("localhost unreachable"),
+ None
+ );
+
+ t.join().unwrap();
+}
+
+#[test]
+fn test_external_alpn() {
+ let creds = SchannelCred::builder()
+ .acquire(Direction::Outbound)
+ .unwrap();
+ let stream = TcpStream::connect("google.com:443").unwrap();
+ let stream = tls_stream::Builder::new()
+ .request_application_protocols(&[b"h2"])
+ .domain("google.com")
+ .connect(creds, stream)
+ .unwrap();
+ assert_eq!(
+ stream
+ .negotiated_application_protocol()
+ .expect("google.com unreachable"),
+ Some(b"h2".to_vec())
+ );
+}
+
+#[test]
+fn test_alpn_list() {
+ let raw_proto_alpn_list = b"\x02h2";
+ // Little-endian bit representation of the expected `SEC_APPLICATION_PROTOCOL_LIST`.
+ let proto_list = &[
+ // `sspi::SecApplicationProtocolNegotiationExt_ALPN` equals 2.
+ &[2, 0, 0, 0, raw_proto_alpn_list.len() as u8, 0] as &[u8],
+ raw_proto_alpn_list,
+ ]
+ .concat();
+ let full_alpn_list = [&[proto_list.len() as u8, 0, 0, 0] as &[u8], proto_list].concat();
+ assert_eq!(
+ &AlpnList::new(&[b"h2".to_vec()]) as &[u8],
+ &full_alpn_list as &[u8]
+ );
+
+ let raw_proto_alpn_list = b"\x02h2\x08http/1.1";
+ // Little-endian bit representation of the expected `SEC_APPLICATION_PROTOCOL_LIST`.
+ let proto_list = &[
+ // `sspi::SecApplicationProtocolNegotiationExt_ALPN` equals 2.
+ &[2, 0, 0, 0, raw_proto_alpn_list.len() as u8, 0] as &[u8],
+ raw_proto_alpn_list,
+ ]
+ .concat();
+ let full_alpn_list = [&[proto_list.len() as u8, 0, 0, 0] as &[u8], proto_list].concat();
+ assert_eq!(
+ &AlpnList::new(&[b"h2".to_vec(), b"http/1.1".to_vec()]) as &[u8],
+ &full_alpn_list as &[u8]
+ );
+}
diff --git a/vendor/schannel/src/tls_stream.rs b/vendor/schannel/src/tls_stream.rs
new file mode 100644
index 0000000..9750d1d
--- /dev/null
+++ b/vendor/schannel/src/tls_stream.rs
@@ -0,0 +1,1050 @@
+//! Schannel TLS streams.
+use std::any::Any;
+use std::cmp;
+use std::error::Error;
+use std::fmt;
+use std::io::{self, BufRead, Cursor, Read, Write};
+use std::mem;
+use std::ptr;
+use std::slice;
+use std::sync::Arc;
+
+use windows_sys::Win32::Foundation;
+use windows_sys::Win32::Security::Authentication::Identity;
+use windows_sys::Win32::Security::Cryptography;
+
+use crate::alpn_list::AlpnList;
+use crate::cert_chain::{CertChain, CertChainContext};
+use crate::cert_context::CertContext;
+use crate::cert_store::{CertAdd, CertStore};
+use crate::context_buffer::ContextBuffer;
+use crate::schannel_cred::SchannelCred;
+use crate::security_context::SecurityContext;
+use crate::{secbuf, secbuf_desc, Inner, ACCEPT_REQUESTS, INIT_REQUESTS};
+
+/// A builder type for `TlsStream`s.
+pub struct Builder {
+ domain: Option<Vec<u16>>,
+ use_sni: bool,
+ accept_invalid_hostnames: bool,
+ verify_callback: Option<Arc<dyn Fn(CertValidationResult) -> io::Result<()> + Sync + Send>>,
+ cert_store: Option<CertStore>,
+ requested_application_protocols: Option<Vec<Vec<u8>>>,
+}
+
+impl Default for Builder {
+ fn default() -> Builder {
+ Builder {
+ domain: None,
+ use_sni: true,
+ accept_invalid_hostnames: false,
+ verify_callback: None,
+ cert_store: None,
+ requested_application_protocols: None,
+ }
+ }
+}
+
+impl Builder {
+ /// Returns a new `Builder`.
+ pub fn new() -> Builder {
+ Builder::default()
+ }
+
+ /// Sets the domain associated with connections created with this `Builder`.
+ ///
+ /// The domain will be used for Server Name Indication as well as
+ /// certificate validation.
+ pub fn domain(&mut self, domain: &str) -> &mut Builder {
+ self.domain = Some(domain.encode_utf16().chain(Some(0)).collect());
+ self
+ }
+
+ /// Determines if Server Name Indication (SNI) will be used.
+ ///
+ /// Defaults to `true`.
+ pub fn use_sni(&mut self, use_sni: bool) -> &mut Builder {
+ self.use_sni = use_sni;
+ self
+ }
+
+ /// Determines if the server's hostname will be checked during certificate verification.
+ ///
+ /// Defaults to `false`.
+ pub fn accept_invalid_hostnames(&mut self, accept_invalid_hostnames: bool) -> &mut Builder {
+ self.accept_invalid_hostnames = accept_invalid_hostnames;
+ self
+ }
+
+ /// Set a verification callback to be used for connections created with this `Builder`.
+ ///
+ /// The callback is provided with an io::Result indicating if the (pre)validation was
+ /// successful. The Ok() variant indicates a successful validation while the Err() variant
+ /// contains the errorcode returned from the internal verification process.
+ /// The validated certificate, is accessible through the second argument of the closure.
+ pub fn verify_callback<F>(&mut self, callback: F) -> &mut Builder
+ where
+ F: Fn(CertValidationResult) -> io::Result<()> + 'static + Sync + Send,
+ {
+ self.verify_callback = Some(Arc::new(callback));
+ self
+ }
+
+ /// Specifies a custom certificate store which is later used when validating
+ /// a server's certificate.
+ ///
+ /// This option is only used for client connections and is used to construct
+ /// the certificate chain which the server's certificate is validated
+ /// against.
+ ///
+ /// Note that adding certificates here means that they are
+ /// implicitly trusted.
+ pub fn cert_store(&mut self, cert_store: CertStore) -> &mut Builder {
+ self.cert_store = Some(cert_store);
+ self
+ }
+
+ /// Requests one of a set of application protocols using alpn
+ pub fn request_application_protocols(&mut self, alpns: &[&[u8]]) -> &mut Builder {
+ self.requested_application_protocols =
+ Some(alpns.iter().map(|bytes| bytes.to_vec()).collect::<Vec<_>>());
+ self
+ }
+
+ /// Initialize a new TLS session where the stream provided will be
+ /// connecting to a remote TLS server.
+ ///
+ /// If the stream provided is a blocking stream then the entire handshake
+ /// will be performed if possible, but if the stream is in nonblocking mode
+ /// then a `HandshakeError::Interrupted` variant may be returned. This
+ /// type can then be extracted to later call
+ /// `MidHandshakeTlsStream::handshake` when data becomes available.
+ pub fn connect<S>(
+ &mut self,
+ cred: SchannelCred,
+ stream: S,
+ ) -> Result<TlsStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ self.initialize(cred, false, stream)
+ }
+
+ /// Initialize a new TLS session where the stream provided will be
+ /// accepting a connection.
+ ///
+ /// This method will tweak the protocol for "who talks first" and also
+ /// currently disables validation of the client that's connecting to us.
+ ///
+ /// If the stream provided is a blocking stream then the entire handshake
+ /// will be performed if possible, but if the stream is in nonblocking mode
+ /// then a `HandshakeError::Interrupted` variant may be returned. This
+ /// type can then be extracted to later call
+ /// `MidHandshakeTlsStream::handshake` when data becomes available.
+ pub fn accept<S>(
+ &mut self,
+ cred: SchannelCred,
+ stream: S,
+ ) -> Result<TlsStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ self.initialize(cred, true, stream)
+ }
+
+ fn initialize<S>(
+ &mut self,
+ mut cred: SchannelCred,
+ server: bool,
+ stream: S,
+ ) -> Result<TlsStream<S>, HandshakeError<S>>
+ where
+ S: Read + Write,
+ {
+ let domain = match self.domain {
+ Some(ref domain) if self.use_sni => Some(&domain[..]),
+ _ => None,
+ };
+ let (ctxt, buf) = match SecurityContext::initialize(
+ &mut cred,
+ server,
+ domain,
+ &self.requested_application_protocols,
+ ) {
+ Ok(pair) => pair,
+ Err(e) => return Err(HandshakeError::Failure(e)),
+ };
+
+ let stream = TlsStream {
+ cred,
+ context: ctxt,
+ cert_store: self.cert_store.clone(),
+ domain: self.domain.clone(),
+ use_sni: self.use_sni,
+ accept_invalid_hostnames: self.accept_invalid_hostnames,
+ verify_callback: self.verify_callback.clone(),
+ stream,
+ server,
+ accept_first: true,
+ state: State::Initializing {
+ needs_flush: false,
+ more_calls: true,
+ shutting_down: false,
+ validated: false,
+ },
+ needs_read: 1,
+ dec_in: Cursor::new(Vec::new()),
+ enc_in: Cursor::new(Vec::new()),
+ out_buf: Cursor::new(buf.map(|b| b.to_owned()).unwrap_or_else(Vec::new)),
+ last_write_len: 0,
+ requested_application_protocols: self.requested_application_protocols.clone(),
+ };
+
+ MidHandshakeTlsStream { inner: stream }.handshake()
+ }
+}
+
+enum State {
+ Initializing {
+ needs_flush: bool,
+ more_calls: bool,
+ shutting_down: bool,
+ validated: bool,
+ },
+ Streaming {
+ sizes: Identity::SecPkgContext_StreamSizes,
+ },
+ Shutdown,
+}
+
+/// An Schannel TLS stream.
+pub struct TlsStream<S> {
+ cred: SchannelCred,
+ context: SecurityContext,
+ cert_store: Option<CertStore>,
+ domain: Option<Vec<u16>>,
+ use_sni: bool,
+ accept_invalid_hostnames: bool,
+ verify_callback: Option<Arc<dyn Fn(CertValidationResult) -> io::Result<()> + Sync + Send>>,
+ stream: S,
+ state: State,
+ server: bool,
+ accept_first: bool,
+ needs_read: usize,
+ // valid from position() to len()
+ dec_in: Cursor<Vec<u8>>,
+ // valid from 0 to position()
+ enc_in: Cursor<Vec<u8>>,
+ // valid from position() to len()
+ out_buf: Cursor<Vec<u8>>,
+ /// the (unencrypted) length of the last write call used to track writes
+ last_write_len: usize,
+ requested_application_protocols: Option<Vec<Vec<u8>>>,
+}
+
+/// ensures that a TlsStream is always Sync/Send
+fn _is_sync() {
+ fn sync<T: Sync + Send>() {}
+ sync::<TlsStream<()>>();
+}
+
+/// A failure which can happen during the `Builder::initialize` phase, either an
+/// I/O error or an intermediate stream which has not completed its handshake.
+#[derive(Debug)]
+pub enum HandshakeError<S> {
+ /// A fatal I/O error occurred
+ Failure(io::Error),
+ /// The stream connection is in progress, but the handshake is not completed
+ /// yet.
+ Interrupted(MidHandshakeTlsStream<S>),
+}
+
+/// A struct used to wrap various cert chain validation results for callback processing.
+pub struct CertValidationResult {
+ chain: CertChainContext,
+ res: i32,
+ chain_index: i32,
+ element_index: i32,
+}
+
+impl CertValidationResult {
+ /// Returns the certificate that failed validation if applicable
+ pub fn failed_certificate(&self) -> Option<CertContext> {
+ if let Some(cert_chain) = self.chain.get_chain(self.chain_index as usize) {
+ return cert_chain.get(self.element_index as usize);
+ }
+ None
+ }
+
+ /// Returns the final certificate chain in the certificate context if applicable
+ pub fn chain(&self) -> Option<CertChain> {
+ self.chain.final_chain()
+ }
+
+ /// Returns the result of the built-in certificate verification process.
+ pub fn result(&self) -> io::Result<()> {
+ if self.res as u32 != Foundation::ERROR_SUCCESS {
+ Err(io::Error::from_raw_os_error(self.res))
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl<S: fmt::Debug + Any> Error for HandshakeError<S> {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match *self {
+ HandshakeError::Failure(ref e) => Some(e),
+ HandshakeError::Interrupted(_) => None,
+ }
+ }
+}
+
+impl<S: fmt::Debug + Any> fmt::Display for HandshakeError<S> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let desc = match *self {
+ HandshakeError::Failure(_) => "failed to perform handshake",
+ HandshakeError::Interrupted(_) => "interrupted performing handshake",
+ };
+ write!(f, "{}", desc)?;
+ if let Some(e) = self.source() {
+ write!(f, ": {}", e)?;
+ }
+ Ok(())
+ }
+}
+
+/// A stream which has not yet completed its handshake.
+#[derive(Debug)]
+pub struct MidHandshakeTlsStream<S> {
+ inner: TlsStream<S>,
+}
+
+impl<S> fmt::Debug for TlsStream<S>
+where
+ S: fmt::Debug,
+{
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("TlsStream")
+ .field("stream", &self.stream)
+ .finish()
+ }
+}
+
+impl<S> TlsStream<S> {
+ /// Returns a reference to the wrapped stream.
+ pub fn get_ref(&self) -> &S {
+ &self.stream
+ }
+
+ /// Returns a mutable reference to the wrapped stream.
+ pub fn get_mut(&mut self) -> &mut S {
+ &mut self.stream
+ }
+
+ /// Indicates if this stream is the server- or client-side of a TLS session.
+ pub fn is_server(&self) -> bool {
+ self.server
+ }
+}
+
+impl<S> TlsStream<S>
+where
+ S: Read + Write,
+{
+ /// Returns the certificate used to identify this side of the TLS session.
+ ///
+ /// Its associated cert store contains any intermediate certificates sent
+ /// along with the leaf.
+ pub fn certificate(&self) -> io::Result<CertContext> {
+ self.context.local_cert()
+ }
+
+ /// Returns the peer's certificate, if available.
+ ///
+ /// Its associated cert store contains any intermediate certificates sent
+ /// by the server.
+ pub fn peer_certificate(&self) -> io::Result<CertContext> {
+ self.context.remote_cert()
+ }
+
+ /// Returns the negotiated application protocol for this tls stream, if one exists
+ pub fn negotiated_application_protocol(&self) -> io::Result<Option<Vec<u8>>> {
+ let client_proto = self.context.application_protocol()?;
+ if client_proto.ProtoNegoStatus != Identity::SecApplicationProtocolNegotiationStatus_Success
+ || client_proto.ProtoNegoExt != Identity::SecApplicationProtocolNegotiationExt_ALPN
+ {
+ return Ok(None);
+ }
+ Ok(Some(
+ client_proto.ProtocolId[..client_proto.ProtocolIdSize as usize].to_vec(),
+ ))
+ }
+
+ /// Returns whether or not the session was resumed.
+ pub fn session_resumed(&self) -> io::Result<bool> {
+ let session_info = self.context.session_info()?;
+ Ok(session_info.dwFlags & Identity::SSL_SESSION_RECONNECT > 0)
+ }
+
+ /// Returns a reference to the buffer of pending data.
+ ///
+ /// Like `BufRead::fill_buf` except that it will return an empty slice
+ /// rather than reading from the wrapped stream if there is no buffered
+ /// data.
+ pub fn get_buf(&self) -> &[u8] {
+ &self.dec_in.get_ref()[self.dec_in.position() as usize..]
+ }
+
+ /// Shuts the TLS session down.
+ pub fn shutdown(&mut self) -> io::Result<()> {
+ match self.state {
+ State::Shutdown => return Ok(()),
+ State::Initializing {
+ shutting_down: true,
+ ..
+ } => {}
+ _ => {
+ unsafe {
+ let mut token = Identity::SCHANNEL_SHUTDOWN;
+ let ptr = &mut token as *mut _ as *mut u8;
+ let size = mem::size_of_val(&token);
+ let token = slice::from_raw_parts_mut(ptr, size);
+ let mut buf = [secbuf(Identity::SECBUFFER_TOKEN, Some(token))];
+ let desc = secbuf_desc(&mut buf);
+
+ match Identity::ApplyControlToken(self.context.get_mut(), &desc) {
+ Foundation::SEC_E_OK => {}
+ err => return Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+
+ self.state = State::Initializing {
+ needs_flush: false,
+ more_calls: true,
+ shutting_down: true,
+ validated: false,
+ };
+ self.needs_read = 0;
+ }
+ }
+
+ self.initialize().map(|_| ())
+ }
+
+ fn step_initialize(&mut self) -> io::Result<()> {
+ unsafe {
+ let pos = self.enc_in.position() as usize;
+ let mut inbufs = vec![
+ secbuf(
+ Identity::SECBUFFER_TOKEN,
+ Some(&mut self.enc_in.get_mut()[..pos]),
+ ),
+ secbuf(Identity::SECBUFFER_EMPTY, None),
+ ];
+ // Make sure `AlpnList` is kept alive for the duration of this function.
+ let mut alpns = self
+ .requested_application_protocols
+ .as_ref()
+ .map(|alpn| AlpnList::new(alpn));
+ if let Some(ref mut alpns) = alpns {
+ inbufs.push(secbuf(
+ Identity::SECBUFFER_APPLICATION_PROTOCOLS,
+ Some(&mut alpns[..]),
+ ));
+ };
+ let inbuf_desc = secbuf_desc(&mut inbufs[..]);
+
+ let mut outbufs = [
+ secbuf(Identity::SECBUFFER_TOKEN, None),
+ secbuf(Identity::SECBUFFER_ALERT, None),
+ secbuf(Identity::SECBUFFER_EMPTY, None),
+ ];
+ let mut outbuf_desc = secbuf_desc(&mut outbufs);
+
+ let mut attributes = 0;
+
+ let status = if self.server {
+ let ptr = if self.accept_first {
+ ptr::null_mut()
+ } else {
+ self.context.get_mut()
+ };
+ Identity::AcceptSecurityContext(
+ &self.cred.as_inner(),
+ ptr,
+ &inbuf_desc,
+ ACCEPT_REQUESTS,
+ 0,
+ self.context.get_mut(),
+ &mut outbuf_desc,
+ &mut attributes,
+ ptr::null_mut(),
+ )
+ } else {
+ let domain = match self.domain {
+ Some(ref domain) if self.use_sni => domain.as_ptr() as *mut u16,
+ _ => ptr::null_mut(),
+ };
+
+ Identity::InitializeSecurityContextW(
+ &self.cred.as_inner(),
+ self.context.get_mut(),
+ domain,
+ INIT_REQUESTS,
+ 0,
+ 0,
+ &inbuf_desc,
+ 0,
+ ptr::null_mut(),
+ &mut outbuf_desc,
+ &mut attributes,
+ ptr::null_mut(),
+ )
+ };
+
+ for buf in &outbufs[1..] {
+ if !buf.pvBuffer.is_null() {
+ Identity::FreeContextBuffer(buf.pvBuffer);
+ }
+ }
+
+ match status {
+ Foundation::SEC_E_OK => {
+ let nread = if inbufs[1].BufferType == Identity::SECBUFFER_EXTRA {
+ self.enc_in.position() as usize - inbufs[1].cbBuffer as usize
+ } else {
+ self.enc_in.position() as usize
+ };
+ let to_write = if outbufs[0].pvBuffer.is_null() {
+ None
+ } else {
+ Some(ContextBuffer(outbufs[0]))
+ };
+
+ self.consume_enc_in(nread);
+ self.needs_read = (self.enc_in.position() == 0) as usize;
+ if let Some(to_write) = to_write {
+ self.out_buf.get_mut().extend_from_slice(&to_write);
+ }
+ if self.enc_in.position() != 0 {
+ self.decrypt()?;
+ }
+ if let State::Initializing {
+ ref mut more_calls, ..
+ } = self.state
+ {
+ *more_calls = false;
+ }
+ }
+ Foundation::SEC_I_CONTINUE_NEEDED => {
+ // Windows apparently doesn't like AcceptSecurityContext
+ // being called as if it were the second time unless the
+ // first call to AcceptSecurityContext succeeded with
+ // CONTINUE_NEEDED.
+ //
+ // In other words, if we were to set `accept_first` to
+ // `false` after the literal first call to
+ // `AcceptSecurityContext` while the call returned
+ // INCOMPLETE_MESSAGE, the next call would return an error.
+ //
+ // For that reason we only set `accept_first` to false here
+ // once we've actually successfully received the full
+ // "token" from the client.
+ self.accept_first = false;
+ let nread = if inbufs[1].BufferType == Identity::SECBUFFER_EXTRA {
+ self.enc_in.position() as usize - inbufs[1].cbBuffer as usize
+ } else {
+ self.enc_in.position() as usize
+ };
+ let to_write = ContextBuffer(outbufs[0]);
+
+ self.consume_enc_in(nread);
+ self.needs_read = (self.enc_in.position() == 0) as usize;
+ self.out_buf.get_mut().extend_from_slice(&to_write);
+ }
+ Foundation::SEC_E_INCOMPLETE_MESSAGE => {
+ self.needs_read = if inbufs[1].BufferType == Identity::SECBUFFER_MISSING {
+ inbufs[1].cbBuffer as usize
+ } else {
+ 1
+ };
+ }
+ err => return Err(io::Error::from_raw_os_error(err)),
+ }
+ Ok(())
+ }
+ }
+
+ fn initialize(&mut self) -> io::Result<Option<Identity::SecPkgContext_StreamSizes>> {
+ loop {
+ match self.state {
+ State::Initializing {
+ mut needs_flush,
+ more_calls,
+ shutting_down,
+ validated,
+ } => {
+ if self.write_out()? > 0 {
+ needs_flush = true;
+ if let State::Initializing {
+ ref mut needs_flush,
+ ..
+ } = self.state
+ {
+ *needs_flush = true;
+ }
+ }
+
+ if needs_flush {
+ self.stream.flush()?;
+ if let State::Initializing {
+ ref mut needs_flush,
+ ..
+ } = self.state
+ {
+ *needs_flush = false;
+ }
+ }
+
+ if !shutting_down && !validated {
+ // on the last call, we require a valid certificate
+ if self.validate(!more_calls)? {
+ if let State::Initializing {
+ ref mut validated, ..
+ } = self.state
+ {
+ *validated = true;
+ }
+ }
+ }
+
+ if !more_calls {
+ self.state = if shutting_down {
+ State::Shutdown
+ } else {
+ State::Streaming {
+ sizes: self.context.stream_sizes()?,
+ }
+ };
+ continue;
+ }
+
+ if self.needs_read > 0 && self.read_in()? == 0 {
+ return Err(io::Error::new(
+ io::ErrorKind::UnexpectedEof,
+ "unexpected EOF during handshake",
+ ));
+ }
+
+ self.step_initialize()?;
+ }
+ State::Streaming { sizes } => return Ok(Some(sizes)),
+ State::Shutdown => return Ok(None),
+ }
+ }
+ }
+
+ /// Returns true when the certificate was succesfully verified
+ /// Returns false, when a verification isn't necessary (yet)
+ /// Returns an error when the verification failed
+ fn validate(&mut self, require_cert: bool) -> io::Result<bool> {
+ // If we're accepting connections then we don't perform any validation
+ // for the remote certificate, that's what they're doing!
+ if self.server {
+ return Ok(false);
+ }
+
+ let cert_context = match self.context.remote_cert() {
+ Err(_) if !require_cert => return Ok(false),
+ ret => ret?,
+ };
+
+ let cert_chain = unsafe {
+ let cert_store = match (cert_context.cert_store(), &self.cert_store) {
+ (Some(ref mut chain_certs), &Some(ref extra_certs)) => {
+ for extra_cert in extra_certs.certs() {
+ chain_certs.add_cert(&extra_cert, CertAdd::ReplaceExisting)?;
+ }
+ chain_certs.as_inner()
+ }
+ (Some(chain_certs), &None) => chain_certs.as_inner(),
+ (None, &Some(ref extra_certs)) => extra_certs.as_inner(),
+ (None, &None) => ptr::null_mut(),
+ };
+
+ let flags = Cryptography::CERT_CHAIN_CACHE_END_CERT
+ | Cryptography::CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY
+ | Cryptography::CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
+
+ let mut para: Cryptography::CERT_CHAIN_PARA = mem::zeroed();
+ para.cbSize = mem::size_of_val(&para) as u32;
+ para.RequestedUsage.dwType = Cryptography::USAGE_MATCH_TYPE_OR;
+
+ let mut identifiers = [
+ Cryptography::szOID_PKIX_KP_SERVER_AUTH as _,
+ Cryptography::szOID_SERVER_GATED_CRYPTO as _,
+ Cryptography::szOID_SGC_NETSCAPE as _,
+ ];
+ para.RequestedUsage.Usage.cUsageIdentifier = identifiers.len() as u32;
+ para.RequestedUsage.Usage.rgpszUsageIdentifier = identifiers.as_mut_ptr();
+
+ let mut cert_chain = mem::zeroed();
+
+ let res = Cryptography::CertGetCertificateChain(
+ Cryptography::HCERTCHAINENGINE::default(),
+ cert_context.as_inner(),
+ ptr::null_mut(),
+ cert_store,
+ &para,
+ flags,
+ ptr::null_mut(),
+ &mut cert_chain,
+ );
+
+ if res != 0 {
+ CertChainContext(cert_chain)
+ } else {
+ return Err(io::Error::last_os_error());
+ }
+ };
+
+ unsafe {
+ // check if we trust the root-CA explicitly
+ let mut para_flags = Cryptography::CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
+ if let Some(ref mut store) = self.cert_store {
+ if let Some(chain) = cert_chain.final_chain() {
+ // check if any cert of the chain is in the passed store (and therefore trusted)
+ if chain
+ .certificates()
+ .any(|cert| store.certs().any(|root_cert| root_cert == cert))
+ {
+ para_flags |= Cryptography::CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG;
+ }
+ }
+ }
+
+ let mut extra_para: Cryptography::HTTPSPolicyCallbackData = mem::zeroed();
+ extra_para.Anonymous.cbSize = mem::size_of_val(&extra_para) as u32;
+ extra_para.dwAuthType = Cryptography::AUTHTYPE_SERVER;
+ match self.domain {
+ Some(ref mut domain) if !self.accept_invalid_hostnames => {
+ extra_para.pwszServerName = domain.as_mut_ptr();
+ }
+ _ => {}
+ }
+
+ let mut para: Cryptography::CERT_CHAIN_POLICY_PARA = mem::zeroed();
+ para.cbSize = mem::size_of_val(&para) as u32;
+ para.dwFlags = para_flags;
+ para.pvExtraPolicyPara = &mut extra_para as *mut _ as *mut _;
+
+ let mut status: Cryptography::CERT_CHAIN_POLICY_STATUS = mem::zeroed();
+ status.cbSize = mem::size_of_val(&status) as u32;
+
+ let verify_chain_policy_structure = Cryptography::CERT_CHAIN_POLICY_SSL;
+ let res = Cryptography::CertVerifyCertificateChainPolicy(
+ verify_chain_policy_structure,
+ cert_chain.0,
+ &para,
+ &mut status,
+ );
+ if res == 0 {
+ return Err(io::Error::last_os_error());
+ }
+
+ let mut verify_result = if status.dwError != Foundation::ERROR_SUCCESS {
+ Err(io::Error::from_raw_os_error(status.dwError as i32))
+ } else {
+ Ok(())
+ };
+
+ // check if there's a user-specified verify callback
+ if let Some(ref callback) = self.verify_callback {
+ verify_result = callback(CertValidationResult {
+ chain: cert_chain,
+ res: status.dwError as i32,
+ chain_index: status.lChainIndex,
+ element_index: status.lElementIndex,
+ });
+ }
+ verify_result?;
+ }
+ Ok(true)
+ }
+
+ fn write_out(&mut self) -> io::Result<usize> {
+ let mut out = 0;
+ while self.out_buf.position() as usize != self.out_buf.get_ref().len() {
+ let position = self.out_buf.position() as usize;
+ let nwritten = self.stream.write(&self.out_buf.get_ref()[position..])?;
+ out += nwritten;
+ self.out_buf.set_position((position + nwritten) as u64);
+ }
+
+ Ok(out)
+ }
+
+ fn read_in(&mut self) -> io::Result<usize> {
+ let mut sum_nread = 0;
+
+ while self.needs_read > 0 {
+ let existing_len = self.enc_in.position() as usize;
+ let min_len = cmp::max(cmp::max(1024, 2 * existing_len), self.needs_read);
+ if self.enc_in.get_ref().len() < min_len {
+ self.enc_in.get_mut().resize(min_len, 0);
+ }
+ let nread = {
+ let buf = &mut self.enc_in.get_mut()[existing_len..];
+ self.stream.read(buf)?
+ };
+ self.enc_in.set_position((existing_len + nread) as u64);
+ self.needs_read = self.needs_read.saturating_sub(nread);
+ if nread == 0 {
+ break;
+ }
+ sum_nread += nread;
+ }
+
+ Ok(sum_nread)
+ }
+
+ fn consume_enc_in(&mut self, nread: usize) {
+ let size = self.enc_in.position() as usize;
+ assert!(size >= nread);
+ let count = size - nread;
+
+ if count > 0 {
+ self.enc_in.get_mut().drain(..nread);
+ }
+
+ self.enc_in.set_position(count as u64);
+ }
+
+ fn decrypt(&mut self) -> io::Result<bool> {
+ unsafe {
+ let position = self.enc_in.position() as usize;
+ let mut bufs = [
+ secbuf(
+ Identity::SECBUFFER_DATA,
+ Some(&mut self.enc_in.get_mut()[..position]),
+ ),
+ secbuf(Identity::SECBUFFER_EMPTY, None),
+ secbuf(Identity::SECBUFFER_EMPTY, None),
+ secbuf(Identity::SECBUFFER_EMPTY, None),
+ ];
+ let bufdesc = secbuf_desc(&mut bufs);
+
+ match Identity::DecryptMessage(self.context.get_mut(), &bufdesc, 0, ptr::null_mut()) {
+ Foundation::SEC_E_OK => {
+ let start = bufs[1].pvBuffer as usize - self.enc_in.get_ref().as_ptr() as usize;
+ let end = start + bufs[1].cbBuffer as usize;
+ self.dec_in.get_mut().clear();
+ self.dec_in
+ .get_mut()
+ .extend_from_slice(&self.enc_in.get_ref()[start..end]);
+ self.dec_in.set_position(0);
+
+ let nread = if bufs[3].BufferType == Identity::SECBUFFER_EXTRA {
+ self.enc_in.position() as usize - bufs[3].cbBuffer as usize
+ } else {
+ self.enc_in.position() as usize
+ };
+ self.consume_enc_in(nread);
+ self.needs_read = (self.enc_in.position() == 0) as usize;
+ Ok(false)
+ }
+ Foundation::SEC_E_INCOMPLETE_MESSAGE => {
+ self.needs_read = if bufs[1].BufferType == Identity::SECBUFFER_MISSING {
+ bufs[1].cbBuffer as usize
+ } else {
+ 1
+ };
+ Ok(false)
+ }
+ Foundation::SEC_I_CONTEXT_EXPIRED => Ok(true),
+ Foundation::SEC_I_RENEGOTIATE => {
+ self.state = State::Initializing {
+ needs_flush: false,
+ more_calls: true,
+ shutting_down: false,
+ validated: false,
+ };
+
+ let nread = if bufs[3].BufferType == Identity::SECBUFFER_EXTRA {
+ self.enc_in.position() as usize - bufs[3].cbBuffer as usize
+ } else {
+ self.enc_in.position() as usize
+ };
+ self.consume_enc_in(nread);
+ self.needs_read = 0;
+ Ok(false)
+ }
+ err => Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+ }
+
+ fn encrypt(
+ &mut self,
+ buf: &[u8],
+ sizes: &Identity::SecPkgContext_StreamSizes,
+ ) -> io::Result<()> {
+ assert!(buf.len() <= sizes.cbMaximumMessage as usize);
+
+ unsafe {
+ let len = sizes.cbHeader as usize + buf.len() + sizes.cbTrailer as usize;
+
+ if self.out_buf.get_ref().len() < len {
+ self.out_buf.get_mut().resize(len, 0);
+ }
+
+ let message_start = sizes.cbHeader as usize;
+ self.out_buf.get_mut()[message_start..message_start + buf.len()].clone_from_slice(buf);
+
+ let mut bufs = {
+ let out_buf = self.out_buf.get_mut();
+ let size = sizes.cbHeader as usize;
+
+ let header = secbuf(
+ Identity::SECBUFFER_STREAM_HEADER,
+ Some(&mut out_buf[..size]),
+ );
+ let data = secbuf(
+ Identity::SECBUFFER_DATA,
+ Some(&mut out_buf[size..size + buf.len()]),
+ );
+ let trailer = secbuf(
+ Identity::SECBUFFER_STREAM_TRAILER,
+ Some(&mut out_buf[size + buf.len()..]),
+ );
+ let empty = secbuf(Identity::SECBUFFER_EMPTY, None);
+ [header, data, trailer, empty]
+ };
+ let bufdesc = secbuf_desc(&mut bufs);
+
+ match Identity::EncryptMessage(self.context.get_mut(), 0, &bufdesc, 0) {
+ Foundation::SEC_E_OK => {
+ let len = bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer;
+ self.out_buf.get_mut().truncate(len as usize);
+ self.out_buf.set_position(0);
+ Ok(())
+ }
+ err => Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+ }
+}
+
+impl<S> MidHandshakeTlsStream<S> {
+ /// Returns a shared reference to the inner stream.
+ pub fn get_ref(&self) -> &S {
+ self.inner.get_ref()
+ }
+
+ /// Returns a mutable reference to the inner stream.
+ pub fn get_mut(&mut self) -> &mut S {
+ self.inner.get_mut()
+ }
+}
+
+impl<S> MidHandshakeTlsStream<S>
+where
+ S: Read + Write,
+{
+ /// Restarts the handshake process.
+ pub fn handshake(mut self) -> Result<TlsStream<S>, HandshakeError<S>> {
+ match self.inner.initialize() {
+ Ok(_) => Ok(self.inner),
+ Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
+ Err(HandshakeError::Interrupted(self))
+ }
+ Err(e) => Err(HandshakeError::Failure(e)),
+ }
+ }
+}
+
+impl<S> Write for TlsStream<S>
+where
+ S: Read + Write,
+{
+ /// In the case of a WouldBlock error, we expect another call
+ /// starting with the same input data
+ /// This is similar to the use of ACCEPT_MOVING_WRITE_BUFFER in openssl
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let sizes = match self.initialize()? {
+ Some(sizes) => sizes,
+ None => {
+ return Err(io::Error::from_raw_os_error(
+ Foundation::SEC_E_CONTEXT_EXPIRED as i32,
+ ))
+ }
+ };
+
+ // if we have pending output data, it must have been because a previous
+ // attempt to send this part of the data ran into an error.
+ if self.out_buf.position() == self.out_buf.get_ref().len() as u64 {
+ let len = cmp::min(buf.len(), sizes.cbMaximumMessage as usize);
+ self.encrypt(&buf[..len], &sizes)?;
+ self.last_write_len = len;
+ }
+ self.write_out()?;
+
+ Ok(self.last_write_len)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ // Make sure the write buffer is emptied
+ self.write_out()?;
+ self.stream.flush()
+ }
+}
+
+impl<S> Read for TlsStream<S>
+where
+ S: Read + Write,
+{
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let nread = {
+ let read_buf = self.fill_buf()?;
+ let nread = cmp::min(buf.len(), read_buf.len());
+ buf[..nread].copy_from_slice(&read_buf[..nread]);
+ nread
+ };
+ self.consume(nread);
+ Ok(nread)
+ }
+}
+
+impl<S> BufRead for TlsStream<S>
+where
+ S: Read + Write,
+{
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ while self.get_buf().is_empty() {
+ if self.initialize()?.is_none() {
+ break;
+ }
+
+ if self.needs_read > 0 {
+ if self.read_in()? == 0 {
+ break;
+ }
+ self.needs_read = 0;
+ }
+
+ let eof = self.decrypt()?;
+ if eof {
+ break;
+ }
+ }
+
+ Ok(self.get_buf())
+ }
+
+ fn consume(&mut self, amt: usize) {
+ let pos = self.dec_in.position() + amt as u64;
+ assert!(pos <= self.dec_in.get_ref().len() as u64);
+ self.dec_in.set_position(pos);
+ }
+}
diff --git a/vendor/schannel/test/cert.der b/vendor/schannel/test/cert.der
new file mode 100644
index 0000000..e1f964d
--- /dev/null
+++ b/vendor/schannel/test/cert.der
Binary files differ
diff --git a/vendor/schannel/test/cert.pem b/vendor/schannel/test/cert.pem
new file mode 100644
index 0000000..032fe60
--- /dev/null
+++ b/vendor/schannel/test/cert.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG
+A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub
+3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ
+mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6
+TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI
+ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y
+euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq
+hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM
+6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE
+wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY
+oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9
+dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp
+HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA==
+-----END CERTIFICATE-----
diff --git a/vendor/schannel/test/identity.p12 b/vendor/schannel/test/identity.p12
new file mode 100644
index 0000000..d16abb8
--- /dev/null
+++ b/vendor/schannel/test/identity.p12
Binary files differ
diff --git a/vendor/schannel/test/key.key b/vendor/schannel/test/key.key
new file mode 100644
index 0000000..6b6209f
--- /dev/null
+++ b/vendor/schannel/test/key.key
Binary files differ
diff --git a/vendor/schannel/test/key.pem b/vendor/schannel/test/key.pem
new file mode 100644
index 0000000..d381795
--- /dev/null
+++ b/vendor/schannel/test/key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk
+3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN
+DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM
+x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5
+H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm
+wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ
+JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/
+n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL
+Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL
+Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r
+YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE
+I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo
+YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9
+yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH
+RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F
+hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx
+qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf
+0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d
+0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T
+mEq154s5rmqh+h+XRIf7Au0SLw==
+-----END PRIVATE KEY-----
diff --git a/vendor/schannel/test/key_invalid_header.pem b/vendor/schannel/test/key_invalid_header.pem
new file mode 100644
index 0000000..6f2c8b8
--- /dev/null
+++ b/vendor/schannel/test/key_invalid_header.pem
@@ -0,0 +1,28 @@
+-----BEGIN GARBAGE-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk
+3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN
+DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM
+x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5
+H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm
+wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ
+JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/
+n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL
+Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL
+Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r
+YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE
+I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo
+YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9
+yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH
+RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F
+hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx
+qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf
+0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d
+0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T
+mEq154s5rmqh+h+XRIf7Au0SLw==
+-----END GARBAGE-----
diff --git a/vendor/schannel/test/key_no_end_header.pem b/vendor/schannel/test/key_no_end_header.pem
new file mode 100644
index 0000000..4b40953
--- /dev/null
+++ b/vendor/schannel/test/key_no_end_header.pem
@@ -0,0 +1,27 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk
+3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN
+DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM
+x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5
+H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm
+wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ
+JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/
+n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL
+Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL
+Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r
+YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE
+I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo
+YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9
+yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH
+RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F
+hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx
+qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf
+0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d
+0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T
+mEq154s5rmqh+h+XRIf7Au0SLw==
diff --git a/vendor/schannel/test/key_no_headers.pem b/vendor/schannel/test/key_no_headers.pem
new file mode 100644
index 0000000..4cf2191
--- /dev/null
+++ b/vendor/schannel/test/key_no_headers.pem
@@ -0,0 +1,26 @@
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk
+3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN
+DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM
+x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5
+H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm
+wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ
+JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/
+n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL
+Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL
+Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r
+YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE
+I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo
+YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9
+yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH
+RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F
+hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx
+qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf
+0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d
+0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T
+mEq154s5rmqh+h+XRIf7Au0SLw==
diff --git a/vendor/schannel/test/key_wrong_header.pem b/vendor/schannel/test/key_wrong_header.pem
new file mode 100644
index 0000000..4e5e070
--- /dev/null
+++ b/vendor/schannel/test/key_wrong_header.pem
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk
+3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN
+DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM
+x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5
+H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm
+wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ
+JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/
+n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL
+Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL
+Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r
+YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE
+I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo
+YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9
+yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH
+RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F
+hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx
+qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf
+0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d
+0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T
+mEq154s5rmqh+h+XRIf7Au0SLw==
+-----END CERTIFICATE-----
diff --git a/vendor/schannel/test/self-signed.badssl.com.cer b/vendor/schannel/test/self-signed.badssl.com.cer
new file mode 100644
index 0000000..a561d7e
--- /dev/null
+++ b/vendor/schannel/test/self-signed.badssl.com.cer
Binary files differ
diff --git a/vendor/schannel/update_test_cert.bat b/vendor/schannel/update_test_cert.bat
new file mode 100644
index 0000000..0e68416
--- /dev/null
+++ b/vendor/schannel/update_test_cert.bat
@@ -0,0 +1,2 @@
+openssl s_client -showcerts -connect self-signed.badssl.com:443 -servername self-signed.badssl.com <NUL|openssl x509 -outform der -out .\test\self-signed.badssl.com.cer
+openssl x509 -noout -fingerprint -sha1 -inform der -in .\test\self-signed.badssl.com.cer 2>&1|powershell.exe -command "Set-Content .\test\self-signed.badssl.com.cer.sha1 -Encoding Byte -Value ([byte[]] -split ($Input.split('=', 2)[1].replace(':', '') -replace '..', '0x$& '))" \ No newline at end of file