summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--keystore2/src/crypto/Android.bp1
-rw-r--r--keystore2/src/crypto/crypto.cpp40
-rw-r--r--keystore2/src/crypto/crypto.hpp23
-rw-r--r--keystore2/src/crypto/error.rs4
-rw-r--r--keystore2/src/crypto/lib.rs62
-rw-r--r--keystore2/src/security_level.rs69
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(&params, None /* attestKey */))
+ let creation_result = map_km_error(km_dev.generateKey(&params, 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,