// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::{ffi::CString, fmt::Debug, sync::Arc}; use native_pkcs11_traits::{ backend, Certificate, CertificateExt, KeyAlgorithm, PrivateKey, PublicKey, }; use p256::pkcs8::{ der::{asn1::OctetString, Encode}, AssociatedOid, }; use pkcs1::{der::Decode, RsaPublicKey}; use pkcs11_sys::{ CKC_X_509, CKK_EC, CKK_RSA, CKO_CERTIFICATE, CKO_PRIVATE_KEY, CKO_PROFILE, CKO_PUBLIC_KEY, CK_CERTIFICATE_CATEGORY_UNSPECIFIED, CK_PROFILE_ID, }; use tracing::debug; use crate::attribute::{Attribute, AttributeType, Attributes}; #[derive(Debug)] pub struct DataObject { pub application: CString, pub label: String, pub value: Vec, } // Usage of generics is a workaround for the following issue: // https://github.com/rust-lang/rust/issues/78808#issuecomment-1664416547 #[derive(Debug, PartialEq, Hash, Eq)] pub enum Object< DynCertificate: ?Sized + PartialEq = dyn Certificate, DynPrivateKey: ?Sized + PartialEq = dyn PrivateKey, DynPublicKey: ?Sized + PartialEq = dyn PublicKey, > { Certificate(Arc), PrivateKey(Arc), Profile(CK_PROFILE_ID), PublicKey(Arc), } impl Clone for Object { fn clone(&self) -> Self { match self { Object::Certificate(cert) => Object::Certificate(cert.clone()), Object::PrivateKey(private_key) => Object::PrivateKey(private_key.clone()), Object::Profile(id) => Object::Profile(*id), Object::PublicKey(public_key) => Object::PublicKey(public_key.clone()), } } } impl Object { pub fn attribute(&self, type_: AttributeType) -> Option { match self { Object::Certificate(cert) => match type_ { AttributeType::CertificateCategory => Some(Attribute::CertificateCategory( CK_CERTIFICATE_CATEGORY_UNSPECIFIED, )), AttributeType::CertificateType => Some(Attribute::CertificateType(CKC_X_509)), AttributeType::Class => Some(Attribute::Class(CKO_CERTIFICATE)), AttributeType::Id => Some(Attribute::Id(cert.public_key().public_key_hash())), AttributeType::Issuer => Some(Attribute::Issuer(cert.issuer())), AttributeType::Label => Some(Attribute::Label(cert.label())), AttributeType::Token => Some(Attribute::Token(true)), AttributeType::Trusted => Some(Attribute::Trusted(false)), AttributeType::SerialNumber => Some(Attribute::SerialNumber(cert.serial_number())), AttributeType::Subject => Some(Attribute::Subject(cert.subject())), AttributeType::Value => Some(Attribute::Value(cert.to_der())), _ => { debug!("certificate: type_ unimplemented: {:?}", type_); None } }, Object::PrivateKey(private_key) => match type_ { AttributeType::AlwaysSensitive => Some(Attribute::AlwaysSensitive(true)), AttributeType::AlwaysAuthenticate => Some(Attribute::AlwaysAuthenticate(false)), AttributeType::Class => Some(Attribute::Class(CKO_PRIVATE_KEY)), AttributeType::Decrypt => Some(Attribute::Decrypt(false)), AttributeType::EcParams => { Some(Attribute::EcParams(p256::NistP256::OID.to_der().ok()?)) } AttributeType::Extractable => Some(Attribute::Extractable(false)), AttributeType::Id => Some(Attribute::Id(private_key.public_key_hash())), AttributeType::KeyType => Some(Attribute::KeyType(match private_key.algorithm() { native_pkcs11_traits::KeyAlgorithm::Rsa => CKK_RSA, native_pkcs11_traits::KeyAlgorithm::Ecc => CKK_EC, })), AttributeType::Label => Some(Attribute::Label(private_key.label())), AttributeType::Modulus => { let modulus = private_key .find_public_key(backend()) .ok() .flatten() .and_then(|public_key| { let der = public_key.to_der(); RsaPublicKey::from_der(&der) .map(|pk| pk.modulus.as_bytes().to_vec()) .ok() }); modulus.map(Attribute::Modulus) } AttributeType::NeverExtractable => Some(Attribute::NeverExtractable(true)), AttributeType::Private => Some(Attribute::Private(true)), AttributeType::PublicExponent => { let public_exponent = private_key .find_public_key(backend()) .ok() .flatten() .and_then(|public_key| { let der = public_key.to_der(); RsaPublicKey::from_der(&der) .map(|pk| pk.public_exponent.as_bytes().to_vec()) .ok() }); public_exponent.map(Attribute::PublicExponent) } AttributeType::Sensitive => Some(Attribute::Sensitive(true)), AttributeType::Sign => Some(Attribute::Sign(true)), AttributeType::SignRecover => Some(Attribute::SignRecover(false)), AttributeType::Token => Some(Attribute::Token(true)), AttributeType::Unwrap => Some(Attribute::Unwrap(false)), _ => { debug!("private_key: type_ unimplemented: {:?}", type_); None } }, Object::Profile(id) => match type_ { AttributeType::Class => Some(Attribute::Class(CKO_PROFILE)), AttributeType::ProfileId => Some(Attribute::ProfileId(*id)), AttributeType::Token => Some(Attribute::Token(true)), _ => { debug!("profile: type_ unimplemented: {:?}", type_); None } }, Object::PublicKey(pk) => match type_ { AttributeType::Class => Some(Attribute::Class(CKO_PUBLIC_KEY)), AttributeType::Label => Some(Attribute::Label(pk.label())), AttributeType::Modulus => { let key = pk.to_der(); let key = RsaPublicKey::from_der(&key).unwrap(); Some(Attribute::Modulus(key.modulus.as_bytes().to_vec())) } AttributeType::PublicExponent => { let key = pk.to_der(); let key = RsaPublicKey::from_der(&key).unwrap(); Some(Attribute::Modulus(key.public_exponent.as_bytes().to_vec())) } AttributeType::KeyType => Some(Attribute::KeyType(match pk.algorithm() { native_pkcs11_traits::KeyAlgorithm::Rsa => CKK_RSA, native_pkcs11_traits::KeyAlgorithm::Ecc => CKK_EC, })), AttributeType::Id => Some(Attribute::Id(pk.public_key_hash())), AttributeType::EcPoint => { if pk.algorithm() != KeyAlgorithm::Ecc { return None; } let wrapped = OctetString::new(pk.to_der()).ok()?; Some(Attribute::EcPoint(wrapped.to_der().ok()?)) } AttributeType::EcParams => { Some(Attribute::EcParams(p256::NistP256::OID.to_der().ok()?)) } _ => { debug!("public_key: type_ unimplemented: {:?}", type_); None } }, } } pub fn matches(&self, others: &Attributes) -> bool { if let Some(class) = others.get(AttributeType::Class) { if *class != self.attribute(AttributeType::Class).unwrap() { return false; } } for other in others { if let Some(attr) = self.attribute(other.attribute_type()) { if *other != attr { return false; } } else { return false; } } true } }