diff options
-rw-r--r-- | keystore2/src/crypto/Android.bp | 1 | ||||
-rw-r--r-- | keystore2/src/crypto/crypto.cpp | 40 | ||||
-rw-r--r-- | keystore2/src/crypto/crypto.hpp | 23 | ||||
-rw-r--r-- | keystore2/src/crypto/error.rs | 4 | ||||
-rw-r--r-- | keystore2/src/crypto/lib.rs | 62 | ||||
-rw-r--r-- | keystore2/src/security_level.rs | 69 |
6 files changed, 191 insertions, 8 deletions
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp index 7673400d..e386735b 100644 --- a/keystore2/src/crypto/Android.bp +++ b/keystore2/src/crypto/Android.bp @@ -74,6 +74,7 @@ rust_bindgen { "--whitelist-function", "ECPOINTOct2Point", "--whitelist-function", "EC_KEY_free", "--whitelist-function", "EC_POINT_free", + "--whitelist-function", "extractSubjectFromCertificate", "--whitelist-type", "EC_KEY", "--whitelist-type", "EC_POINT", "--whitelist-var", "EC_MAX_BYTES", diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp index 3cc19c5a..2e613fda 100644 --- a/keystore2/src/crypto/crypto.cpp +++ b/keystore2/src/crypto/crypto.cpp @@ -26,6 +26,7 @@ #include <openssl/evp.h> #include <openssl/hkdf.h> #include <openssl/rand.h> +#include <openssl/x509.h> #include <vector> @@ -261,3 +262,42 @@ EC_POINT* ECPOINTOct2Point(const uint8_t* buf, size_t len) { } return point; } + +int extractSubjectFromCertificate(const uint8_t* cert_buf, size_t cert_len, uint8_t* subject_buf, + size_t subject_buf_len) { + if (!cert_buf || !subject_buf) { + ALOGE("extractSubjectFromCertificate: received null pointer"); + return 0; + } + + const uint8_t* p = cert_buf; + bssl::UniquePtr<X509> cert(d2i_X509(nullptr /* Allocate X509 struct */, &p, cert_len)); + if (!cert) { + ALOGE("extractSubjectFromCertificate: failed to parse certificate"); + return 0; + } + + X509_NAME* subject = X509_get_subject_name(cert.get()); + if (!subject) { + ALOGE("extractSubjectFromCertificate: failed to retrieve subject name"); + return 0; + } + + int subject_len = i2d_X509_NAME(subject, nullptr /* Don't copy the data */); + if (subject_len < 0) { + ALOGE("extractSubjectFromCertificate: error obtaining encoded subject name length"); + return 0; + } + + if (subject_len > subject_buf_len) { + // Return the subject length, negated, so the caller knows how much + // buffer space is required. + ALOGI("extractSubjectFromCertificate: needed %d bytes for subject, caller provided %zu", + subject_len, subject_buf_len); + return -subject_len; + } + + // subject_buf has enough space. + uint8_t* tmp = subject_buf; + return i2d_X509_NAME(subject, &tmp); +} diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp index 9bd7758e..1b8971f7 100644 --- a/keystore2/src/crypto/crypto.hpp +++ b/keystore2/src/crypto/crypto.hpp @@ -60,6 +60,29 @@ extern "C" { size_t ECPOINTPoint2Oct(const EC_POINT *point, uint8_t *buf, size_t len); EC_POINT* ECPOINTOct2Point(const uint8_t *buf, size_t len); + } +// Parse a DER-encoded X.509 certificate contained in cert_buf, with length +// cert_len, extract the subject, DER-encode it and write the result to +// subject_buf, which has subject_buf_len capacity. +// +// Because the length of the issuer is unknown, and becaue we'd like to (a) be +// able to handle subjects of any size and (b) avoid parsing the certificate +// twice most of the time, once to discover the length and once to parse it, the +// return value is overloaded. +// +// If the return value > 0 it specifies the number of bytes written into +// subject_buf; the operation was successful. +// +// If the return value == 0, certificate parsing failed unrecoverably. The +// reason will be logged. +// +// If the return value < 0, the operation failed because the subject size > +// subject_buf_len. The return value is -(subject_size), where subject_size is +// the size of the extracted DER-encoded subject field. Call +// extractSubjectFromCertificate again with a sufficiently-large buffer. +int extractSubjectFromCertificate(const uint8_t* cert_buf, size_t cert_len, + uint8_t* subject_buf, size_t subject_buf_len); + #endif // __CRYPTO_H__ diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs index 1e84fc68..1eec3216 100644 --- a/keystore2/src/crypto/error.rs +++ b/keystore2/src/crypto/error.rs @@ -85,4 +85,8 @@ pub enum Error { /// This is returned if the C implementation of ECPOINTOct2Point returned null. #[error("Failed to convert oct to point.")] ECOct2PointFailed, + + /// This is returned if the C implementation of extractSubjectFromCertificate failed. + #[error("Failed to extract certificate subject.")] + ExtractSubjectFailed, } diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs index 92b257c2..f23778ce 100644 --- a/keystore2/src/crypto/lib.rs +++ b/keystore2/src/crypto/lib.rs @@ -19,11 +19,12 @@ mod error; mod zvec; pub use error::Error; use keystore2_crypto_bindgen::{ - generateKeyFromPassword, randomBytes, AES_gcm_decrypt, AES_gcm_encrypt, ECDHComputeKey, - ECKEYDeriveFromSecret, ECKEYGenerateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, - EC_KEY_get0_public_key, EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, - EVP_MAX_MD_SIZE, + extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt, + AES_gcm_encrypt, ECDHComputeKey, ECKEYDeriveFromSecret, ECKEYGenerateKey, ECPOINTOct2Point, + ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key, EC_POINT_free, HKDFExpand, HKDFExtract, + EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE, }; +use std::convert::TryFrom; use std::convert::TryInto; use std::marker::PhantomData; pub use zvec::ZVec; @@ -353,6 +354,59 @@ pub fn ec_point_oct_to_point(buf: &[u8]) -> Result<OwnedECPoint, Error> { Ok(OwnedECPoint(result)) } +/// Uses BoringSSL to extract the DER-encoded issuer subject from a +/// DER-encoded X.509 certificate. +pub fn parse_issuer_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> { + // Try with a 200-byte output buffer, should be enough in all but bizarre cases. + let mut retval = vec![0; 200]; + let mut size = unsafe { + extractSubjectFromCertificate( + cert_buf.as_ptr(), + cert_buf.len(), + retval.as_mut_ptr(), + retval.len(), + ) + }; + + if size == 0 { + return Err(Error::ExtractSubjectFailed); + } + + if size < 0 { + // Our buffer wasn't big enough. Make one that is just the right size and try again. + let negated_size = usize::try_from(-size); + retval = match negated_size.ok() { + None => return Err(Error::ExtractSubjectFailed), + Some(size) => vec![0; size], + }; + + size = unsafe { + extractSubjectFromCertificate( + cert_buf.as_ptr(), + cert_buf.len(), + retval.as_mut_ptr(), + retval.len(), + ) + }; + + if size <= 0 { + return Err(Error::ExtractSubjectFailed); + } + } + + // Reduce buffer size to the amount written. + let safe_size = usize::try_from(size); + retval.resize( + match safe_size.ok() { + None => return Err(Error::ExtractSubjectFailed), + Some(size) => size, + }, + 0, + ); + + Ok(retval) +} + #[cfg(test)] mod tests { diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs index b96aea89..37e43c6b 100644 --- a/keystore2/src/security_level.rs +++ b/keystore2/src/security_level.rs @@ -18,8 +18,9 @@ use crate::globals::get_keymint_device; use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ - Algorithm::Algorithm, HardwareAuthenticatorType::HardwareAuthenticatorType, - IKeyMintDevice::IKeyMintDevice, KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, + Algorithm::Algorithm, AttestationKey::AttestationKey, + HardwareAuthenticatorType::HardwareAuthenticatorType, IKeyMintDevice::IKeyMintDevice, + KeyCreationResult::KeyCreationResult, KeyFormat::KeyFormat, KeyMintHardwareInfo::KeyMintHardwareInfo, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel, Tag::Tag, }; @@ -54,6 +55,7 @@ use crate::{ }; use anyhow::{anyhow, Context, Result}; use binder::{IBinder, Strong, ThreadState}; +use keystore2_crypto::parse_issuer_subject_from_certificate; /// Implementation of the IKeystoreSecurityLevel Interface. pub struct KeystoreSecurityLevel { @@ -359,7 +361,7 @@ impl KeystoreSecurityLevel { fn generate_key( &self, key: &KeyDescriptor, - attestation_key: Option<&KeyDescriptor>, + attest_key_descriptor: Option<&KeyDescriptor>, params: &[KeyParameter], flags: i32, entropy: &[u8], @@ -383,19 +385,78 @@ impl KeystoreSecurityLevel { // generate_key requires the rebind permission. check_key_permission(KeyPerm::rebind(), &key, &None).context("In generate_key.")?; + let attest_key = match attest_key_descriptor { + None => None, + Some(key) => Some( + self.get_attest_key(key, caller_uid) + .context("In generate_key: Trying to load attest key")?, + ), + }; + let params = Self::add_certificate_parameters(caller_uid, params, &key) .context("In generate_key: Trying to get aaid.")?; let km_dev: Strong<dyn IKeyMintDevice> = self.keymint.get_interface()?; map_km_error(km_dev.addRngEntropy(entropy)) .context("In generate_key: Trying to add entropy.")?; - let creation_result = map_km_error(km_dev.generateKey(¶ms, None /* attestKey */)) + let creation_result = map_km_error(km_dev.generateKey(¶ms, attest_key.as_ref())) .context("In generate_key: While generating Key")?; let user_id = uid_to_android_user(caller_uid); self.store_new_key(key, creation_result, user_id).context("In generate_key.") } + fn get_attest_key(&self, key: &KeyDescriptor, caller_uid: u32) -> Result<AttestationKey> { + let (km_blob, cert) = self + .load_attest_key_blob_and_cert(&key, caller_uid) + .context("In get_attest_key: Failed to load blob and cert")?; + + let issuer_subject: Vec<u8> = parse_issuer_subject_from_certificate(&cert) + .context("In get_attest_key: Failed to parse subject from certificate.")?; + + Ok(AttestationKey { + keyBlob: km_blob.to_vec(), + attestKeyParams: [].to_vec(), + issuerSubjectName: issuer_subject, + }) + } + + fn load_attest_key_blob_and_cert( + &self, + key: &KeyDescriptor, + caller_uid: u32, + ) -> Result<(Vec<u8>, Vec<u8>)> { + match key.domain { + Domain::BLOB => Err(error::Error::Km(ErrorCode::INVALID_ARGUMENT)).context( + "In load_attest_key_blob_and_cert: Domain::BLOB attestation keys not supported", + ), + _ => { + let (key_id_guard, mut key_entry) = DB + .with::<_, Result<(KeyIdGuard, KeyEntry)>>(|db| { + db.borrow_mut().load_key_entry( + &key, + KeyType::Client, + KeyEntryLoadBits::BOTH, + caller_uid, + |k, av| check_key_permission(KeyPerm::use_(), k, &av), + ) + }) + .context("In load_attest_key_blob_and_cert: Failed to load key.")?; + + let (blob, _) = + key_entry.take_key_blob_info().ok_or_else(Error::sys).context(concat!( + "In load_attest_key_blob_and_cert: Successfully loaded key entry,", + " but KM blob was missing." + ))?; + let cert = key_entry.take_cert().ok_or_else(Error::sys).context(concat!( + "In load_attest_key_blob_and_cert: Successfully loaded key entry,", + " but cert was missing." + ))?; + Ok((blob, cert)) + } + } + } + fn import_key( &self, key: &KeyDescriptor, |