chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/schannel/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"85405a6dca13d8d585b4eedbf55643ee4d31988d92cc98c9bf30671d3a4c85b2","Cargo.lock":"9e42626cd21577a1de603914fcf78fe56b29b201587aa964d5a1a35c37e9d993","Cargo.toml":"929f428ff42001e8c5f41d30b396d8c4995da4e25921987d33cf862d56eed633","Cargo.toml.orig":"031774ac518f74efac72f601efcb8ed1617f9651458c72fd8d07c2002808d1a6","LICENSE.md":"aa72991ac35b4de0034da0afe943e62b48c4092fc2ba13ae47806d8e9a4ad551","README.md":"1356487328648f2e2ee621872cc0adb930d1f6929e587ceb1877ef3eb989fd44","src/alpn_list.rs":"1401fa57397540a42c3fa3b00c4bf901150b63d020a112d63ae56f17c241a98b","src/cert_chain.rs":"a09c6ca084b19a5ecd86b115454c4de7ce5e1146c747c6e25adec1af5ee4708d","src/cert_context.rs":"1a8f104990313b514a67c3f97d1c4d954b5fa18d15b2a54a40a8e7f8a7789848","src/cert_store.rs":"88e9b4e8b7b8109baf2c96612965d7fe249aa6513ed75a4dd3d243298f900a29","src/context_buffer.rs":"f6b667b55d8e6f4c1826519e9030066cd489399dca2d55bb927fe7f583f62715","src/crypt_key.rs":"1d0d8baeafbe30a2e43ff677fc8e707150306f92d13ede6692ba8d84dba9e0e4","src/crypt_prov.rs":"77284f20aeac4faa3747ef156b1e4ffe0b2e994229e975392a2d624936660dc9","src/ctl_context.rs":"0fabd379c98ac3325c357c0188a7eebd1036a10ce029e64b82a53e6ef19bb02e","src/key_handle.rs":"01e8b642db3759b3cb73c035a664404353a6b1d77f571b7591047a23d3163a1a","src/lib.rs":"17b4cd6e510e045d9ad56557e4e66b9a2d39a18111840ea3cc970674f106ae2e","src/ncrypt_key.rs":"25d08cdb4c9337cfe3983c4381a95536c2fc24b7aeb8df23ddf4367ad9e17943","src/schannel_cred.rs":"4799f99767d15a4eb03853686e77129d79eb743e2f6ed6c4d4d1357bc6c537fb","src/security_context.rs":"dba9600184d526e6a1dd582e1fcf66658fe8dcc707b6997692e7f85078beffd3","src/test.rs":"b787f4852ff2d8e8a39ee75ffb4677ef4459ea90085bdc3a341613751b92808e","src/tls_stream.rs":"c1eb9ac4a44582f7cdd522fb7ae9122b4a9fb3ee8ec9c6b4d96f20982feb7981"},"package":"91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"}

6
vendor/schannel/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "ff7ffd306152f039cff49f42801a63519068ce0b"
},
"path_in_vcs": ""
}

25
vendor/schannel/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "schannel"
version = "0.1.29"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-sys"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
dependencies = [
"windows-link",
]

69
vendor/schannel/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,69 @@
# 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"
rust-version = "1.71.0"
name = "schannel"
version = "0.1.29"
authors = [
"Steven Fackler <sfackler@gmail.com>",
"Steffen Butzer <steffen.butzer@outlook.com>",
]
build = false
include = [
"/src/",
"LICENSE.md",
"README.md",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
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"
[lib]
name = "schannel"
path = "src/lib.rs"
[dependencies.windows-sys]
version = "0.61"
features = [
"Win32_Foundation",
"Win32_Security_Cryptography",
"Win32_Security_Authentication_Identity",
"Win32_Security_Credentials",
"Win32_System_LibraryLoader",
"Win32_System_Memory",
"Win32_System_SystemInformation",
]
[dev-dependencies.windows-sys]
version = "0.61"
features = [
"Win32_System_SystemInformation",
"Win32_System_Time",
]

7
vendor/schannel/LICENSE.md vendored Normal file
View File

@@ -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.

6
vendor/schannel/README.md vendored Normal file
View File

@@ -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.

93
vendor/schannel/src/alpn_list.rs vendored Normal file
View File

@@ -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())
}
}
}

139
vendor/schannel/src/cert_chain.rs vendored Normal file
View File

@@ -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)
}
}

743
vendor/schannel/src/cert_context.rs vendored Normal file
View File

@@ -0,0 +1,743 @@
//! 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
#[derive(Copy, Clone)]
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 = windows_sys::core::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 {
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());
}
}

484
vendor/schannel/src/cert_store.rs vendored Normal file
View File

@@ -0,0 +1,484 @@
//! 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,
)
}
/// If set, the private key in the archive will be exportable.
pub fn exportable_private_key(
&mut self,
exportable_private_key: bool,
) -> &mut PfxImportOptions {
self.flag(Cryptography::CRYPT_EXPORTABLE, exportable_private_key)
}
fn flag(&mut self, flag: u32, set: bool) -> &mut PfxImportOptions {
if set {
self.flags |= flag;
} else {
self.flags &= !flag;
}
self
}
/// If set, the private keys are stored under the local computer and not under the current user.
pub fn machine_keyset(&mut self, machine_keyset: bool) -> &mut PfxImportOptions {
self.flag(Cryptography::CRYPT_MACHINE_KEYSET, machine_keyset)
}
/// 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);
}
}

26
vendor/schannel/src/context_buffer.rs vendored Normal file
View File

@@ -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) }
}
}

16
vendor/schannel/src/crypt_key.rs vendored Normal file
View File

@@ -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);

420
vendor/schannel/src/crypt_prov.rs vendored Normal file
View File

@@ -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::Foundation;
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);
Foundation::LocalFree(buf as *mut _);
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));
Foundation::LocalFree(buf as *mut _);
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());
}
}

187
vendor/schannel/src/ctl_context.rs vendored Normal file
View File

@@ -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)
}
}
}

3
vendor/schannel/src/key_handle.rs vendored Normal file
View File

@@ -0,0 +1,3 @@
//! Deprecated.
#[deprecated(note = "use cert_context::PrivateKey", since = "0.1.5")]
pub use crate::cert_context::PrivateKey as KeyHandle;

114
vendor/schannel/src/lib.rs vendored Normal file
View File

@@ -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(),
}
}

16
vendor/schannel/src/ncrypt_key.rs vendored Normal file
View File

@@ -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);

341
vendor/schannel/src/schannel_cred.rs vendored Normal file
View File

@@ -0,0 +1,341 @@
//! 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-Hellmanagreed 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,
}
}
}
fn verify_min_os_build(major: u32, build: u32) -> Option<()> {
use windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW;
let handle = std::ptr::NonNull::new(unsafe {
windows_sys::Win32::System::LibraryLoader::GetModuleHandleW(windows_sys::w!("ntdll.dll"))
})?;
let rtl_get_ver = unsafe {
windows_sys::Win32::System::LibraryLoader::GetProcAddress(handle.as_ptr(), windows_sys::s!("RtlGetVersion"))
}?;
type RtlGetVersionFunc = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
let proc: RtlGetVersionFunc = unsafe { mem::transmute(rtl_get_ver) };
let mut info: OSVERSIONINFOW = unsafe { mem::zeroed() };
info.dwOSVersionInfoSize = mem::size_of::<OSVERSIONINFOW>() as u32;
unsafe { proc(&mut info) };
if info.dwMajorVersion > major || (info.dwMajorVersion == major && info.dwBuildNumber >= build) {
Some(())
} else {
None
}
}
/// 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> {
let mut enabled_protocols: u32 = 0;
if let Some(ref enable_list) = self.enabled_protocols {
enabled_protocols = enable_list
.iter()
.map(|p| p.dword(direction))
.fold(0, |acc, p| acc | p);
}
unsafe {
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;
cred_data.grbitEnabledProtocols = enabled_protocols;
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 mut tls_param: Identity::TLS_PARAMETERS = mem::zeroed();
let mut cred_data2: Identity::SCH_CREDENTIALS = mem::zeroed();
let mut pauthdata: *const core::ffi::c_void = ptr::null();
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 _;
} else if verify_min_os_build(10, 17763).is_some() {
// If no algorithms specified and should be supported, use new SCH_CREDENTIALS interface which supports TLS1.3.
// Although we check for win10 build 17763 above, I have only seen this work on win 11.
if enabled_protocols != 0 {
tls_param.grbitDisabledProtocols = !enabled_protocols;
}
// TODO: support something to select tls13-ciphers
cred_data2.dwVersion = Identity::SCH_CREDENTIALS_VERSION;
cred_data2.dwFlags = Identity::SCH_USE_STRONG_CRYPTO | Identity::SCH_CRED_NO_DEFAULT_CREDS;
cred_data2.cCreds = certs.len() as u32;
cred_data2.paCred = certs.as_mut_ptr() as _;
cred_data2.cTlsParameters = 1;
cred_data2.pTlsParameters = &mut tls_param;
pauthdata = &mut cred_data2 as *const _ as *const _;
}
if pauthdata.is_null() {
pauthdata = &mut cred_data as *const _ as *const _;
}
let direction = match direction {
Direction::Inbound => Identity::SECPKG_CRED_INBOUND,
Direction::Outbound => Identity::SECPKG_CRED_OUTBOUND,
};
let mut handle: Credentials::SecHandle = mem::zeroed();
match Identity::AcquireCredentialsHandleA(
ptr::null(),
Identity::UNISP_NAME_A,
direction,
ptr::null_mut(),
pauthdata,
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
}
}

134
vendor/schannel/src/security_context.rs vendored Normal file
View File

@@ -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))
}
}
}

1072
vendor/schannel/src/test.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1063
vendor/schannel/src/tls_stream.rs vendored Normal file

File diff suppressed because it is too large Load Diff