<commit_before>extern crate schannel;

use self::schannel::cert_context::{CertContext, HashAlgorithm, KeySpec};
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
use self::schannel::crypt_prov::{AcquireOptions, ProviderType};
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
use self::schannel::tls_stream;
use std::error;
use std::fmt;
use std::io;
use std::str;

use {TlsAcceptorBuilder, TlsConnectorBuilder};

const SEC_E_NO_CREDENTIALS: u32 = 0x8009030E;

static PROTOCOLS: &'static [Protocol] = &[
    Protocol::Ssl3,
    Protocol::Tls10,
    Protocol::Tls11,
    Protocol::Tls12,
];

fn convert_protocols(min: Option<::Protocol>, max: Option<::Protocol>) -> &'static [Protocol] {
    let mut protocols = PROTOCOLS;
    if let Some(p) = max.and_then(|max| protocols.get(..=max as usize)) {
        protocols = p;
    }
    if let Some(p) = min.and_then(|min| protocols.get(min as usize..)) {
        protocols = p;
    }
    protocols
}

pub struct Error(io::Error);

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error(error)
    }
}

#[derive(Clone)]
pub struct Identity {
    cert: CertContext,
}

impl Identity {
    pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
        let store = PfxImportOptions::new().password(pass).import(buf)?;
        let mut identity = None;

        for cert in store.certs() {
            if cert
                .private_key()
                .silent(true)
                .compare_key(true)
                .acquire()
                .is_ok()
            {
                identity = Some(cert);
                break;
            }
        }

        let identity = match identity {
            Some(identity) => identity,
            None => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "No identity found in PKCS #12 archive",
                )
                .into());
            }
        };

        Ok(Identity { cert: identity })
    }

    pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> {
        let mut store = Memory::new()?.into_store();
        let mut cert_iter = pem::PemBlock::new(pem).into_iter();
        let leaf = cert_iter.next().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "at least one certificate must be provided to create an identity",
            )
        })?;
        let cert = CertContext::from_pem(std::str::from_utf8(leaf).map_err(|_| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "leaf cert contains invalid utf8",
            )
        })?)?;

        let mut options = AcquireOptions::new();
        options.container("schannel");
        let type_ = ProviderType::rsa_full();

        let mut container = match options.acquire(type_) {
            Ok(container) => container,
            Err(_) => options.new_keyset(true).acquire(type_)?,
        };
        container.import().import_pkcs8_pem(&key)?;

        cert.set_key_prov_info()
            .container("schannel")
            .type_(type_)
            .keep_open(true)
            .key_spec(KeySpec::key_exchange())
            .set()?;
        let mut context = store.add_cert(&cert, CertAdd::Always)?;

        for int_cert in cert_iter {
            let certificate = Certificate::from_pem(int_cert)?;
            context = store.add_cert(&certificate.0, CertAdd::Always)?;
        }
        Ok(Identity { cert: context })
    }
}

#[derive(Clone)]<commit_msg>Use unique container name<commit_after>extern crate schannel;

use self::schannel::cert_context::{CertContext, HashAlgorithm, KeySpec};
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
use self::schannel::crypt_prov::{AcquireOptions, ProviderType};
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
use self::schannel::tls_stream;
use std::error;
use std::fmt;
use std::io;
use std::str;

use {TlsAcceptorBuilder, TlsConnectorBuilder};

const SEC_E_NO_CREDENTIALS: u32 = 0x8009030E;

static PROTOCOLS: &'static [Protocol] = &[
    Protocol::Ssl3,
    Protocol::Tls10,
    Protocol::Tls11,
    Protocol::Tls12,
];

fn convert_protocols(min: Option<::Protocol>, max: Option<::Protocol>) -> &'static [Protocol] {
    let mut protocols = PROTOCOLS;
    if let Some(p) = max.and_then(|max| protocols.get(..=max as usize)) {
        protocols = p;
    }
    if let Some(p) = min.and_then(|min| protocols.get(min as usize..)) {
        protocols = p;
    }
    protocols
}

pub struct Error(io::Error);

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        error::Error::source(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error(error)
    }
}

#[derive(Clone)]
pub struct Identity {
    cert: CertContext,
}

impl Identity {
    pub fn from_pkcs12(buf: &[u8], pass: &str) -> Result<Identity, Error> {
        let store = PfxImportOptions::new().password(pass).import(buf)?;
        let mut identity = None;

        for cert in store.certs() {
            if cert
                .private_key()
                .silent(true)
                .compare_key(true)
                .acquire()
                .is_ok()
            {
                identity = Some(cert);
                break;
            }
        }

        let identity = match identity {
            Some(identity) => identity,
            None => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "No identity found in PKCS #12 archive",
                )
                .into());
            }
        };

        Ok(Identity { cert: identity })
    }

    pub fn from_pkcs8(pem: &[u8], key: &[u8]) -> Result<Identity, Error> {
        let mut store = Memory::new()?.into_store();
        let mut cert_iter = pem::PemBlock::new(pem).into_iter();
        let leaf = cert_iter.next().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "at least one certificate must be provided to create an identity",
            )
        })?;
        let cert = CertContext::from_pem(std::str::from_utf8(leaf).map_err(|_| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "leaf cert contains invalid utf8",
            )
        })?)?;

        let name = gen_container_name();
        let mut options = AcquireOptions::new();
        options.container(&name);
        let type_ = ProviderType::rsa_full();

        let mut container = match options.acquire(type_) {
            Ok(container) => container,
            Err(_) => options.new_keyset(true).acquire(type_)?,
        };
        container.import().import_pkcs8_pem(&key)?;

        cert.set_key_prov_info()
            .container(&name)
            .type_(type_)
            .keep_open(true)
            .key_spec(KeySpec::key_exchange())
            .set()?;
        let mut context = store.add_cert(&cert, CertAdd::Always)?;

        for int_cert in cert_iter {
            let certificate = Certificate::from_pem(int_cert)?;
            context = store.add_cert(&certificate.0, CertAdd::Always)?;
        }
        Ok(Identity { cert: context })
    }
}

// The name of the container must be unique to have multiple active keys.
fn gen_container_name() -> String {
    use std::sync::atomic::{AtomicUsize, Ordering};
    static COUNTER: AtomicUsize = AtomicUsize::new(0);
    format!("native-tls-{}", COUNTER.fetch_add(1, Ordering::Relaxed))
}

#[derive(Clone)]
{'fraction_non_alphanumeric': 0.14392946503038245, 'fraction_numerical': 0.007863695937090432, 'mean_word_length': 2.2904743237945904, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1506160', 'n_tokens_mistral': 2638, 'n_tokens_neox': 2589, 'n_words': 557}