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
142 changes: 142 additions & 0 deletions security-framework/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,34 @@ 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_sys::number::{kCFNumberSInt32Type, CFNumberGetValue};
use core_foundation::number::CFNumber;
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation_sys::base::{CFComparisonResult, CFRelease};
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
use core_foundation_sys::string::{CFStringCompareFlags, CFStringRef};
#[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 +83,124 @@ 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()?;
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);
if public_key_type.is_none() || public_keysize.is_none() {
return Ok(None);
}
let public_key_type = public_key_type.unwrap();
let public_keysize = unsafe { CFNumber::from_void(*public_keysize.unwrap().deref()) };
let public_keysize_val = if let Some(v) = public_keysize.to_i64() {
v as u32
} else {
return Ok(None);
};
let hdr_bytes =
get_asn1_header_bytes(*public_key_type.deref() as CFStringRef, public_keysize_val);
if hdr_bytes.is_none() {
return Ok(None);
}
let hdr_bytes = hdr_bytes.unwrap();
let public_key_data = public_key.external_representation();
if public_key_data.is_none() {
return Ok(None);
}
let public_key_data = public_key_data.unwrap();
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());

Ok(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: CFStringRef, ksz: u32) -> Option<&'static [u8]> {
unsafe {
if CFStringCompare(pkt, kSecAttrKeyTypeRSA, 0) as i64 == 0 && ksz == 2048 {
return Some(&RSA_2048_ASN1_HEADER);
}
if CFStringCompare(pkt, kSecAttrKeyTypeRSA, 0) as i64 == 0 && ksz == 4096 {
return Some(&RSA_4096_ASN1_HEADER);
}
if CFStringCompare(pkt, kSecAttrKeyTypeECSECPrimeRandom, 0) as i64 == 0 && ksz == 256 {
return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER);
}
if CFStringCompare(pkt, kSecAttrKeyTypeECSECPrimeRandom, 0) as i64 == 0 && ksz == 384 {
return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER);
}
}
None
}

extern "C" {
#[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
pub fn CFStringCompare(
Copy link
Owner

Choose a reason for hiding this comment

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

You don't need this. CFString implements Eq, so you can compare them with just ==

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using CFStringRef's due to comparing with constants kSecAttrKeyTypeRSA and kSecAttrKeyTypeECSECPrimeRandom. Can I turn those into CFString without allocating?

Copy link
Owner

Choose a reason for hiding this comment

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

I think you can. The CFString is a refcounted type, so a conversion from Ref to non-Ref should only need to change reference count:

https://docs.rs/core-foundation/0.6.3/core_foundation/base/trait.TCFType.html#tymethod.wrap_under_get_rule

Copy link
Owner

Choose a reason for hiding this comment

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

Also the eq is implemented as self.as_CFType().eq(&other.as_CFType())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok switched to CFString now.

theString1: CFStringRef,
theString2: CFStringRef,
compareOptions: CFStringCompareFlags,
) -> CFComparisonResult;
}

#[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