// Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashMap;
use std::mem::size_of;

use super::{CertInfo, EcKeyInfo, RsaKeyInfo};
use crate::backend::Mechanism;
use crate::crypto;
use crate::pkcs11;
use crate::util::{CkRawAttrTemplate, Error as UtilError};
use crate::{Error, Result};

/// Object and object attribute handling logic. See the PKCS#11
/// Section 4 on objects for more details on how these attributes
/// are handled. Each object has a unique handle and
/// a well defined class (i.e. private key, certificate etc.) and
/// based on this class a well defined set of valid attributes.
/// Since there is no R/W session support these objects are created
/// from the user provisioned database.
#[derive(Clone, Copy, Debug, Hash)]
pub struct ObjectHandle(u64);

impl From<pkcs11::CK_OBJECT_HANDLE> for ObjectHandle {
    fn from(src: pkcs11::CK_OBJECT_HANDLE) -> Self {
        Self(src)
    }
}

impl From<usize> for ObjectHandle {
    fn from(src: usize) -> Self {
        Self(src as u64)
    }
}

impl From<u32> for ObjectHandle {
    fn from(src: u32) -> Self {
        Self(src as u64)
    }
}

impl From<ObjectHandle> for u64 {
    fn from(src: ObjectHandle) -> Self {
        src.0
    }
}

impl From<ObjectHandle> for usize {
    fn from(src: ObjectHandle) -> Self {
        src.0 as usize
    }
}

#[derive(Clone)]
pub enum Attr {
    Bytes(Vec<u8>),
    CkBbool([u8; size_of::<pkcs11::CK_BBOOL>()]),
    CkByte([u8; size_of::<pkcs11::CK_BYTE>()]),
    CkKeyType([u8; size_of::<pkcs11::CK_KEY_TYPE>()]),
    CkCertType([u8; size_of::<pkcs11::CK_CERTIFICATE_TYPE>()]),
    CkCertCategory([u8; size_of::<pkcs11::CK_CERTIFICATE_CATEGORY>()]),
    CkMechanismType([u8; size_of::<pkcs11::CK_MECHANISM_TYPE>()]),
    CkObjectClass([u8; size_of::<pkcs11::CK_OBJECT_CLASS>()]),
    CkUlong([u8; size_of::<pkcs11::CK_ULONG>()]),
    Sensitive,
}

impl Attr {
    const CK_TRUE: Self = Self::CkBbool([pkcs11::CK_TRUE; 1]);
    const CK_FALSE: Self = Self::CkBbool([pkcs11::CK_FALSE; 1]);

    fn len(&self) -> usize {
        match self {
            Self::CkBbool(v) => v.len(),
            Self::CkByte(v) => v.len(),
            Self::CkKeyType(v) => v.len(),
            Self::CkCertType(v) => v.len(),
            Self::CkCertCategory(v) => v.len(),
            Self::CkMechanismType(v) => v.len(),
            Self::CkObjectClass(v) => v.len(),
            Self::CkUlong(v) => v.len(),
            Self::Bytes(v) => v.len(),
            Self::Sensitive => 0,
        }
    }

    fn as_bytes(&self) -> &[u8] {
        match self {
            Self::CkBbool(v) => v,
            Self::CkByte(v) => v,
            Self::CkKeyType(v) => v,
            Self::CkCertType(v) => v,
            Self::CkCertCategory(v) => v,
            Self::CkMechanismType(v) => v,
            Self::CkObjectClass(v) => v,
            Self::CkUlong(v) => v,
            Self::Bytes(v) => v,
            Self::Sensitive => &[0u8; 0],
        }
    }

    fn from_ck_byte(src: pkcs11::CK_BYTE) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkByte(src.to_le_bytes())
    }

    fn from_ck_key_type(src: pkcs11::CK_KEY_TYPE) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkKeyType(src.to_le_bytes())
    }

    fn from_ck_cert_type(src: pkcs11::CK_CERTIFICATE_TYPE) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkCertType(src.to_le_bytes())
    }
    fn from_ck_cert_category(src: pkcs11::CK_CERTIFICATE_CATEGORY) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkCertCategory(src.to_le_bytes())
    }

    fn from_ck_mechanism_type(src: pkcs11::CK_MECHANISM_TYPE) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkMechanismType(src.to_le_bytes())
    }

    fn from_ck_object_class(src: pkcs11::CK_OBJECT_CLASS) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkObjectClass(src.to_le_bytes())
    }

    fn from_ck_ulong(src: pkcs11::CK_ULONG) -> Self {
        #[cfg(target_endian = "little")]
        Self::CkUlong(src.to_le_bytes())
    }
}

impl PartialEq<Attr> for Attr {
    fn eq(&self, other: &Attr) -> bool {
        self.as_bytes() == other.as_bytes()
    }
}

#[derive(Clone, Debug)]
pub enum ObjectKind {
    RsaPrivateKey(String),
    RsaPublicKey(String),
    EcPrivateKey(String),
    EcPublicKey(String),
    Certificate,
    Mechanism(Mechanism),
}

#[derive(Clone)]
pub struct Object {
    attrs: HashMap<pkcs11::CK_ATTRIBUTE_TYPE, Attr>,
    kind: ObjectKind,
}

impl Object {
    pub fn new_mechanism(mech: Mechanism) -> Self {
        let mut attrs = HashMap::new();
        attrs.insert(
            pkcs11::CKA_CLASS,
            Attr::from_ck_object_class(pkcs11::CKO_MECHANISM),
        );
        attrs.insert(
            pkcs11::CKA_MECHANISM_TYPE,
            Attr::from_ck_mechanism_type(mech.ck_type()),
        );
        Self {
            kind: ObjectKind::Mechanism(mech),
            attrs,
        }
    }

    pub fn new_rsa_private_key(info: RsaKeyInfo) -> Self {
        let mut attrs = HashMap::new();
        attrs.insert(
            pkcs11::CKA_CLASS,
            Attr::from_ck_object_class(pkcs11::CKO_PRIVATE_KEY),
        );
        attrs.insert(
            pkcs11::CKA_KEY_TYPE,
            Attr::from_ck_key_type(pkcs11::CKK_RSA),
        );
        attrs.insert(pkcs11::CKA_ID, Attr::from_ck_byte(info.id));
        attrs.insert(pkcs11::CKA_LABEL, Attr::Bytes(info.label.into()));
        attrs.insert(pkcs11::CKA_PRIVATE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_ALWAYS_AUTHENTICATE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_SENSITIVE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_EXTRACTABLE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_SIGN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_DECRYPT, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_DERIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_WRAP, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_LOCAL, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_TOKEN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_MODULUS_BITS, Attr::from_ck_ulong(info.num_bits));
        attrs.insert(pkcs11::CKA_MODULUS, Attr::Bytes(info.modulus));
        attrs.insert(
            pkcs11::CKA_PUBLIC_EXPONENT,
            Attr::Bytes(info.public_exponent),
        );
        attrs.insert(pkcs11::CKA_PRIVATE_EXPONENT, Attr::Sensitive);
        attrs.insert(pkcs11::CKA_PRIME_1, Attr::Sensitive);
        attrs.insert(pkcs11::CKA_PRIME_2, Attr::Sensitive);
        attrs.insert(pkcs11::CKA_EXPONENT_1, Attr::Sensitive);
        attrs.insert(pkcs11::CKA_EXPONENT_2, Attr::Sensitive);
        attrs.insert(pkcs11::CKA_COEFFICIENT, Attr::Sensitive);
        Self {
            kind: ObjectKind::RsaPrivateKey(info.priv_pem),
            attrs,
        }
    }

    pub fn new_rsa_public_key(info: RsaKeyInfo) -> Self {
        let mut attrs = HashMap::new();
        attrs.insert(
            pkcs11::CKA_CLASS,
            Attr::from_ck_object_class(pkcs11::CKO_PUBLIC_KEY),
        );
        attrs.insert(
            pkcs11::CKA_KEY_TYPE,
            Attr::from_ck_key_type(pkcs11::CKK_RSA),
        );
        attrs.insert(pkcs11::CKA_ID, Attr::from_ck_byte(info.id));
        attrs.insert(pkcs11::CKA_LABEL, Attr::Bytes(info.label.into()));
        attrs.insert(pkcs11::CKA_PRIVATE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_SENSITIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_EXTRACTABLE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_VERIFY, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_ENCRYPT, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_DERIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_WRAP, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_LOCAL, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_TOKEN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_MODULUS_BITS, Attr::from_ck_ulong(info.num_bits));
        attrs.insert(pkcs11::CKA_MODULUS, Attr::Bytes(info.modulus));
        attrs.insert(
            pkcs11::CKA_PUBLIC_EXPONENT,
            Attr::Bytes(info.public_exponent),
        );
        Self {
            kind: ObjectKind::RsaPublicKey(info.priv_pem),
            attrs,
        }
    }

    pub fn new_ec_private_key(info: EcKeyInfo) -> Self {
        let mut attrs = HashMap::new();
        attrs.insert(
            pkcs11::CKA_CLASS,
            Attr::from_ck_object_class(pkcs11::CKO_PRIVATE_KEY),
        );
        attrs.insert(pkcs11::CKA_KEY_TYPE, Attr::from_ck_key_type(pkcs11::CKK_EC));
        attrs.insert(pkcs11::CKA_ID, Attr::from_ck_byte(info.id));
        attrs.insert(pkcs11::CKA_LABEL, Attr::Bytes(info.label.into()));
        attrs.insert(pkcs11::CKA_PRIVATE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_SENSITIVE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_EXTRACTABLE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_SIGN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_DECRYPT, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_DERIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_WRAP, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_LOCAL, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_TOKEN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_EC_PARAMS, Attr::Bytes(info.params_x962));
        attrs.insert(pkcs11::CKA_EC_POINT, Attr::Bytes(info.point_q_x962));
        attrs.insert(pkcs11::CKA_VALUE, Attr::Sensitive);
        Self {
            kind: ObjectKind::EcPrivateKey(info.priv_pem),
            attrs,
        }
    }

    pub fn new_ec_public_key(info: EcKeyInfo) -> Self {
        let mut attrs = HashMap::new();
        attrs.insert(
            pkcs11::CKA_CLASS,
            Attr::from_ck_object_class(pkcs11::CKO_PUBLIC_KEY),
        );
        attrs.insert(pkcs11::CKA_KEY_TYPE, Attr::from_ck_key_type(pkcs11::CKK_EC));
        attrs.insert(pkcs11::CKA_ID, Attr::from_ck_byte(info.id));
        attrs.insert(pkcs11::CKA_LABEL, Attr::Bytes(info.label.into()));
        attrs.insert(pkcs11::CKA_PRIVATE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_SENSITIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_EXTRACTABLE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_VERIFY, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_ENCRYPT, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_DERIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_WRAP, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_LOCAL, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_TOKEN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_EC_PARAMS, Attr::Bytes(info.params_x962));
        attrs.insert(pkcs11::CKA_EC_POINT, Attr::Bytes(info.point_q_x962));
        Self {
            kind: ObjectKind::EcPublicKey(info.priv_pem),
            attrs,
        }
    }

    pub fn new_x509_cert(info: CertInfo) -> Self {
        let mut attrs = HashMap::new();
        attrs.insert(
            pkcs11::CKA_CLASS,
            Attr::from_ck_object_class(pkcs11::CKO_CERTIFICATE),
        );
        attrs.insert(
            pkcs11::CKA_CERTIFICATE_TYPE,
            Attr::from_ck_cert_type(pkcs11::CKC_X_509),
        );
        let categ = match info.categ {
            crypto::CertCategory::Unverified => pkcs11::CK_CERTIFICATE_CATEGORY_UNSPECIFIED,
            crypto::CertCategory::Token => pkcs11::CK_CERTIFICATE_CATEGORY_TOKEN_USER,
            crypto::CertCategory::Authority => pkcs11::CK_CERTIFICATE_CATEGORY_AUTHORITY,
            crypto::CertCategory::Other => pkcs11::CK_CERTIFICATE_CATEGORY_OTHER_ENTITY,
        };
        attrs.insert(
            pkcs11::CKA_CERTIFICATE_CATEGORY,
            Attr::from_ck_cert_category(categ),
        );
        attrs.insert(pkcs11::CKA_ID, Attr::from_ck_byte(info.id));
        attrs.insert(pkcs11::CKA_LABEL, Attr::Bytes(info.label.into()));
        attrs.insert(pkcs11::CKA_TOKEN, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_SENSITIVE, Attr::CK_FALSE);
        attrs.insert(pkcs11::CKA_EXTRACTABLE, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_TRUSTED, Attr::CK_TRUE);
        attrs.insert(pkcs11::CKA_SUBJECT, Attr::Bytes(info.subject_der.into()));
        attrs.insert(pkcs11::CKA_ISSUER, Attr::Bytes(info.issuer_der.into()));
        attrs.insert(
            pkcs11::CKA_SERIAL_NUMBER,
            Attr::Bytes(info.serno_der.into()),
        );
        attrs.insert(pkcs11::CKA_VALUE, Attr::Bytes(info.cert_der.into()));
        Self {
            kind: ObjectKind::Certificate,
            attrs,
        }
    }

    pub fn attr(&self, attr_type: pkcs11::CK_ATTRIBUTE_TYPE) -> Option<&Attr> {
        self.attrs.get(&attr_type)
    }

    pub fn kind(&self) -> &ObjectKind {
        &self.kind
    }

    pub fn is_private(&self) -> bool {
        match self.attr(pkcs11::CKA_PRIVATE) {
            Some(attr) => *attr == Attr::CK_TRUE,
            _ => false,
        }
    }

    pub fn is_mechanism(&self) -> bool {
        match self.kind {
            ObjectKind::Mechanism(_) => true,
            _ => false,
        }
    }

    pub fn match_attr_template(&self, tpl: &CkRawAttrTemplate) -> bool {
        let mut class_matched = false;
        for raw_attr in tpl.iter() {
            match self.attr(raw_attr.type_()) {
                Some(attr) => match raw_attr.val_bytes() {
                    Some(raw_bytes) => attr.as_bytes() == raw_bytes || return false,
                    None => return false,
                },
                None => return false,
            };
            class_matched = class_matched || (raw_attr.type_() == pkcs11::CKA_CLASS);
        }

        // Per the PKCS#11 v2.40 spec, mechanism objects must only match templates that
        // explicitely provide CKA_CLASS = CKO_MECHANISM.
        if self.is_mechanism() {
            class_matched
        } else {
            true
        }
    }

    pub fn fill_attr_template(&self, tpl: &mut CkRawAttrTemplate) -> Result<()> {
        let mut rcode = pkcs11::CKR_OK;

        for mut raw_attr in tpl.iter() {
            match self.attr(raw_attr.type_()) {
                Some(attr) => {
                    let sres = match attr {
                        Attr::Sensitive => {
                            rcode = pkcs11::CKR_ATTRIBUTE_SENSITIVE;
                            raw_attr.set_len(pkcs11::CK_UNAVAILABLE_INFORMATION);
                            continue;
                        }
                        a => raw_attr.set_val_bytes(a.as_bytes()),
                    };
                    match sres {
                        Err(UtilError::BufTooSmall) => {
                            rcode = pkcs11::CKR_BUFFER_TOO_SMALL;
                            raw_attr.set_len(pkcs11::CK_UNAVAILABLE_INFORMATION);
                        }
                        _ => raw_attr.set_len(attr.len() as pkcs11::CK_ULONG),
                    };
                }
                None => {
                    rcode = pkcs11::CKR_ATTRIBUTE_TYPE_INVALID;
                    raw_attr.set_len(pkcs11::CK_UNAVAILABLE_INFORMATION);
                }
            };
        }
        if rcode == pkcs11::CKR_OK {
            Ok(())
        } else {
            Err(Error::CkError(rcode))
        }
    }
}


// Copyright 2021-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::bignum_to_vec;
use super::ffi;
use super::FfiBox;
use super::{Error, Pkey, Result};

/// Logic for managing X509 public key certificates
/// These certificates are constructed from plain PEM files provided during
/// provisioning. The X509 is a wrapper over the generic X509
/// presented in the backend cryptographic library.
pub struct X509(FfiBox<ffi::X509>);

/// Certificate object type
#[derive(Clone)]
pub enum CertCategory {
    #[allow(dead_code)]
    /// Default (unverified)
    Unverified,
    /// Token certificate
    Token,
    /// CA certificate
    Authority,
    #[allow(dead_code)]
    /// Other
    Other,
}

impl X509 {
    /// Construct a certificate object from a BIO entry loaded with a
    /// certificate (or certificate chain) PEM file
    pub fn from_bio(bio: *mut ffi::BIO) -> Result<Self> {
        let cert = FfiBox::new(unsafe {
            ffi::PEM_read_bio_X509(
                bio,
                std::ptr::null_mut(),
                std::ptr::null(),
                std::ptr::null_mut(),
            )
        })?;

        Ok(Self(cert))
    }

    /// Get the certificate subject
    pub fn subject_name(&self) -> Result<Vec<u8>> {
        let name = unsafe { ffi::X509_get_subject_name(self.as_ptr()) };
        let pder = std::ptr::null();
        let mut pderlen: ffi::c_size_t = 0;
        let rc = unsafe { ffi::X509_NAME_get0_der(name, &pder, &mut pderlen) };
        if rc != 1 {
            return Err(Error::CertName);
        }
        if pderlen > 0 {
            let vec = unsafe { std::slice::from_raw_parts(pder, pderlen as usize).to_vec() };
            return Ok(vec);
        }
        Err(Error::CertName)
    }

    /// Get the certificate issuer
    pub fn issuer(&self) -> Result<Vec<u8>> {
        let issuer = unsafe { ffi::X509_get_issuer_name(self.as_ptr()) };
        let pder = std::ptr::null();
        let mut pderlen: ffi::c_size_t = 0;
        let rc = unsafe { ffi::X509_NAME_get0_der(issuer, &pder, &mut pderlen) };
        if rc != 1 {
            return Err(Error::CertIssuer);
        }
        if pderlen > 0 {
            let vec = unsafe { std::slice::from_raw_parts(pder, pderlen as usize).to_vec() };
            return Ok(vec);
        }
        Err(Error::CertIssuer)
    }

    /// Get the certificate serial number
    pub fn serial_no(&self) -> Result<Vec<u8>> {
        let asn1_sn = unsafe { ffi::X509_get0_serialNumber(self.as_ptr()) };
        let bn = unsafe { ffi::ASN1_INTEGER_to_BN(asn1_sn, std::ptr::null_mut()) };
        if bn.is_null() {
            return Err(Error::CertSerialNo);
        }
        let sn = bignum_to_vec(bn)?;
        unsafe { ffi::BN_free(bn) };
        Ok(sn)
    }

    /// PEM to DER X509 encoder. Needed as per pkcs#11 standard when storing certificates
    pub fn to_der(&self) -> Result<Vec<u8>> {
        let mut bio = FfiBox::new(unsafe { ffi::BIO_new(ffi::BIO_s_mem()) })?;
        let rv = unsafe { ffi::i2d_X509_bio(bio.as_mut_ptr(), self.as_ptr()) };
        if rv != 1 {
            return Err(Error::CertDerEncode);
        }
        let out_ptr = std::ptr::null();
        let mut out_ptr_len = 0;
        let rv = unsafe { ffi::BIO_mem_contents(bio.as_ptr(), &out_ptr, &mut out_ptr_len) };
        if rv != 1 {
            return Err(Error::CertDerEncode);
        }
        let vec = unsafe { std::slice::from_raw_parts(out_ptr, out_ptr_len as usize).to_vec() };
        Ok(vec)
    }

    pub fn as_ptr(&self) -> *const ffi::X509 {
        self.0.as_ptr()
    }

    #[allow(dead_code)]
    pub fn as_mut_ptr(&mut self) -> *mut ffi::X509 {
        self.0.as_mut_ptr()
    }
}

/// Container for holding a certificate or a chain of certificates
/// Parses the input X509 PEM and constructs X509 objects in the order
/// of certificate_list from RFC4346:
/// - Server Certificate
/// - One or more intermediary certificates
/// - Root CA certificate (optional)
/// Each certificate validates the one preceeding it
pub struct X509Chain {
    cert_chain: Vec<X509>,
}

impl X509Chain {
    /// Construct a chain of X509 objects from a certificate PEM file
    pub fn chain_from_pem(pem: &str) -> Result<Self> {
        let mut bio = FfiBox::new(unsafe {
            ffi::BIO_new_mem_buf(
                pem.as_ptr() as *const std::os::raw::c_void,
                pem.len() as i32,
            )
        })?;
        let mut cert_chain = Vec::new();
        loop {
            if let Ok(cert) = X509::from_bio(bio.as_mut_ptr()) {
                cert_chain.push(cert);
            } else {
                // No more entries. Check if EOF or bad PEM entries
                let rv = unsafe { ffi::BIO_eof(bio.as_mut_ptr()) };
                if rv == 0 {
                    return Err(Error::CertBadPem);
                }
                break;
            }
        }
        Ok(Self {
            cert_chain: cert_chain,
        })
    }

    /// Validate the entire certificate chain against the server certificate
    pub fn verify_chain(&self) -> Result<()> {
        let mut cert_store = FfiBox::new(unsafe { ffi::X509_STORE_new() })?;
        let mut rv = unsafe {
            ffi::X509_STORE_set_flags(
                cert_store.as_mut_ptr(),
                // Verification is done 'offline' here and as per RFC 4158
                // we might not have a root CA provisioned but only an
                // incomplete (valid) certificate chain
                ffi::X509_V_FLAG_X509_STRICT | ffi::X509_V_FLAG_PARTIAL_CHAIN,
            )
        };
        if rv != 1 {
            return Err(Error::CertChainErr);
        }
        for (pos, cert) in self.enumerate() {
            if pos > 0 {
                // Only intermediate and/or root certificates are stored for verification
                rv = unsafe { ffi::X509_STORE_add_cert(cert_store.as_mut_ptr(), cert.as_ptr()) };
                if rv != 1 {
                    return Err(Error::CertChainErr);
                }
            }
        }
        let mut cert_store_ctx = FfiBox::new(unsafe { ffi::X509_STORE_CTX_new() })?;
        rv = unsafe {
            ffi::X509_STORE_CTX_init(
                cert_store_ctx.as_mut_ptr(),
                cert_store.as_mut_ptr(),
                self.cert_chain.first().ok_or(Error::CertChainErr)?.as_ptr(),
                std::ptr::null_mut(),
            )
        };
        if rv != 1 {
            return Err(Error::CertChainErr);
        }
        unsafe {
            ffi::X509_STORE_CTX_set_verify_cb(
                cert_store_ctx.as_mut_ptr(),
                Some(ffi::x509_verify_cb),
            );
        }

        // Set the purpose explicitly to avoid invalidation via
        // any special X509.V3 features.
        unsafe {
            ffi::X509_STORE_CTX_set_purpose(cert_store_ctx.as_mut_ptr(), ffi::X509_PURPOSE_ANY)
        };
        // Issue the verification
        rv = unsafe { ffi::X509_verify_cert(cert_store_ctx.as_mut_ptr()) };
        if rv != 1 {
            return Err(Error::CertChainErr);
        }
        unsafe { ffi::X509_STORE_CTX_cleanup(cert_store_ctx.as_mut_ptr()) };

        Ok(())
    }

    /// Verify if the server certificate matches its private key.
    /// Shall fail if it is part of a trust chain
    pub fn verify(&self, pkey: Pkey) -> Result<()> {
        let rv = unsafe {
            ffi::X509_verify(
                self.cert_chain.first().ok_or(Error::CertChainErr)?.as_ptr(),
                pkey.as_ptr(),
            )
        };
        if rv != 1 {
            return Err(Error::CertChainErr);
        }
        Ok(())
    }

    /// Returns true if a bundle of certificates was provisioned
    pub fn multiple_certs(&self) -> bool {
        self.cert_chain.len() > 1
    }

    pub fn enumerate(&self) -> impl Iterator<Item = (u8, &X509)> {
        self.cert_chain
            .iter()
            .enumerate()
            .map(|(index, cert)| (index as u8, cert))
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::backend::Mechanism;

use super::config_evp_pkey_ctx;
use super::ffi;
use super::{Error, FfiBox, OpCtxState, Pkey};

/// Decryption context logic interfacing the cryptographic backend library
/// Each session can have one active decryption context at a time

pub trait DecryptCtx: Send {
    fn update(&mut self, data: &[u8]) -> Result<(), Error>;
    fn finalize(self: Box<Self>) -> Result<Vec<u8>, Error>;
    fn decrypt(self: Box<Self>, data: &[u8]) -> Result<Vec<u8>, Error>;
    fn len(&self) -> usize;
    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error>;
}

pub struct DirectDecryptCtx {
    evp_pkey_ctx: FfiBox<ffi::EVP_PKEY_CTX>,
    decrypt_len: usize,
}

impl DirectDecryptCtx {
    pub fn new(mech: &Mechanism, mut key: Pkey) -> Result<Self, Error> {
        let mut evp_pkey_ctx =
            FfiBox::new(unsafe { ffi::EVP_PKEY_CTX_new(key.as_mut_ptr(), std::ptr::null()) })
                .map_err(|_| Error::PkeyCtxInit)?;

        let rc = unsafe { ffi::EVP_PKEY_decrypt_init(evp_pkey_ctx.as_mut_ptr()) };
        if rc != 1 {
            return Err(Error::DecryptInit);
        }

        config_evp_pkey_ctx(evp_pkey_ctx.as_mut_ptr(), mech)?;

        // Asymmetric decryption outputs up to modulus size bytes
        let decrypt_len = unsafe { ffi::EVP_PKEY_size(key.as_ptr()) as usize };

        Ok(Self {
            evp_pkey_ctx,
            decrypt_len,
        })
    }
}

impl DecryptCtx for DirectDecryptCtx {
    fn decrypt(mut self: Box<Self>, encrypted_data: &[u8]) -> Result<Vec<u8>, Error> {
        let mut data = vec![0u8; self.decrypt_len];
        let mut data_len: ffi::c_size_t = self.decrypt_len as ffi::c_size_t;

        let rc = unsafe {
            ffi::EVP_PKEY_decrypt(
                self.evp_pkey_ctx.as_mut_ptr(),
                data.as_mut_ptr(),
                &mut data_len as *mut ffi::c_size_t,
                encrypted_data.as_ptr(),
                encrypted_data.len() as ffi::c_size_t,
            )
        };

        if rc != 1 {
            return Err(Error::DirectDecrypt);
        }

        // Resize to actual length
        data.resize(data_len as usize, 0);
        Ok(data.to_vec())
    }

    fn len(&self) -> usize {
        self.decrypt_len
    }

    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        if let OpCtxState::SinglepartActive = state {
            Ok(())
        } else {
            Err(Error::OperationActive)
        }
    }

    fn update(&mut self, _data: &[u8]) -> Result<(), Error> {
        Err(Error::OperationActive)
    }
    fn finalize(self: Box<Self>) -> Result<Vec<u8>, Error> {
        Err(Error::OperationActive)
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::pkcs11;

use super::ffi;
use super::mech_type_to_evp_md;
use super::Error;
use super::FfiBox;
use super::OpCtxState;

/// Message digest context logic interfacing the cryptographic backend library
/// Each session can have one active message digest context at a time

pub struct DigestCtx {
    state: OpCtxState,
    digest_len: usize,
    evp_ctx: FfiBox<ffi::EVP_MD_CTX>,
    mech_type: pkcs11::CK_MECHANISM_TYPE,
}

impl DigestCtx {
    pub fn new(mech_type: pkcs11::CK_MECHANISM_TYPE) -> Result<Self, Error> {
        let evp_md = mech_type_to_evp_md(mech_type)?;
        let mut evp_ctx = FfiBox::new(unsafe { ffi::EVP_MD_CTX_new() })?;

        let rv =
            unsafe { ffi::EVP_DigestInit_ex(evp_ctx.as_mut_ptr(), evp_md, std::ptr::null_mut()) };
        if rv == 0 {
            return Err(Error::DigestInit);
        }

        let digest_len = unsafe { ffi::EVP_MD_size(evp_md) };

        Ok(Self {
            state: OpCtxState::Initialized,
            digest_len: digest_len as usize,
            evp_ctx,
            mech_type,
        })
    }

    pub fn len(&self) -> usize {
        self.digest_len
    }

    pub fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        let ret = match (self.state, state) {
            (OpCtxState::Initialized, _) => Ok(()),
            (OpCtxState::SinglepartActive, OpCtxState::SinglepartActive) => Ok(()),
            (OpCtxState::SinglepartActive, _) => Err(Error::OperationActive),
            (OpCtxState::MultipartActive, OpCtxState::MultipartActive) => Ok(()),
            (OpCtxState::MultipartActive, OpCtxState::MultipartReady) => Ok(()),
            (OpCtxState::MultipartActive, _) => Err(Error::OperationActive),
            (OpCtxState::MultipartReady, OpCtxState::MultipartReady) => Ok(()),
            (OpCtxState::MultipartReady, _) => Err(Error::OperationActive),
        };
        if ret.is_ok() {
            self.state = state;
        }
        ret
    }

    pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
        self.enter_state(OpCtxState::MultipartActive)?;

        let rv = unsafe {
            ffi::EVP_DigestUpdate(
                self.evp_ctx.as_mut_ptr(),
                data.as_ptr() as *const std::os::raw::c_void,
                data.len() as std::os::raw::c_ulong,
            )
        };
        if rv == 0 {
            return Err(Error::DigestUpdate);
        }
        self.state = OpCtxState::MultipartActive;
        Ok(())
    }

    pub fn finalize(mut self) -> Result<Vec<u8>, Error> {
        self.enter_state(OpCtxState::MultipartReady)?;

        let mut ret = vec![0u8; ffi::EVP_MAX_MD_SIZE];
        let mut len: std::os::raw::c_uint = 0;
        let rv = unsafe {
            ffi::EVP_DigestFinal_ex(
                self.evp_ctx.as_mut_ptr(),
                ret.as_mut_ptr(),
                &mut len as *mut std::os::raw::c_uint,
            )
        };
        if rv == 0 {
            return Err(Error::DigestFinal);
        }
        ret.resize(len as usize, 0);
        Ok(ret)
    }

    pub fn digest(mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
        self.enter_state(OpCtxState::SinglepartActive)?;

        let evp_md = mech_type_to_evp_md(self.mech_type)?;
        let mut md_out = vec![0u8; self.digest_len];
        let mut md_out_len: std::os::raw::c_uint = self.digest_len as std::os::raw::c_uint;
        let rv = unsafe {
            ffi::EVP_Digest(
                data.as_ptr() as *const std::os::raw::c_void,
                data.len() as ffi::c_size_t,
                md_out.as_mut_ptr(),
                &mut md_out_len as *mut std::os::raw::c_uint,
                evp_md,
                std::ptr::null_mut(),
            )
        };
        if rv != 1 {
            return Err(Error::Digest);
        }
        Ok(md_out.to_vec())
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::backend::Mechanism;

use super::config_evp_pkey_ctx;
use super::ffi;
use super::{Error, FfiBox, OpCtxState, Pkey};

/// Encryption context logic interfacing the cryptographic backend library
/// Each session can have one active encryption context at a time

pub trait EncryptCtx: Send {
    fn update(&mut self, data: &[u8]) -> Result<(), Error>;
    fn finalize(self: Box<Self>) -> Result<Vec<u8>, Error>;
    fn encrypt(self: Box<Self>, data: &[u8]) -> Result<Vec<u8>, Error>;
    fn len(&self) -> usize;
    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error>;
}

pub struct DirectEncryptCtx {
    evp_pkey_ctx: FfiBox<ffi::EVP_PKEY_CTX>,
    encrypt_len: usize,
}

impl DirectEncryptCtx {
    pub fn new(mech: &Mechanism, mut key: Pkey) -> Result<Self, Error> {
        let mut evp_pkey_ctx =
            FfiBox::new(unsafe { ffi::EVP_PKEY_CTX_new(key.as_mut_ptr(), std::ptr::null()) })
                .map_err(|_| Error::PkeyCtxInit)?;

        let rc = unsafe { ffi::EVP_PKEY_encrypt_init(evp_pkey_ctx.as_mut_ptr()) };
        if rc != 1 {
            return Err(Error::EncryptInit);
        }
        config_evp_pkey_ctx(evp_pkey_ctx.as_mut_ptr(), mech)?;

        let encrypt_len = unsafe { ffi::EVP_PKEY_size(key.as_ptr()) as usize };

        Ok(Self {
            evp_pkey_ctx,
            encrypt_len,
        })
    }
}

impl EncryptCtx for DirectEncryptCtx {
    fn encrypt(mut self: Box<Self>, data: &[u8]) -> Result<Vec<u8>, Error> {
        let mut enc = vec![0u8; self.encrypt_len];
        let mut encrypt_len: ffi::c_size_t = self.encrypt_len as ffi::c_size_t;

        let rc = unsafe {
            ffi::EVP_PKEY_encrypt(
                self.evp_pkey_ctx.as_mut_ptr(),
                enc.as_mut_ptr(),
                &mut encrypt_len as *mut ffi::c_size_t,
                data.as_ptr(),
                data.len() as ffi::c_size_t,
            )
        };
        if rc != 1 {
            return Err(Error::Encrypt);
        }

        enc.resize(encrypt_len as usize, 0);
        Ok(enc)
    }

    fn len(&self) -> usize {
        self.encrypt_len
    }

    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        if let OpCtxState::SinglepartActive = state {
            Ok(())
        } else {
            Err(Error::OperationActive)
        }
    }

    fn update(&mut self, _data: &[u8]) -> Result<(), Error> {
        Err(Error::OperationActive)
    }
    fn finalize(self: Box<Self>) -> Result<Vec<u8>, Error> {
        Err(Error::OperationActive)
    }
}


// Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#![allow(non_snake_case)]
#![allow(non_camel_case_types)]

use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void};
pub type c_size_t = c_ulong;

/// The low level backend cryptographic interface used by this module.
/// Built on top of the backend cryptographic library (i.e. BoringSSL,
/// OpenSSL, AWS Crypto). See the library documentation for each entries
/// found here, depending on which one is linked at run-time.

/// Maximum message digest size
pub const EVP_MAX_MD_SIZE: usize = 64;

/// RSA padding types
pub const RSA_PKCS1_PADDING: c_int = 1;
pub const RSA_NO_PADDING: c_int = 3;
pub const RSA_PKCS1_PSS_PADDING: c_int = 6;

/// EVP_PKEY types
pub const EVP_PKEY_RSA: c_int = 6;
pub const EVP_PKEY_EC: c_int = 408;

/// X509 verification purpose flag
pub const X509_V_FLAG_X509_STRICT: c_ulong = 0;
pub const X509_V_FLAG_PARTIAL_CHAIN: c_ulong = 0x80000;
pub const X509_PURPOSE_ANY: c_int = 7;

/// Opaque cryptographic objects
#[repr(C)]
pub struct BIGNUM {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct BIO {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct BIO_METHOD {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct ENGINE {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EVP_MD {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EVP_MD_CTX {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EVP_PKEY {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EVP_PKEY_CTX {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct RSA {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EC_KEY {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EC_POINT {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct EC_GROUP {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct BN_CTX {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct ECDSA_SIG {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct X509 {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct X509_NAME {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct X509_STORE {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct X509_STORE_CTX {
    _ph: [u8; 0],
}
#[repr(C)]
pub struct ASN1_INTEGER {
    _ph: [u8; 0],
}

/// Transparent cryptographic objects
#[repr(C)]
pub struct cbb_buffer_st {
    buf: *mut u8,
    len: c_size_t,
    cap: c_size_t,
    can_resize: c_char,
    error: c_char,
}
#[repr(C)]
pub struct cbb_st {
    base: *mut cbb_buffer_st,
    child: *mut CBB,
    offset: c_size_t,
    pending_len_len: u8,
    pending_is_asn1: c_char,
    is_child: c_char,
}
pub type CBB = cbb_st;

pub type X509_STORE_CTX_verify_cb =
    Option<extern "C" fn(sts: c_int, ctx: *mut X509_STORE_CTX) -> c_int>;

impl CBB {
    pub fn new() -> Result<Self, super::Error> {
        let mut cbb = std::mem::MaybeUninit::uninit();
        if unsafe { CBB_init(cbb.as_mut_ptr(), 0) } != 1 {
            return Err(super::Error::GeneralError);
        }
        Ok(unsafe { cbb.assume_init() })
    }
}
impl Drop for CBB {
    fn drop(&mut self) {
        unsafe {
            CBB_cleanup(self as *mut CBB);
        }
    }
}

/// Imported cryptographic APIs
extern "C" {
    /// Private key utility functions (i.e. from PEM)
    pub fn BIO_new_mem_buf(buf: *const c_void, len: c_int) -> *mut BIO;

    pub fn BIO_s_mem() -> *const BIO_METHOD;

    pub fn BIO_new(method: *const BIO_METHOD) -> *mut BIO;

    pub fn BIO_free(bio: *mut BIO) -> c_int;

    pub fn BIO_eof(bio: *mut BIO) -> c_int;

    pub fn BIO_mem_contents(
        bio: *const BIO,
        out_contents: *const *const u8,
        out_len: *mut c_size_t,
    ) -> c_int;

    pub fn PEM_read_bio_PrivateKey(
        bp: *mut BIO,
        x: *mut *mut EVP_PKEY,
        cb: *const c_void,
        u: *mut c_void,
    ) -> *mut EVP_PKEY;

    /// Message digest constants
    pub fn EVP_sha1() -> *const EVP_MD;
    pub fn EVP_sha224() -> *const EVP_MD;
    pub fn EVP_sha256() -> *const EVP_MD;
    pub fn EVP_sha384() -> *const EVP_MD;
    pub fn EVP_sha512() -> *const EVP_MD;
    pub fn EVP_MD_size(md: *const EVP_MD) -> c_int;

    pub fn EVP_MD_CTX_new() -> *mut EVP_MD_CTX;
    pub fn EVP_MD_CTX_free(ctx: *mut EVP_MD_CTX);

    /// Message Digest functions
    pub fn EVP_DigestInit_ex(
        ctx: *mut EVP_MD_CTX,
        type_: *const EVP_MD,
        engine: *mut c_void,
    ) -> c_int;
    pub fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX, d: *const c_void, cnt: c_size_t) -> c_int;
    pub fn EVP_DigestFinal_ex(ctx: *mut EVP_MD_CTX, md: *mut c_uchar, s: *mut c_uint) -> c_int;
    pub fn EVP_Digest(
        data: *const c_void,
        len: c_size_t,
        md_out: *mut u8,
        md_out_size: *mut c_uint,
        type_: *const EVP_MD,
        impl_: *mut ENGINE,
    ) -> c_int;

    /// Sign functions
    pub fn EVP_PKEY_sign_init(ctx: *mut EVP_PKEY_CTX) -> c_int;
    pub fn EVP_PKEY_sign(
        ctx: *mut EVP_PKEY_CTX,
        sig: *mut c_uchar,
        siglen: *mut c_size_t,
        tbs: *const c_uchar,
        tbslen: c_size_t,
    ) -> c_int;
    pub fn EVP_DigestSignInit(
        ctx: *mut EVP_MD_CTX,
        pctx: *mut *mut EVP_PKEY_CTX,
        type_: *const EVP_MD,
        e: *const c_void,
        pkey: *mut EVP_PKEY,
    ) -> c_int;
    pub fn EVP_DigestSignUpdate(ctx: *mut EVP_MD_CTX, d: *const c_void, cnt: c_size_t) -> c_int;
    pub fn EVP_DigestSignFinal(
        ctx: *mut EVP_MD_CTX,
        sig: *mut c_uchar,
        siglen: *mut c_size_t,
    ) -> c_int;
    pub fn EVP_DigestSign(
        ctx: *mut EVP_MD_CTX,
        out_sig: *mut u8,
        out_sig_len: *mut c_size_t,
        data: *const u8,
        data_len: c_size_t,
    ) -> c_int;

    /// Verify functions
    pub fn EVP_PKEY_verify_init(ctx: *mut EVP_PKEY_CTX) -> c_int;
    pub fn EVP_PKEY_verify(
        ctx: *mut EVP_PKEY_CTX,
        sig: *const c_uchar,
        sig_len: c_size_t,
        digest: *const c_uchar,
        digest_len: c_size_t,
    ) -> c_int;
    pub fn EVP_DigestVerifyInit(
        ctx: *mut EVP_MD_CTX,
        pctx: *mut *mut EVP_PKEY_CTX,
        type_: *const EVP_MD,
        e: *const c_void,
        pkey: *mut EVP_PKEY,
    ) -> c_int;
    pub fn EVP_DigestVerifyUpdate(
        ctx: *mut EVP_MD_CTX,
        data: *const c_void,
        len: c_size_t,
    ) -> c_int;
    pub fn EVP_DigestVerifyFinal(
        ctx: *mut EVP_MD_CTX,
        sig: *const c_uchar,
        sig_len: c_size_t,
    ) -> c_int;
    pub fn EVP_DigestVerify(
        ctx: *mut EVP_MD_CTX,
        sig: *const c_uchar,
        sig_len: c_size_t,
        data: *const c_uchar,
        len: c_size_t,
    ) -> c_int;

    /// Decryption functions
    pub fn EVP_PKEY_decrypt_init(ctx: *mut EVP_PKEY_CTX) -> c_int;
    pub fn EVP_PKEY_decrypt(
        ctx: *mut EVP_PKEY_CTX,
        out: *mut u8,
        out_len: *mut c_size_t,
        in_: *const u8,
        in_len: c_size_t,
    ) -> c_int;

    /// Encryption functions
    pub fn EVP_PKEY_encrypt_init(ctx: *mut EVP_PKEY_CTX) -> c_int;
    pub fn EVP_PKEY_encrypt(
        ctx: *mut EVP_PKEY_CTX,
        out: *mut u8,
        out_len: *mut c_size_t,
        in_: *const u8,
        in_len: c_size_t,
    ) -> c_int;

    /// Generic EVP PKEY container functions
    pub fn EVP_PKEY_free(key: *mut EVP_PKEY);
    pub fn EVP_PKEY_bits(key: *const EVP_PKEY) -> c_int;
    pub fn EVP_PKEY_size(key: *const EVP_PKEY) -> c_int;
    pub fn EVP_PKEY_id(key: *const EVP_PKEY) -> c_int;

    /// Generic EVP PKEY context functions
    pub fn EVP_PKEY_CTX_new(pkey: *mut EVP_PKEY, e: *const c_void) -> *mut EVP_PKEY_CTX;
    pub fn EVP_PKEY_CTX_free(ctx: *mut EVP_PKEY_CTX);
    pub fn EVP_PKEY_CTX_get0_pkey(ctx: *const EVP_PKEY_CTX) -> *const EVP_PKEY;
    pub fn EVP_PKEY_CTX_set_signature_md(ctx: *mut EVP_PKEY_CTX, md: *const EVP_MD) -> c_int;

    /// RSA key specific functions
    pub fn EVP_PKEY_get0_RSA(pkey: *const EVP_PKEY) -> *const RSA;
    // Note: OpenSSL doesn't export these, implementing them as a macros instead.
    pub fn EVP_PKEY_CTX_set_rsa_padding(ctx: *mut EVP_PKEY_CTX, pad: c_int) -> c_int;
    pub fn EVP_PKEY_CTX_set_rsa_mgf1_md(ctx: *mut EVP_PKEY_CTX, md: *const EVP_MD) -> c_int;
    pub fn EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx: *mut EVP_PKEY_CTX, len: c_int) -> c_int;
    pub fn RSA_get0_key(
        rsa: *const RSA,
        out_n: *mut *const BIGNUM,
        out_e: *mut *const BIGNUM,
        out_d: *mut *const BIGNUM,
    );
    pub fn RSA_free(rsa: *mut RSA);

    /// EC key specific functions
    pub fn EVP_PKEY_get0_EC_KEY(pkey: *const EVP_PKEY) -> *mut EC_KEY;
    pub fn EC_KEY_key2buf(
        ec: *const EC_KEY,
        form: c_int,
        out_buf: *mut *mut c_uchar,
        ctx: *mut BN_CTX,
    ) -> c_size_t;
    pub fn EC_KEY_get0_group(key: *const EC_KEY) -> *const EC_GROUP;
    pub fn EC_KEY_marshal_curve_name(cbb: *mut CBB, group: *const EC_GROUP) -> c_int;
    pub fn EC_KEY_get_conv_form(ec: *const EC_KEY) -> c_int; // point_conversion_form_t enum
    pub fn EC_KEY_free(ec: *mut EC_KEY);
    pub fn ECDSA_SIG_get0(
        sig: *const ECDSA_SIG,
        out_r: *mut *const BIGNUM,
        our_s: *mut *const BIGNUM,
    );
    pub fn d2i_ECDSA_SIG(
        sig: *mut *mut ECDSA_SIG,
        pp: *mut *const c_uchar,
        len: c_long,
    ) -> *mut ECDSA_SIG;
    pub fn i2d_ECDSA_SIG(sig: *const ECDSA_SIG, outp: *mut *mut u8) -> c_int;
    pub fn ECDSA_SIG_set0(sig: *mut ECDSA_SIG, r: *mut BIGNUM, s: *mut BIGNUM) -> c_int;
    pub fn ECDSA_SIG_new() -> *mut ECDSA_SIG;
    pub fn ECDSA_SIG_free(sig: *mut ECDSA_SIG);
    pub fn ECDSA_SIG_max_len(order_len: c_size_t) -> c_size_t;

    pub fn EC_POINT_free(point: *mut EC_POINT);
    pub fn EC_GROUP_free(group: *mut EC_GROUP);

    /// CBB helper functions
    pub fn CBB_init(cbb: *mut CBB, initial_capacity: c_size_t) -> c_int;
    pub fn CBB_cleanup(cbb: *mut CBB);
    pub fn CBB_data(cbb: *const CBB) -> *const u8;
    pub fn CBB_len(cbb: *const CBB) -> c_size_t;

    /// BIGNUM helper functions
    pub fn BN_num_bytes(bn: *const BIGNUM) -> c_uint;
    pub fn BN_bn2bin(bn: *const BIGNUM, to: *mut c_char) -> c_int;
    pub fn BN_bn2bin_padded(out: *mut u8, len: c_size_t, in_: *const BIGNUM) -> c_int;
    pub fn BN_bin2bn(in_: *const u8, len: c_size_t, ret: *mut BIGNUM) -> *mut BIGNUM;
    pub fn BN_CTX_new() -> *mut BN_CTX;
    pub fn BN_CTX_free(ctx: *mut BN_CTX);
    pub fn BN_free(bn: *mut BIGNUM);

    /// Imported X509 certificate functions
    pub fn PEM_read_bio_X509(
        bp: *mut BIO,
        x: *mut *mut X509,
        c: *const c_void,
        u: *mut c_void,
    ) -> *mut X509;
    pub fn X509_get_subject_name(x: *const X509) -> *mut X509_NAME;
    pub fn X509_get_issuer_name(x: *const X509) -> *mut X509_NAME;
    pub fn X509_NAME_get0_der(
        nm: *mut X509_NAME,
        pder: *const *const c_uchar,
        pderlen: *mut c_size_t,
    ) -> c_int;
    pub fn X509_get0_serialNumber(x: *const X509) -> *const ASN1_INTEGER;
    pub fn ASN1_INTEGER_to_BN(ai: *const ASN1_INTEGER, bn: *mut BIGNUM) -> *mut BIGNUM;
    pub fn i2d_X509_bio(bp: *mut BIO, x: *const X509) -> c_int;
    pub fn X509_free(cert: *mut X509);
    pub fn X509_STORE_new() -> *mut X509_STORE;
    pub fn X509_STORE_free(cert: *mut X509_STORE);
    pub fn X509_STORE_CTX_new() -> *mut X509_STORE_CTX;
    pub fn X509_STORE_CTX_init(
        ctx: *mut X509_STORE_CTX,
        store: *mut X509_STORE,
        x509: *const X509,
        chain: *mut c_void, /* STACK_OF(X509) */
    ) -> c_int;
    pub fn X509_STORE_CTX_cleanup(ctx: *mut X509_STORE_CTX);
    pub fn X509_STORE_CTX_free(store: *mut X509_STORE_CTX);
    pub fn X509_STORE_add_cert(store: *mut X509_STORE, x509: *const X509) -> c_int;
    pub fn X509_STORE_set_flags(store: *mut X509_STORE, flags: c_ulong) -> c_int;
    pub fn X509_STORE_CTX_set_purpose(ctx: *mut X509_STORE_CTX, purpose: c_int) -> c_int;
    pub fn X509_STORE_CTX_set_verify_cb(ctx: *mut X509_STORE_CTX, cb: X509_STORE_CTX_verify_cb);
    pub fn X509_verify_cert(ctx: *mut X509_STORE_CTX) -> c_int;
    pub fn X509_verify(cert: *const X509, pkey: *const EVP_PKEY) -> c_int;
    pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int;
    pub fn X509_STORE_CTX_get_error_depth(ctx: *mut X509_STORE_CTX) -> c_int;
}

/// Callback for handling x509 verification issues or debugging
pub extern "C" fn x509_verify_cb(sts: c_int, ctx: *mut X509_STORE_CTX) -> c_int {
    let ret_sts = sts;
    if ret_sts == 0 {
        let err = unsafe { X509_STORE_CTX_get_error(ctx) };
        let err_depth = unsafe { X509_STORE_CTX_get_error_depth(ctx) };

        warn!("X509 verification error: err {} depth {}", err, err_depth);
    }
    // Resume crypto backend behavior
    ret_sts
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::bignum_to_vec;
use super::ffi;
use super::FfiBox;
use super::{Error, Result};

#[derive(Eq, PartialEq)]
pub enum KeyAlgo {
    Ec,
    Rsa,
}

/// Logic for managing cryptographic keys.
/// These keys are constructed from decrypted PEM files provided during
/// provisioning. The Pkey is a wrapper over the generic EVP_PKEY
/// presented in the backend cryptographic library.
/// We currently support RSA and ECDSA key types of various sizes.
pub struct Pkey(FfiBox<ffi::EVP_PKEY>);

impl Pkey {
    /// Construct a private key from a private key PEM file
    pub fn from_private_pem(pem: &str) -> Result<Self> {
        let mut bio = FfiBox::new(unsafe {
            ffi::BIO_new_mem_buf(
                pem.as_ptr() as *const std::os::raw::c_void,
                pem.len() as i32,
            )
        })?;

        let pkey = FfiBox::new(unsafe {
            ffi::PEM_read_bio_PrivateKey(
                bio.as_mut_ptr(),
                std::ptr::null_mut(),
                std::ptr::null(),
                std::ptr::null_mut(),
            )
        })?;

        Ok(Self(pkey))
    }

    /// Get the key type of this key object
    pub fn algo(&self) -> Result<KeyAlgo> {
        match unsafe { ffi::EVP_PKEY_id(self.0.as_ptr()) } {
            ffi::EVP_PKEY_RSA => Ok(KeyAlgo::Rsa),
            ffi::EVP_PKEY_EC => Ok(KeyAlgo::Ec),
            _ => Err(Error::UnknownKeyType),
        }
    }

    /// Get the key size of this key object in bits. Caller should
    /// compute this value based on context and key type
    pub fn num_bits(&self) -> Result<usize> {
        let rv = unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) };
        if rv <= 0 {
            return Err(Error::GeneralError);
        }
        Ok(rv as usize)
    }

    /// Get the length (in bytes) for a signature perfomed by this Pkey.
    ///
    /// Note: signatures may have different formats. The value returned here concerns the
    ///       signature format yielded by the underlying libcrypto signing functions
    ///       (e.g. EVP_DigestSign*(), EVP_PKEY_sign(), etc).
    ///       This is important because the Cryptoki / PKCS#11 ABI uses a different format
    ///       for ECDSA signatures. Whereas libcryto DER-encodes ECDSA signatures, Cryptoki
    ///       uses a different, raw [R, S] format.
    pub fn sig_len(&self) -> Result<usize> {
        let rv = unsafe { ffi::EVP_PKEY_size(self.as_ptr()) };
        if rv <= 0 {
            return Err(Error::GeneralError);
        }
        Ok(rv as usize)
    }

    /// Get the length (in bytes) for a signature perfomed by this Pkey, when represented in
    /// the Cryptoki / PKCS#11 signature format.
    pub fn sig_len_ck(&self) -> Result<usize> {
        match self.algo()? {
            KeyAlgo::Ec => Ok((self.num_bits()? + 7) / 8 * 2),
            KeyAlgo::Rsa => self.sig_len(),
        }
    }

    /// Get the key modulus (applicable if the object wraps an RSA key)
    pub fn rsa_modulus(&self) -> Result<Vec<u8>> {
        let rsa = self.rsa_key()?;
        let mut bn_ptr: *const ffi::BIGNUM = std::ptr::null();
        unsafe {
            // Note: RSA_get0_key just fetches / copies some pointers from the inner RSA struct,
            // so the BIGNUM structs are still owned by RSA. We are not taking ownership here.
            ffi::RSA_get0_key(
                rsa,
                &mut bn_ptr as *mut *const ffi::BIGNUM,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
            );
        }
        // RSA_get0_key will always set the output params, so there's no need to check for
        // NULL on return. Also, bignum_to_vec already does param validation.
        bignum_to_vec(bn_ptr)
    }

    /// Get the key public exponent (applicable if the object wraps an RSA key)
    pub fn rsa_public_exponent(&self) -> Result<Vec<u8>> {
        let rsa = self.rsa_key()?;
        let mut bn_ptr: *const ffi::BIGNUM = std::ptr::null();
        unsafe {
            // Note: RSA_get0_key just fetches / copies some pointers from the inner RSA struct,
            // so the BIGNUM structs are still owned by RSA. We are not taking ownership here.
            ffi::RSA_get0_key(
                rsa,
                std::ptr::null_mut(),
                &mut bn_ptr as *mut *const ffi::BIGNUM,
                std::ptr::null_mut(),
            );
        }
        // RSA_get0_key will always set the output params, so there's no need to check for
        // NULL on return. Also, bignum_to_vec already does param validation.
        bignum_to_vec(bn_ptr)
    }

    /// Get the DER-encoding of the DER-encoding of ANSI X9.62 ECPoint value Q
    /// (applicable if the object wraps an EC key)
    pub fn ec_point_q_x962(&self) -> Result<Vec<u8>> {
        let ec = self.ec_key()?;
        let mut ctx = FfiBox::new(unsafe { ffi::BN_CTX_new() })?;
        let mut out_ptr: *mut std::os::raw::c_uchar = std::ptr::null_mut();
        let len = unsafe {
            ffi::EC_KEY_key2buf(
                ec,
                ffi::EC_KEY_get_conv_form(ec),
                &mut out_ptr,
                ctx.as_mut_ptr(),
            )
        };
        if len > 0 {
            let vec = unsafe { std::slice::from_raw_parts(out_ptr, len as usize).to_vec() };
            return Ok(vec);
        }
        Err(Error::GeneralError)
    }

    /// Get the DER-encoding of the DER-encoding of an ANSI X9.62 parameters value
    /// (applicable if the object wraps an EC key)
    pub fn ec_params_x962(&self) -> Result<Vec<u8>> {
        let ec = self.ec_key()?;
        let group = unsafe { ffi::EC_KEY_get0_group(ec) };
        if group.is_null() {
            return Err(Error::BadKeyType);
        }
        // Get DER encoding of the curve
        let mut cbb = ffi::CBB::new()?;
        let rc = unsafe { ffi::EC_KEY_marshal_curve_name(&mut cbb as *mut ffi::CBB, group) };
        if rc != 1 {
            return Err(Error::GeneralError);
        }
        let der_len = unsafe { ffi::CBB_len(&cbb as *const ffi::CBB) as usize };
        let der = unsafe { ffi::CBB_data(&cbb as *const ffi::CBB) };
        let vec = unsafe { std::slice::from_raw_parts(der, der_len).to_vec() };

        Ok(vec)
    }

    pub fn as_ptr(&self) -> *const ffi::EVP_PKEY {
        self.0.as_ptr()
    }

    pub fn as_mut_ptr(&mut self) -> *mut ffi::EVP_PKEY {
        self.0.as_mut_ptr()
    }

    #[allow(dead_code)]
    pub fn into_raw(self) -> *mut ffi::EVP_PKEY {
        self.0.into_raw()
    }

    /// Get a reference to the wrapped EC key. Ownership is not taken.
    /// (applicable if the object wraps an EC key)
    fn ec_key(&self) -> Result<*const ffi::EC_KEY> {
        let ec = unsafe { ffi::EVP_PKEY_get0_EC_KEY(self.as_ptr()) };
        if ec.is_null() {
            return Err(Error::BadKeyType);
        }
        Ok(ec)
    }

    /// Get a reference to the wrapped RSA key. Ownership is not taken.
    /// (applicable if the object wraps an RSA key)
    fn rsa_key(&self) -> Result<*const ffi::RSA> {
        let rsa = unsafe { ffi::EVP_PKEY_get0_RSA(self.as_ptr()) };
        if rsa.is_null() {
            return Err(Error::BadKeyType);
        }
        Ok(rsa)
    }
}


// Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::pkcs11;

pub mod cert;
pub mod decrypt;
pub mod digest;
pub mod encrypt;
mod ffi;
pub mod key;
pub mod sign;
pub mod verify;

pub use cert::{CertCategory, X509Chain, X509};
pub use decrypt::{DecryptCtx, DirectDecryptCtx};
pub use digest::DigestCtx;
pub use encrypt::{DirectEncryptCtx, EncryptCtx};
pub use key::{KeyAlgo, Pkey};
pub use sign::{DigestSignCtx, DirectSignCtx, SignCtx};
pub use verify::{DigestVerifyCtx, DirectVerifyCtx, VerifyCtx};

use crate::backend::Mechanism;

/// Utility RAII wrappers for managing the cryptographic library interface
/// logic and also objects allocated through the backend cryptographic library

#[derive(Clone, Copy, Debug)]
pub enum Error {
    BadFlow,
    BadMech,
    BadMgf,
    BadSigFormat,
    BignumAlloc,
    DataMissing,
    Digest,
    DigestInit,
    DigestFinal,
    DigestSign,
    DigestSignFinal,
    DigestSignUpdate,
    DigestUpdate,
    DirectSign,
    MdCtxInit,
    GeneralError,
    OperationActive,
    PkeyCtxInit,
    PkeyCtxCtl,
    SignInit,
    VerifyInit,
    DigestVerify,
    DigestVerifyFinal,
    DigestVerifyUpdate,
    DirectVerify,
    DecryptInit,
    DirectDecrypt,
    EncryptInit,
    Encrypt,
    BadKeyType,
    UnknownKeyType,
    CertBadPem,
    CertName,
    CertIssuer,
    CertSerialNo,
    CertDerEncode,
    CertChainErr,
    CertChainInvalid,
}
pub type Result<T> = std::result::Result<T, Error>;

trait FfiFree {
    fn free(&mut self);
}

impl FfiFree for ffi::BIO {
    fn free(&mut self) {
        trace!("calling BIO_free");
        unsafe {
            ffi::BIO_free(self as *mut ffi::BIO);
        }
    }
}

impl FfiFree for ffi::EVP_MD_CTX {
    fn free(&mut self) {
        unsafe {
            ffi::EVP_MD_CTX_free(self as *mut ffi::EVP_MD_CTX);
        }
    }
}

impl FfiFree for ffi::EVP_PKEY {
    fn free(&mut self) {
        trace!("calling EVP_PKEY_free");
        unsafe {
            ffi::EVP_PKEY_free(self as *mut ffi::EVP_PKEY);
        }
    }
}

impl FfiFree for ffi::EVP_PKEY_CTX {
    fn free(&mut self) {
        unsafe {
            ffi::EVP_PKEY_CTX_free(self as *mut ffi::EVP_PKEY_CTX);
        }
    }
}

impl FfiFree for ffi::RSA {
    fn free(&mut self) {
        unsafe {
            ffi::RSA_free(self as *mut ffi::RSA);
        }
    }
}

impl FfiFree for ffi::EC_KEY {
    fn free(&mut self) {
        unsafe {
            ffi::EC_KEY_free(self as *mut ffi::EC_KEY);
        }
    }
}

impl FfiFree for ffi::BN_CTX {
    fn free(&mut self) {
        unsafe {
            ffi::BN_CTX_free(self as *mut ffi::BN_CTX);
        }
    }
}

impl FfiFree for ffi::ECDSA_SIG {
    fn free(&mut self) {
        unsafe {
            trace!("calling ECDSA_SIG_free");
            ffi::ECDSA_SIG_free(self as *mut ffi::ECDSA_SIG);
        }
    }
}

impl FfiFree for ffi::BIGNUM {
    fn free(&mut self) {
        unsafe {
            ffi::BN_free(self as *mut ffi::BIGNUM);
        }
    }
}

impl FfiFree for ffi::EC_GROUP {
    fn free(&mut self) {
        unsafe {
            ffi::EC_GROUP_free(self as *mut ffi::EC_GROUP);
        }
    }
}

impl FfiFree for ffi::EC_POINT {
    fn free(&mut self) {
        unsafe {
            ffi::EC_POINT_free(self as *mut ffi::EC_POINT);
        }
    }
}

impl FfiFree for ffi::X509 {
    fn free(&mut self) {
        unsafe {
            trace!("calling X509_free");
            ffi::X509_free(self as *mut ffi::X509);
        }
    }
}

impl FfiFree for ffi::X509_STORE {
    fn free(&mut self) {
        unsafe {
            trace!("calling X509_STORE_free");
            ffi::X509_STORE_free(self as *mut ffi::X509_STORE);
        }
    }
}

impl FfiFree for ffi::X509_STORE_CTX {
    fn free(&mut self) {
        unsafe {
            trace!("calling X509_STORE_CTX_free");
            ffi::X509_STORE_CTX_free(self as *mut ffi::X509_STORE_CTX);
        }
    }
}

struct FfiBox<T: FfiFree> {
    ptr: *mut T,
}
unsafe impl std::marker::Send for FfiBox<ffi::EVP_MD_CTX> {}
unsafe impl std::marker::Send for FfiBox<ffi::EVP_PKEY> {}
unsafe impl std::marker::Send for FfiBox<ffi::EVP_PKEY_CTX> {}
unsafe impl std::marker::Send for FfiBox<ffi::X509> {}
unsafe impl std::marker::Send for FfiBox<ffi::X509_STORE> {}
unsafe impl std::marker::Send for FfiBox<ffi::X509_STORE_CTX> {}

impl<T> FfiBox<T>
where
    T: FfiFree,
{
    pub fn new(ptr: *mut T) -> Result<Self> {
        if ptr.is_null() {
            Err(Error::GeneralError)
        } else {
            Ok(Self { ptr })
        }
    }

    pub fn as_ptr(&self) -> *const T {
        self.ptr
    }

    pub fn as_mut_ptr(&mut self) -> *mut T {
        self.ptr
    }

    pub fn into_raw(mut self) -> *mut T {
        let ret = self.ptr;
        self.ptr = std::ptr::null_mut();
        ret
    }
}

impl<T> Drop for FfiBox<T>
where
    T: FfiFree,
{
    fn drop(&mut self) {
        if !self.ptr.is_null() {
            unsafe { (*self.ptr).free() }
        }
    }
}

/// Operation Context state. An operation context state is stored
/// in order to avoid misbehaving applications calling the incorrect
/// cryptographic intercaces (i.e. C_SignInit() -> C_Sign() -> C_SignUpdate())
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum OpCtxState {
    Initialized,
    SinglepartActive,
    MultipartActive,
    MultipartReady,
}

/// Convert a byte slice to a BIGNUM
fn bytes_to_bignum(src: &[u8]) -> Result<FfiBox<ffi::BIGNUM>> {
    FfiBox::new(unsafe {
        ffi::BN_bin2bn(
            src.as_ptr(),
            src.len() as ffi::c_size_t,
            std::ptr::null_mut(),
        )
    })
}

/// Convert an BIGNUM to a byte vector
fn bignum_to_vec(bn: *const ffi::BIGNUM) -> Result<Vec<u8>> {
    if bn.is_null() {
        return Err(Error::GeneralError);
    }
    let len = unsafe { ffi::BN_num_bytes(bn) };
    if len == 0 {
        return Err(Error::GeneralError);
    }
    let mut ret = vec![0u8; len as usize];

    let written = unsafe { ffi::BN_bn2bin(bn, ret.as_mut_ptr() as *mut _) };
    if ret.len() != written as usize {
        return Err(Error::GeneralError);
    }
    Ok(ret)
}

/// Convert an PKCS#11 message digest type to an EVP MD type
fn mech_type_to_evp_md(mech_type: pkcs11::CK_MECHANISM_TYPE) -> Result<*const ffi::EVP_MD> {
    match mech_type {
        pkcs11::CKM_SHA_1
        | pkcs11::CKM_SHA1_RSA_PKCS
        | pkcs11::CKM_SHA1_RSA_PKCS_PSS
        | pkcs11::CKM_ECDSA_SHA1 => Ok(unsafe { ffi::EVP_sha1() }),
        pkcs11::CKM_SHA224
        | pkcs11::CKM_SHA224_RSA_PKCS
        | pkcs11::CKM_SHA224_RSA_PKCS_PSS
        | pkcs11::CKM_ECDSA_SHA224 => Ok(unsafe { ffi::EVP_sha224() }),
        pkcs11::CKM_SHA256
        | pkcs11::CKM_SHA256_RSA_PKCS
        | pkcs11::CKM_SHA256_RSA_PKCS_PSS
        | pkcs11::CKM_ECDSA_SHA256 => Ok(unsafe { ffi::EVP_sha256() }),
        pkcs11::CKM_SHA384
        | pkcs11::CKM_SHA384_RSA_PKCS
        | pkcs11::CKM_SHA384_RSA_PKCS_PSS
        | pkcs11::CKM_ECDSA_SHA384 => Ok(unsafe { ffi::EVP_sha384() }),
        pkcs11::CKM_SHA512
        | pkcs11::CKM_SHA512_RSA_PKCS
        | pkcs11::CKM_SHA512_RSA_PKCS_PSS
        | pkcs11::CKM_ECDSA_SHA512 => Ok(unsafe { ffi::EVP_sha512() }),
        _ => Err(Error::BadMech),
    }
}

/// Convert an PKCS#11 MGF type to an EVP MD type
fn mgf_to_evp_md(mgf: pkcs11::CK_RSA_PKCS_MGF_TYPE) -> Result<*const ffi::EVP_MD> {
    match mgf {
        pkcs11::CKG_MGF1_SHA1 => Ok(unsafe { ffi::EVP_sha1() }),
        pkcs11::CKG_MGF1_SHA224 => Ok(unsafe { ffi::EVP_sha224() }),
        pkcs11::CKG_MGF1_SHA256 => Ok(unsafe { ffi::EVP_sha256() }),
        pkcs11::CKG_MGF1_SHA384 => Ok(unsafe { ffi::EVP_sha384() }),
        pkcs11::CKG_MGF1_SHA512 => Ok(unsafe { ffi::EVP_sha512() }),
        _ => Err(Error::BadMgf),
    }
}

/// Convert an ECDSA signature from the Cryptoki [R,S] format to DER.
/// R or S are 0-padded so that each of them takes exactly half of the signature.
fn ecdsa_sig_ckrs_to_der(ckrs: &[u8]) -> Result<Vec<u8>> {
    let mut bn_r = bytes_to_bignum(&ckrs[..ckrs.len() / 2])?;
    let mut bn_s = bytes_to_bignum(&ckrs[ckrs.len() / 2..])?;

    let mut ec_sig = FfiBox::new(unsafe { ffi::ECDSA_SIG_new() })?;

    let rv =
        unsafe { ffi::ECDSA_SIG_set0(ec_sig.as_mut_ptr(), bn_r.as_mut_ptr(), bn_s.as_mut_ptr()) };
    if rv != 1 {
        return Err(Error::GeneralError);
    }
    // ec_sign now owns bn_r and bn_s; relinquish ownership.
    bn_r.into_raw();
    bn_s.into_raw();

    // i2d_ECDSA_SIG can either:
    // - fill in a caller-provided buffer (here, via *der_sig_ptr); or
    // - return a new buffer into the caller's ownership.
    // We are going with the former here, since we don't want to deal with OPENSSL_free().
    let der_sig_max_len = unsafe { ffi::ECDSA_SIG_max_len(ckrs.len() as u64 / 2) };
    let mut der_sig = vec![0u8; der_sig_max_len as usize];
    let mut der_sig_ptr = der_sig.as_mut_ptr();

    let der_sig_len = unsafe { ffi::i2d_ECDSA_SIG(ec_sig.as_ptr(), &mut der_sig_ptr) };
    if der_sig_len < 0 {
        return Err(Error::GeneralError);
    }
    der_sig.resize(der_sig_len as usize, 0);

    Ok(der_sig)
}

/// Convert a DER-encoded ECDSA signature to the Cryptoki [R,S] format, where either R
/// or S is 0-padded, so that they are exactly the same length, and:
/// R = sig[0 .. sig_len/2] and S = sig[sig_len/2 .. sig_len]
fn ecdsa_sig_der_to_ckrs(sig_der: &[u8]) -> Result<Vec<u8>> {
    let mut sig_der_ptr = sig_der.as_ptr();
    let ec_sig = FfiBox::new(unsafe {
        ffi::d2i_ECDSA_SIG(
            std::ptr::null_mut(),
            &mut sig_der_ptr,
            sig_der.len() as std::os::raw::c_long,
        )
    })?;

    let mut bn_r: *const ffi::BIGNUM = std::ptr::null();
    let mut bn_s: *const ffi::BIGNUM = std::ptr::null();
    unsafe { ffi::ECDSA_SIG_get0(ec_sig.as_ptr(), &mut bn_r, &mut bn_s) };
    if bn_r.is_null() || bn_s.is_null() {
        return Err(Error::GeneralError);
    }

    let r_len = unsafe { ffi::BN_num_bytes(bn_r) } as usize;
    let s_len = unsafe { ffi::BN_num_bytes(bn_s) } as usize;
    let order_len = if r_len >= s_len { r_len } else { s_len };

    let mut ret = vec![0u8; 2 * order_len];
    let rv = unsafe { ffi::BN_bn2bin_padded(ret.as_mut_ptr(), order_len as ffi::c_size_t, bn_r) };
    if rv != 1 {
        return Err(Error::GeneralError);
    }
    let rv = unsafe {
        ffi::BN_bn2bin_padded(
            ret[order_len..].as_mut_ptr(),
            order_len as ffi::c_size_t,
            bn_s,
        )
    };
    if rv != 1 {
        return Err(Error::GeneralError);
    }

    Ok(ret)
}

/// Configure the current EVP PKEY prior to a signing or a verification operation.
/// Depending on the RSA mechanism used, padding might differ.
/// ECDSA does not require any special handling
fn config_evp_pkey_ctx(pctx: *mut ffi::EVP_PKEY_CTX, mech: &Mechanism) -> Result<()> {
    let padding = match mech {
        Mechanism::Digest(_) => return Err(Error::BadMech),
        Mechanism::RsaX509 => ffi::RSA_NO_PADDING,
        Mechanism::RsaPkcs(_) => ffi::RSA_PKCS1_PADDING,
        Mechanism::RsaPkcsPss(_, _) => ffi::RSA_PKCS1_PSS_PADDING,
        Mechanism::Ecdsa(_) => return Ok(()),
    };
    let rc = unsafe { ffi::EVP_PKEY_CTX_set_rsa_padding(pctx, padding) };
    if rc != 1 {
        return Err(Error::PkeyCtxCtl);
    }

    if let Mechanism::RsaPkcsPss(_, Some(params)) = mech {
        let evp_md_hash = mech_type_to_evp_md(params.hashAlg)?;
        let rc = unsafe { ffi::EVP_PKEY_CTX_set_signature_md(pctx, evp_md_hash) };
        if rc != 1 {
            return Err(Error::PkeyCtxCtl);
        }
        let evp_md = mgf_to_evp_md(params.mgf)?;
        let rc = unsafe { ffi::EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, evp_md) };
        if rc != 1 {
            return Err(Error::PkeyCtxCtl);
        }

        let rc = unsafe {
            ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, params.sLen as std::os::raw::c_int)
        };
        if rc != 1 {
            return Err(Error::PkeyCtxCtl);
        }
    }

    Ok(())
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::backend::Mechanism;

use super::ffi;
use super::key::KeyAlgo;
use super::{config_evp_pkey_ctx, ecdsa_sig_der_to_ckrs, mech_type_to_evp_md};
use super::{Error, FfiBox, OpCtxState, Pkey};

/// Signing context logic interfacing the cryptographic backend library
/// Each session can have one active signing context at a time

pub trait SignCtx: Send {
    fn update(&mut self, data: &[u8]) -> Result<(), Error>;
    fn finalize(self: Box<Self>) -> Result<Vec<u8>, Error>;
    fn sign(self: Box<Self>, data: &[u8]) -> Result<Vec<u8>, Error>;
    fn sig_len_ck(&self) -> usize;
    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error>;
}

pub struct DigestSignCtx {
    state: OpCtxState,
    evp_md_ctx: FfiBox<ffi::EVP_MD_CTX>,
    algo: KeyAlgo,
    sig_len: usize,
    sig_len_ck: usize,
}

pub struct DirectSignCtx {
    evp_pkey_ctx: FfiBox<ffi::EVP_PKEY_CTX>,
    algo: KeyAlgo,
    sig_len: usize,
    sig_len_ck: usize,
}

impl DigestSignCtx {
    pub fn new(mech: &Mechanism, mut pkey: Pkey) -> Result<Self, Error> {
        let mut evp_pkey_ctx: *mut ffi::EVP_PKEY_CTX = std::ptr::null_mut();
        let mut evp_md_ctx =
            FfiBox::new(unsafe { ffi::EVP_MD_CTX_new() }).map_err(|_| Error::MdCtxInit)?;

        let evp_md = mech_type_to_evp_md(mech.ck_type())?;

        let rc = unsafe {
            ffi::EVP_DigestSignInit(
                evp_md_ctx.as_mut_ptr(),
                &mut evp_pkey_ctx as *mut *mut ffi::EVP_PKEY_CTX,
                evp_md,
                std::ptr::null(),
                pkey.as_mut_ptr(),
            )
        };
        if rc != 1 {
            return Err(Error::SignInit);
        }

        config_evp_pkey_ctx(evp_pkey_ctx, mech)?;

        Ok(Self {
            state: OpCtxState::Initialized,
            evp_md_ctx,
            algo: pkey.algo()?,
            sig_len: pkey.sig_len()?,
            sig_len_ck: pkey.sig_len_ck()?,
        })
    }
}

impl SignCtx for DigestSignCtx {
    fn update(&mut self, data: &[u8]) -> Result<(), Error> {
        self.enter_state(OpCtxState::MultipartActive)?;
        let rc = unsafe {
            ffi::EVP_DigestSignUpdate(
                self.evp_md_ctx.as_mut_ptr(),
                data.as_ptr() as *const std::os::raw::c_void,
                data.len() as ffi::c_size_t,
            )
        };
        if rc != 1 {
            return Err(Error::DigestSignUpdate);
        }
        Ok(())
    }

    fn finalize(mut self: Box<Self>) -> Result<Vec<u8>, Error> {
        self.enter_state(OpCtxState::MultipartReady)?;
        let mut sig = vec![0u8; self.sig_len];
        let mut sig_len: ffi::c_size_t = self.sig_len as ffi::c_size_t;

        let rc = unsafe {
            ffi::EVP_DigestSignFinal(
                self.evp_md_ctx.as_mut_ptr(),
                sig.as_mut_ptr(),
                &mut sig_len as *mut ffi::c_size_t,
            )
        };
        if rc != 1 {
            return Err(Error::DigestSignFinal);
        }

        match self.algo {
            KeyAlgo::Ec => ecdsa_sig_der_to_ckrs(sig.as_slice()),
            KeyAlgo::Rsa => Ok(sig),
        }
    }

    fn sign(mut self: Box<Self>, data: &[u8]) -> Result<Vec<u8>, Error> {
        self.enter_state(OpCtxState::SinglepartActive)?;
        let mut sig = vec![0u8; self.sig_len];
        let mut sig_len: ffi::c_size_t = self.sig_len as ffi::c_size_t;

        let rc = unsafe {
            ffi::EVP_DigestSign(
                self.evp_md_ctx.as_mut_ptr(),
                sig.as_mut_ptr(),
                &mut sig_len as *mut ffi::c_size_t,
                data.as_ptr(),
                data.len() as ffi::c_size_t,
            )
        };
        if rc != 1 {
            return Err(Error::DigestSign);
        }

        match self.algo {
            KeyAlgo::Ec => ecdsa_sig_der_to_ckrs(sig.as_slice()),
            KeyAlgo::Rsa => Ok(sig),
        }
    }

    fn sig_len_ck(&self) -> usize {
        self.sig_len_ck
    }

    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        let ret = match (self.state, state) {
            (OpCtxState::Initialized, _) => Ok(()),
            (OpCtxState::SinglepartActive, OpCtxState::SinglepartActive) => Ok(()),
            (OpCtxState::SinglepartActive, _) => Err(Error::OperationActive),
            (OpCtxState::MultipartActive, OpCtxState::MultipartActive) => Ok(()),
            (OpCtxState::MultipartActive, OpCtxState::MultipartReady) => Ok(()),
            (OpCtxState::MultipartActive, _) => Err(Error::OperationActive),
            (OpCtxState::MultipartReady, OpCtxState::MultipartReady) => Ok(()),
            (OpCtxState::MultipartReady, _) => Err(Error::OperationActive),
        };
        if ret.is_ok() {
            self.state = state;
        }
        ret
    }
}

impl DirectSignCtx {
    pub fn new(mech: &Mechanism, mut pkey: Pkey) -> Result<Self, Error> {
        let mut evp_pkey_ctx =
            FfiBox::new(unsafe { ffi::EVP_PKEY_CTX_new(pkey.as_mut_ptr(), std::ptr::null()) })
                .map_err(|_| Error::PkeyCtxInit)?;
        let rc = unsafe { ffi::EVP_PKEY_sign_init(evp_pkey_ctx.as_mut_ptr()) };
        if rc != 1 {
            return Err(Error::SignInit);
        }

        config_evp_pkey_ctx(evp_pkey_ctx.as_mut_ptr(), mech)?;

        Ok(Self {
            evp_pkey_ctx,
            algo: pkey.algo()?,
            sig_len: pkey.sig_len()?,
            sig_len_ck: pkey.sig_len_ck()?,
        })
    }
}

impl SignCtx for DirectSignCtx {
    fn sign(mut self: Box<Self>, data: &[u8]) -> Result<Vec<u8>, Error> {
        let mut sig = vec![0u8; self.sig_len];
        let mut sig_len: ffi::c_size_t = self.sig_len as ffi::c_size_t;

        let rc = unsafe {
            ffi::EVP_PKEY_sign(
                self.evp_pkey_ctx.as_mut_ptr(),
                sig.as_mut_ptr(),
                &mut sig_len as *mut ffi::c_size_t,
                data.as_ptr(),
                data.len() as ffi::c_size_t,
            )
        };
        if rc != 1 {
            return Err(Error::DirectSign);
        }

        let pkey = unsafe { ffi::EVP_PKEY_CTX_get0_pkey(self.evp_pkey_ctx.as_ptr()) };
        if pkey.is_null() {
            return Err(Error::GeneralError);
        }

        match self.algo {
            KeyAlgo::Ec => ecdsa_sig_der_to_ckrs(sig.as_slice()),
            KeyAlgo::Rsa => Ok(sig),
        }
    }

    fn sig_len_ck(&self) -> usize {
        self.sig_len_ck
    }

    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        if state == OpCtxState::SinglepartActive {
            Ok(())
        } else {
            Err(Error::OperationActive)
        }
    }

    fn update(&mut self, _data: &[u8]) -> Result<(), Error> {
        Err(Error::OperationActive)
    }
    fn finalize(self: Box<Self>) -> Result<Vec<u8>, Error> {
        Err(Error::OperationActive)
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::backend::Mechanism;

use super::ffi;
use super::key::KeyAlgo;
use super::{config_evp_pkey_ctx, ecdsa_sig_ckrs_to_der, mech_type_to_evp_md};
use super::{Error, FfiBox, OpCtxState, Pkey};

/// Verifying context logic interfacing the cryptographic backend library
/// Each session can have one active verifying context at a time

pub trait VerifyCtx: Send {
    fn update(&mut self, data: &[u8]) -> Result<(), Error>;
    fn finalize(self: Box<Self>, signature: &[u8]) -> Result<(), Error>;
    fn verify(self: Box<Self>, data: &[u8], signature: &[u8]) -> Result<(), Error>;
    fn verify_sig_len_ck(&self, sig_len_ck: usize) -> bool;
    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error>;
}

pub struct DigestVerifyCtx {
    state: OpCtxState,
    evp_md_ctx: FfiBox<ffi::EVP_MD_CTX>,
    algo: KeyAlgo,
    sig_len_ck: usize,
}

pub struct DirectVerifyCtx {
    evp_pkey_ctx: FfiBox<ffi::EVP_PKEY_CTX>,
    algo: KeyAlgo,
    sig_len_ck: usize,
}

impl DigestVerifyCtx {
    pub fn new(mech: &Mechanism, mut pkey: Pkey) -> Result<Self, Error> {
        let mut evp_pkey_ctx: *mut ffi::EVP_PKEY_CTX = std::ptr::null_mut();
        let mut evp_md_ctx =
            FfiBox::new(unsafe { ffi::EVP_MD_CTX_new() }).map_err(|_| Error::MdCtxInit)?;

        let evp_md = mech_type_to_evp_md(mech.ck_type())?;

        let rc = unsafe {
            ffi::EVP_DigestVerifyInit(
                evp_md_ctx.as_mut_ptr(),
                &mut evp_pkey_ctx as *mut *mut ffi::EVP_PKEY_CTX,
                evp_md,
                std::ptr::null(),
                pkey.as_mut_ptr(),
            )
        };
        if rc != 1 {
            return Err(Error::VerifyInit);
        }

        config_evp_pkey_ctx(evp_pkey_ctx, mech)?;

        Ok(Self {
            state: OpCtxState::Initialized,
            evp_md_ctx,
            algo: pkey.algo()?,
            sig_len_ck: pkey.sig_len_ck()?,
        })
    }
}

impl VerifyCtx for DigestVerifyCtx {
    fn update(&mut self, data: &[u8]) -> Result<(), Error> {
        self.enter_state(OpCtxState::MultipartActive)?;

        let rc = unsafe {
            ffi::EVP_DigestVerifyUpdate(
                self.evp_md_ctx.as_mut_ptr(),
                data.as_ptr() as *const std::os::raw::c_void,
                data.len() as std::os::raw::c_ulong,
            )
        };
        if rc != 1 {
            return Err(Error::DigestVerifyUpdate);
        }

        Ok(())
    }

    fn finalize(mut self: Box<Self>, signature: &[u8]) -> Result<(), Error> {
        self.enter_state(OpCtxState::MultipartReady)?;

        let rc = unsafe {
            ffi::EVP_DigestVerifyFinal(
                self.evp_md_ctx.as_mut_ptr(),
                signature.as_ptr() as *const std::os::raw::c_uchar,
                signature.len() as std::os::raw::c_ulong,
            )
        };
        if rc != 1 {
            return Err(Error::DigestVerifyFinal);
        }
        Ok(())
    }

    fn verify(mut self: Box<Self>, data: &[u8], signature: &[u8]) -> Result<(), Error> {
        self.enter_state(OpCtxState::SinglepartActive)?;

        let crypto_sig = match self.algo {
            KeyAlgo::Ec => ecdsa_sig_ckrs_to_der(signature)?,
            KeyAlgo::Rsa => signature.to_vec(),
        };

        let rc = unsafe {
            ffi::EVP_DigestVerify(
                self.evp_md_ctx.as_mut_ptr(),
                crypto_sig.as_ptr() as *mut std::os::raw::c_uchar,
                crypto_sig.len() as std::os::raw::c_ulong,
                data.as_ptr() as *mut std::os::raw::c_uchar,
                data.len() as std::os::raw::c_ulong,
            )
        };
        if rc != 1 {
            return Err(Error::DigestVerify);
        }
        Ok(())
    }

    fn verify_sig_len_ck(&self, sig_len_ck: usize) -> bool {
        // NOTE: With ECDSA, the PKCS#11 v2.40 Mechanisms Spec does allow for smaller
        //       signatures, obtained by only padding the lesser of R or S to the size of the
        //       greater one (as opposed to zero-padding both R and S to their maximum size).
        //       So this check should actualy be:
        //         (sig_len_ck & 1) == 0 && sig_len_ck <= self.sig_len_ck
        //       However, older versions of the spec, required full zero-padding, and v2.40
        //       suggests it as well. Our test suite enforces/expects full zero-padding, so
        //       for now, let's just go with the flow.
        sig_len_ck == self.sig_len_ck
    }

    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        let ret = match (self.state, state) {
            (OpCtxState::Initialized, _) => Ok(()),
            (OpCtxState::SinglepartActive, OpCtxState::SinglepartActive) => Ok(()),
            (OpCtxState::SinglepartActive, _) => Err(Error::OperationActive),
            (OpCtxState::MultipartActive, OpCtxState::MultipartActive) => Ok(()),
            (OpCtxState::MultipartActive, OpCtxState::MultipartReady) => Ok(()),
            (OpCtxState::MultipartActive, _) => Err(Error::OperationActive),
            (OpCtxState::MultipartReady, OpCtxState::MultipartReady) => Ok(()),
            (OpCtxState::MultipartReady, _) => Err(Error::OperationActive),
        };
        if ret.is_ok() {
            self.state = state;
        }
        ret
    }
}

impl DirectVerifyCtx {
    pub fn new(mech: &Mechanism, mut pkey: Pkey) -> Result<Self, Error> {
        let mut evp_pkey_ctx =
            FfiBox::new(unsafe { ffi::EVP_PKEY_CTX_new(pkey.as_mut_ptr(), std::ptr::null()) })
                .map_err(|_| Error::PkeyCtxInit)?;
        let rc = unsafe { ffi::EVP_PKEY_verify_init(evp_pkey_ctx.as_mut_ptr()) };
        if rc != 1 {
            return Err(Error::VerifyInit);
        }
        config_evp_pkey_ctx(evp_pkey_ctx.as_mut_ptr(), mech)?;

        Ok(Self {
            evp_pkey_ctx,
            algo: pkey.algo()?,
            sig_len_ck: pkey.sig_len_ck()?,
        })
    }
}

impl VerifyCtx for DirectVerifyCtx {
    fn verify(mut self: Box<Self>, data: &[u8], signature: &[u8]) -> Result<(), Error> {
        let crypto_sig = match self.algo {
            KeyAlgo::Ec => ecdsa_sig_ckrs_to_der(signature)?,
            KeyAlgo::Rsa => signature.to_vec(),
        };
        let rc = unsafe {
            ffi::EVP_PKEY_verify(
                self.evp_pkey_ctx.as_mut_ptr(),
                crypto_sig.as_ptr(),
                crypto_sig.len() as ffi::c_size_t,
                data.as_ptr(),
                data.len() as ffi::c_size_t,
            )
        };
        if rc != 1 {
            return Err(Error::DirectVerify);
        }
        Ok(())
    }

    fn verify_sig_len_ck(&self, sig_len_ck: usize) -> bool {
        // NOTE: With ECDSA, the PKCS#11 v2.40 Mechanisms Spec does allow for smaller
        //       signatures, obtained by only padding the lesser of R or S to the size of the
        //       greater one (as opposed to zero-padding both R and S to their maximum size).
        //       So this check should actualy be:
        //         (sig_len_ck & 1) == 0 && sig_len_ck <= self.sig_len_ck
        //       However, older versions of the spec, required full zero-padding, and v2.40
        //       suggests it as well. Our test suite enforces/expects full zero-padding, so
        //       for now, let's just go with the flow.
        sig_len_ck == self.sig_len_ck
    }

    fn enter_state(&mut self, state: OpCtxState) -> Result<(), Error> {
        if let OpCtxState::SinglepartActive = state {
            Ok(())
        } else {
            Err(Error::OperationActive)
        }
    }

    fn update(&mut self, _data: &[u8]) -> Result<(), Error> {
        Err(Error::OperationActive)
    }
    fn finalize(self: Box<Self>, _signature: &[u8]) -> Result<(), Error> {
        Err(Error::OperationActive)
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::iter::Iterator;

use super::Error;
use crate::pkcs11;

pub struct CkRawAttr(pkcs11::CK_ATTRIBUTE_PTR);

impl CkRawAttr {
    pub unsafe fn from_raw_ptr_unchecked(ptr: pkcs11::CK_ATTRIBUTE_PTR) -> Self {
        Self(ptr)
    }

    pub fn type_(&self) -> pkcs11::CK_ATTRIBUTE_TYPE {
        unsafe { (*self.0).type_ }
    }

    pub fn val_bytes(&self) -> Option<&[u8]> {
        let val_ptr = unsafe { (*self.0).pValue };
        if val_ptr.is_null() {
            return None;
        }
        unsafe {
            Some(std::slice::from_raw_parts(
                val_ptr as *const u8,
                self.len() as usize,
            ))
        }
    }

    pub fn len(&self) -> pkcs11::CK_ULONG {
        unsafe { (*self.0).ulValueLen }
    }

    pub fn set_len(&mut self, len: pkcs11::CK_ULONG) {
        unsafe {
            (*self.0).ulValueLen = len;
        }
    }

    pub fn set_val_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
        unsafe {
            if (*self.0).pValue.is_null() {
                return Err(Error::NullPtrDeref);
            }
            if bytes.len() > (*self.0).ulValueLen as usize {
                return Err(Error::BufTooSmall);
            }
            std::ptr::copy_nonoverlapping(bytes.as_ptr(), (*self.0).pValue as *mut u8, bytes.len());
        }
        Ok(())
    }
}

pub struct CkRawAttrTemplate {
    ptr: pkcs11::CK_ATTRIBUTE_PTR,
    count: usize,
}

impl CkRawAttrTemplate {
    pub unsafe fn from_raw_ptr_unchecked(ptr: pkcs11::CK_ATTRIBUTE_PTR, count: usize) -> Self {
        Self { ptr, count }
    }

    pub fn attr_wrapper(&self, index: usize) -> Option<CkRawAttr> {
        if index >= self.count {
            return None;
        }
        Some(unsafe { CkRawAttr::from_raw_ptr_unchecked(self.ptr.add(index)) })
    }

    pub fn len(&self) -> usize {
        self.count
    }

    pub fn iter(&self) -> CkRawAttrTemplateIter {
        CkRawAttrTemplateIter {
            tpl: self,
            index: 0,
        }
    }
}

pub struct CkRawAttrTemplateIter<'a> {
    tpl: &'a CkRawAttrTemplate,
    index: usize,
}

impl<'a> Iterator for CkRawAttrTemplateIter<'a> {
    type Item = CkRawAttr;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.tpl.len() {
            let ret = self.tpl.attr_wrapper(self.index);
            self.index += 1;
            ret
        } else {
            None
        }
    }
}

pub struct CkRawMechanism {
    ptr: *mut pkcs11::CK_MECHANISM,
}
pub trait MechParams {}
impl MechParams for pkcs11::CK_RSA_PKCS_PSS_PARAMS {}

impl CkRawMechanism {
    pub unsafe fn from_raw_ptr_unchecked(ptr: *mut pkcs11::CK_MECHANISM) -> Self {
        Self { ptr }
    }

    pub fn type_(&self) -> pkcs11::CK_MECHANISM_TYPE {
        unsafe { (*self.ptr).mechanism }
    }

    // Note: marking this unsafe, even if it breaks our pattern of using object constructors
    // to cover unsafe FFI code.
    // Reading the wrong data type is bad, mkay?
    pub unsafe fn params<T: MechParams>(&self) -> Result<Option<T>, Error> {
        let param_ptr = (*self.ptr).pParameter;
        let param_len = (*self.ptr).ulParameterLen;
        if param_ptr.is_null() || param_len == 0 {
            return Ok(None);
        }
        if std::mem::size_of::<T>() != param_len as usize {
            return Err(Error::MechParamTypeMismatch);
        }
        Ok(Some(std::ptr::read(param_ptr as *const T)))
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use log::{Level, LevelFilter, Log, Metadata, Record};

pub struct Logger;

impl Logger {
    pub fn init() {
        let boxed_logger = Box::new(Self {});
        let res = log::set_boxed_logger(boxed_logger);
        match res {
            Ok(_) => log::set_max_level(LevelFilter::Trace),
            Err(_) => trace!("Logger already initialized"),
        }
    }
}

impl Log for Logger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Error
    }

    fn flush(&self) {}

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            eprintln!(
                "{:8} {}:{} {}",
                record.metadata().level(),
                record.file().unwrap(),
                record.line().unwrap(),
                record.args()
            );
        }
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

pub mod ckraw;
pub mod logger;

pub use ckraw::{CkRawAttr, CkRawAttrTemplate, CkRawMechanism};

macro_rules! ck_padded_str {
    ($src:expr, $len: expr) => {{
        let mut ret = [b' '; $len];
        let count = std::cmp::min($src.len(), $len);
        ret[..count].copy_from_slice(&$src.as_bytes()[..count]);
        ret
    }};
}

macro_rules! ck_version {
    ($major:expr, $minor:expr) => {
        crate::pkcs11::CK_VERSION {
            major: $major,
            minor: $minor,
        }
    };
}

pub enum Error {
    BufTooSmall,
    MechParamTypeMismatch,
    NullPtrDeref,
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

pub mod schema {

    pub use v1::*;

    pub mod v1 {
        use super::super::validators;
        use serde::{Deserialize, Serialize};

        /// The RPC URL used for the p11ne API.
        /// Note: this can and should be used to versionize the API.
        pub const API_URL: &str = "/rpc/v1";

        /// An RPC API request, holding the API endpoint (i.e. procedure) and its input params.
        ///
        /// This type will provide serialization (and deserialization) facilities, so that it can be
        /// sent over an RPC transport.
        #[derive(Debug, Deserialize, Serialize)]
        pub enum ApiRequest {
            /// Add a new token. The new token will be inserted into the first free slot.
            ///
            /// Returns:
            /// - `ApiOk::None` on success;
            /// - `ApiError::TooManyTokens` if there are no more free slots left;
            /// - `ApiError::TokenLabelInUse` if the token label is already being used by
            ///    another token;
            /// - `ApiError::{InvalidArgs, InternalError}`.
            AddToken {
                /// The parameters of the new token.
                token: Token,
            },

            /// Get a high-level description of the p11ne device, including active tokens
            /// and the current number of free slots.
            ///
            /// Returns:
            /// - `ApiOk::DeviceDescription` on success;
            /// - `ApiError::InternalError`.
            ///
            DescribeDevice,

            /// Get a detailed description of a specific token.
            /// The caller will need to provide the correct PIN in order to access the token.
            ///
            /// Returns:
            /// - `ApiOk::TokenDescription` on success;
            /// - `ApiError::AccessDenied` if the incorrect PIN is supplied;
            /// - `ApiError::TokenNotFound` if the supplied label doesn't match any active token;
            /// - `ApiError::InternalError`.
            DescribeToken {
                /// Label identified the token to describe.
                label: String,
                /// The PIN granting access to the token to describe.
                pin: String,
            },

            /// Refresh a specific token, by going through the attestation process again, using the
            /// newly supplied credentials.
            ///
            /// Returns:
            /// - `ApiOk::None` on success;
            /// - `ApiError::AccessDenied` if the incorrect PIN is supplied;
            /// - `ApiError::TokenNotFound` if the supplied label doesn't match any active token;
            /// - `ApiError::{InvalidArgs, InternalError}`.
            RefreshToken {
                /// Label of the token to be refreshed.
                label: String,
                /// The PIN granting access to the token identified by `label`.
                pin: String,
                /// The envelope key specification, used to perform enclave attestation / decrypt
                /// the token private keys.
                envelope_key: EnvelopeKey,
            },

            /// Remove a specific token from the p11ne device.
            ///
            /// Returns:
            /// - `ApiOk::None` on success;
            /// - `ApiError::TokenNotFound` if the supplied label doesn't match any active token;
            /// - `ApiError::AccessDenied` if the incorect PIN is supplied;
            /// - `ApiError::InternalError`.
            RemoveToken {
                /// Label of the token to remove.
                label: String,
                /// The PIN granting access to the target token.
                pin: String,
            },

            /// Update a specific token in-place.
            ///
            /// NYI
            UpdateToken {
                label: String,
                pin: String,
                token: Token,
            },
        }

        /// An RPC API response, holding the result type for every API endpoint described by
        /// `ApiRequest`.
        ///
        /// This type will provide serialization (and deserialization) facilities, so that it can be
        /// sent over an RPC transport.
        pub type ApiResponse = std::result::Result<ApiOk, ApiError>;

        #[derive(Debug, Deserialize, Serialize)]
        pub enum ApiOk {
            None,
            DeviceDescription(DeviceDescription),
            TokenDescription(TokenDescription),
        }

        #[derive(Debug, Deserialize, Serialize)]
        pub enum ApiError {
            AccessDenied,
            EmptyToken,
            InvalidArgs(validators::Error),
            InternalError,
            TokenLabelInUse,
            TokenNotFound,
            TooManyTokens,
            TokenProvisioningFailed,
            TokenRefreshFailed,
            TokenKeyDecodingFailed,
            KmsDecryptFailed,
        }

        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub enum EnvelopeKey {
            Kms {
                region: String,
                access_key_id: String,
                secret_access_key: String,
                session_token: String,
            },
        }

        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub struct PrivateKey {
            pub encrypted_pem_b64: String,
            pub id: u8,
            pub label: String,
            pub cert_pem: Option<String>,
        }

        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub struct Token {
            pub label: String,
            pub pin: String,
            pub envelope_key: EnvelopeKey,
            pub keys: Vec<PrivateKey>,
        }

        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub struct PrivateKeyDescription {
            pub label: String,
            pub id: u8,
            pub uri: String,
        }

        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub struct TokenDescription {
            pub label: String,
            pub slot_id: usize,
            pub ttl_secs: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            pub keys: Option<Vec<PrivateKeyDescription>>,
        }

        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub struct DeviceDescription {
            pub free_slot_count: usize,
            pub tokens: Vec<TokenDescription>,
        }
    }
}

pub mod validators {
    use super::schema;
    use serde::{Deserialize, Serialize};
    use std::collections::HashSet;
    use vtok_common::defs;

    #[derive(Debug, Deserialize, Serialize)]
    pub enum Error {
        DuplicateKeyId(u8),
        DuplicateKeyLabel(String),
        LabelTooLong,
        LabelTooShort,
        PinTooLong,
        PinTooShort,
    }

    fn validate_token_pin(pin: &str) -> Result<(), Error> {
        if pin.len() < defs::TOKEN_MIN_PIN_LEN {
            return Err(Error::PinTooShort);
        }
        if pin.len() > defs::TOKEN_MAX_PIN_LEN {
            return Err(Error::PinTooLong);
        }
        Ok(())
    }

    fn validate_token_label(label: &str) -> Result<(), Error> {
        if label.len() < defs::TOKEN_MIN_LABEL_LEN {
            return Err(Error::LabelTooShort);
        }
        if label.len() > defs::TOKEN_MAX_LABEL_LEN {
            return Err(Error::LabelTooLong);
        }
        Ok(())
    }

    fn validate_token(token: &schema::Token) -> Result<(), Error> {
        validate_token_pin(token.pin.as_str())?;
        validate_token_label(token.label.as_str())?;
        let mut ids = HashSet::new();
        let mut labels = HashSet::new();
        for k in token.keys.iter() {
            if !ids.insert(k.id) {
                return Err(Error::DuplicateKeyId(k.id));
            }
            if !labels.insert(k.label.as_str()) {
                return Err(Error::DuplicateKeyLabel(k.label.clone()));
            }
        }
        Ok(())
    }

    impl schema::ApiRequest {
        pub fn validate_args(&self) -> Result<(), Error> {
            match self {
                Self::AddToken { token } => {
                    validate_token(&token)?;
                }
                Self::DescribeDevice => (),
                Self::DescribeToken { label, pin } => {
                    validate_token_pin(pin.as_str())?;
                    validate_token_label(label.as_str())?;
                }
                Self::UpdateToken { label, pin, token } => {
                    validate_token_pin(pin.as_str())?;
                    validate_token_label(label.as_str())?;
                    validate_token(&token)?;
                }
                Self::RefreshToken { label, pin, .. } => {
                    validate_token_pin(pin.as_str())?;
                    validate_token_label(label.as_str())?;
                }
                Self::RemoveToken { label, pin } => {
                    validate_token_pin(pin.as_str())?;
                    validate_token_label(label.as_str())?;
                }
            }
            Ok(())
        }
    }
}


// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

extern crate serde;
extern crate serde_json;

pub mod api;
pub mod proto;
pub mod transport;

pub use proto::{Listener, VsockAddr, VsockListener, VsockStream};
pub use transport::Error as TransportError;
pub use transport::{HttpTransport, Transport};