Skip to content

SecCertificate::public_key_info_der #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 15, 2019
1 change: 0 additions & 1 deletion security-framework-sys/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ extern "C" {
) -> SecCertificateRef;
pub fn SecCertificateCopyData(certificate: SecCertificateRef) -> CFDataRef;
pub fn SecCertificateCopySubjectSummary(certificate: SecCertificateRef) -> CFStringRef;
#[cfg(target_os = "macos")]
pub fn SecCertificateCopyCommonName(
certificate: SecCertificateRef,
common_name: *mut CFStringRef,
Expand Down
2 changes: 0 additions & 2 deletions security-framework-sys/src/key.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use core_foundation_sys::base::CFTypeID;
#[cfg(target_os = "macos")]
use core_foundation_sys::data::CFDataRef;
use core_foundation_sys::dictionary::CFDictionaryRef;
#[cfg(target_os = "macos")]
use core_foundation_sys::error::CFErrorRef;

use base::SecKeyRef;
Expand Down
122 changes: 122 additions & 0 deletions security-framework/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,31 @@ use core_foundation_sys::base::kCFAllocatorDefault;
use security_framework_sys::base::{errSecParam, SecCertificateRef};
use security_framework_sys::certificate::*;
use std::fmt;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use std::ptr;

use base::{Error, Result};
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation::base::FromVoid;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation::number::CFNumber;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation_sys::base::CFRelease;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use security_framework_sys::base::SecPolicyRef;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use security_framework_sys::item::*;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use security_framework_sys::policy::SecPolicyCreateBasicX509;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use security_framework_sys::trust::{
SecTrustCopyPublicKey, SecTrustCreateWithCertificates, SecTrustEvaluate, SecTrustRef,
SecTrustResultType,
};
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use std::ops::Deref;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use {cvt, key};

declare_TCFType! {
/// A type representing a certificate.
Expand Down Expand Up @@ -57,8 +80,107 @@ impl SecCertificate {
CFString::wrap_under_create_rule(summary).to_string()
}
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
/// Returns DER encoded subjectPublicKeyInfo of certificate if available. This can be used
/// for certificate pinning.
pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that this is a very specific single-purpose high-level function. Could it be broken down into components that help others write their public_key_info_der and alike themselves?

// Imported from TrustKit
// https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/TSKSPKIHashCache.m
let public_key = self.public_key()?;
Ok(self.pk_to_der(public_key))
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
fn pk_to_der(&self, public_key: key::SecKey) -> Option<Vec<u8>> {
let public_key_attributes = public_key.attributes();
let public_key_type =
public_key_attributes.find(unsafe { kSecAttrKeyType } as *const std::os::raw::c_void)?;
let public_keysize = public_key_attributes
.find(unsafe { kSecAttrKeySizeInBits } as *const std::os::raw::c_void)?;
let public_keysize = unsafe { CFNumber::from_void(*public_keysize.deref()) };
let public_keysize_val = public_keysize.to_i64()? as u32;
let hdr_bytes = get_asn1_header_bytes(
unsafe { CFString::wrap_under_get_rule(*public_key_type.deref() as _) },
public_keysize_val,
)?;
let public_key_data = public_key.external_representation()?;
let mut out = Vec::with_capacity(hdr_bytes.len() + public_key_data.len() as usize);
out.extend_from_slice(hdr_bytes);
out.extend_from_slice(public_key_data.bytes());
Some(out)
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
/// Get public key from certificate
pub fn public_key(&self) -> Result<key::SecKey> {
unsafe {
// Create an X509 trust using the using the certificate
let mut trust: SecTrustRef = ptr::null_mut();
let policy: SecPolicyRef = SecPolicyCreateBasicX509();
cvt(SecTrustCreateWithCertificates(
self.as_concrete_TypeRef() as _,
policy as _,
&mut trust,
))?;

// Get a public key reference for the certificate from the trust
let mut result: SecTrustResultType = 0;
cvt(SecTrustEvaluate(trust, &mut result))?;
let public_key = SecTrustCopyPublicKey(trust);
CFRelease(policy as _);
CFRelease(trust as _);

Ok(key::SecKey::wrap_under_create_rule(public_key))
}
}
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
fn get_asn1_header_bytes(pkt: CFString, ksz: u32) -> Option<&'static [u8]> {
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 2048 {
return Some(&RSA_2048_ASN1_HEADER);
}
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 4096 {
return Some(&RSA_4096_ASN1_HEADER);
}
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) }
&& ksz == 256
{
return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER);
}
if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) }
&& ksz == 384
{
return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER);
}
None
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
const RSA_2048_ASN1_HEADER: [u8; 24] = [
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
];

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
const RSA_4096_ASN1_HEADER: [u8; 24] = [
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
];

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
const EC_DSA_SECP_256_R1_ASN1_HEADER: [u8; 26] = [
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
];

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
const EC_DSA_SECP_384_R1_ASN1_HEADER: [u8; 23] = [
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00,
];

#[cfg(test)]
mod test {
use test::certificate;
Expand Down
30 changes: 30 additions & 0 deletions security-framework/src/key.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
//! Encryption key support

use core_foundation::base::TCFType;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation::base::ToVoid;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation::data::CFData;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation::dictionary::CFDictionary;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation_sys::error::CFErrorRef;
use security_framework_sys::base::SecKeyRef;
use security_framework_sys::key::SecKeyGetTypeID;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use security_framework_sys::key::{SecKeyCopyAttributes, SecKeyCopyExternalRepresentation};
use std::fmt;

declare_TCFType! {
Expand All @@ -14,6 +24,26 @@ impl_TCFType!(SecKey, SecKeyRef, SecKeyGetTypeID);
unsafe impl Sync for SecKey {}
unsafe impl Send for SecKey {}

impl SecKey {
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
/// Translates to SecKeyCopyAttributes
pub fn attributes(&self) -> CFDictionary {
let pka = unsafe { SecKeyCopyAttributes(self.to_void() as _) };
unsafe { CFDictionary::wrap_under_create_rule(pka) }
}

#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
/// Translates to SecKeyCopyExternalRepresentation
pub fn external_representation(&self) -> Option<CFData> {
let mut error: CFErrorRef = ::std::ptr::null_mut();
let data = unsafe { SecKeyCopyExternalRepresentation(self.to_void() as _, &mut error) };
if data == ::std::ptr::null() {
return None;
}
Some(unsafe { CFData::wrap_under_create_rule(data) })
}
}

// FIXME
impl fmt::Debug for SecKey {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
Expand Down