Keystore 2.0: Add Pure Certificate Entry suport.
Allow storing certificates without keys.
Also allow deleting subcomponents by setting corresponding arguments to
None.
Test: KeyStore CTS and keystore2_test
Change-Id: Ie3c937941c6dd6d4a43cd86273cce4f0d7880ca6
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 4e12c64..840fbc6 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -448,6 +448,7 @@
sec_level: SecurityLevel,
parameters: Vec<KeyParameter>,
metadata: KeyMetaData,
+ pure_cert: bool,
}
impl KeyEntry {
@@ -495,10 +496,15 @@
pub fn metadata(&self) -> &KeyMetaData {
&self.metadata
}
+ /// This returns true if the entry is a pure certificate entry with no
+ /// private key component.
+ pub fn pure_cert(&self) -> bool {
+ self.pure_cert
+ }
}
/// Indicates the sub component of a key entry for persistent storage.
-#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct SubComponentType(u32);
impl SubComponentType {
/// Persistent identifier for a key blob.
@@ -878,10 +884,19 @@
.context("In get_or_create_key_with.")?;
let (blob, metadata) = create_new_key().context("In get_or_create_key_with.")?;
- Self::insert_blob_internal(&tx, id, SubComponentType::KEY_BLOB, &blob)
+ Self::set_blob_internal(&tx, id, SubComponentType::KEY_BLOB, Some(&blob))
.context("In get_of_create_key_with.")?;
metadata.store_in_db(id, &tx).context("In get_or_create_key_with.")?;
- (id, KeyEntry { id, km_blob: Some(blob), metadata, ..Default::default() })
+ (
+ id,
+ KeyEntry {
+ id,
+ km_blob: Some(blob),
+ metadata,
+ pure_cert: false,
+ ..Default::default()
+ },
+ )
}
};
tx.commit().context("In get_or_create_key_with: Failed to commit transaction.")?;
@@ -948,36 +963,53 @@
))
}
- /// Inserts a new blob and associates it with the given key id. Each blob
- /// has a sub component type and a security level.
+ /// Set a new blob and associates it with the given key id. Each blob
+ /// has a sub component type.
/// Each key can have one of each sub component type associated. If more
/// are added only the most recent can be retrieved, and superseded blobs
- /// will get garbage collected. The security level field of components
- /// other than `SubComponentType::KEY_BLOB` are ignored.
- pub fn insert_blob(
+ /// will get garbage collected.
+ /// Components SubComponentType::CERT and SubComponentType::CERT_CHAIN can be
+ /// removed by setting blob to None.
+ pub fn set_blob(
&mut self,
key_id: &KeyIdGuard,
sc_type: SubComponentType,
- blob: &[u8],
+ blob: Option<&[u8]>,
) -> Result<()> {
self.with_transaction(TransactionBehavior::Immediate, |tx| {
- Self::insert_blob_internal(&tx, key_id.0, sc_type, blob)
+ Self::set_blob_internal(&tx, key_id.0, sc_type, blob)
})
- .context("In insert_blob.")
+ .context("In set_blob.")
}
- fn insert_blob_internal(
+ fn set_blob_internal(
tx: &Transaction,
key_id: i64,
sc_type: SubComponentType,
- blob: &[u8],
+ blob: Option<&[u8]>,
) -> Result<()> {
- tx.execute(
- "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob)
- VALUES (?, ?, ?);",
- params![sc_type, key_id, blob],
- )
- .context("In insert_blob_internal: Failed to insert blob.")?;
+ match (blob, sc_type) {
+ (Some(blob), _) => {
+ tx.execute(
+ "INSERT INTO persistent.blobentry
+ (subcomponent_type, keyentryid, blob) VALUES (?, ?, ?);",
+ params![sc_type, key_id, blob],
+ )
+ .context("In set_blob_internal: Failed to insert blob.")?;
+ }
+ (None, SubComponentType::CERT) | (None, SubComponentType::CERT_CHAIN) => {
+ tx.execute(
+ "DELETE FROM persistent.blobentry
+ WHERE subcomponent_type = ? AND keyentryid = ?;",
+ params![sc_type, key_id],
+ )
+ .context("In set_blob_internal: Failed to delete blob.")?;
+ }
+ (None, _) => {
+ return Err(KsError::sys())
+ .context("In set_blob_internal: Other blobs cannot be deleted in this way.");
+ }
+ }
Ok(())
}
@@ -1113,24 +1145,24 @@
self.with_transaction(TransactionBehavior::Immediate, |tx| {
let key_id = Self::create_key_entry_internal(tx, domain, namespace)
.context("Trying to create new key entry.")?;
- Self::insert_blob_internal(tx, key_id.id(), SubComponentType::KEY_BLOB, blob)
+ Self::set_blob_internal(tx, key_id.id(), SubComponentType::KEY_BLOB, Some(blob))
.context("Trying to insert the key blob.")?;
if let Some(cert) = cert {
- Self::insert_blob_internal(tx, key_id.id(), SubComponentType::CERT, cert)
+ Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT, Some(&cert))
.context("Trying to insert the certificate.")?;
}
if let Some(cert_chain) = cert_chain {
- Self::insert_blob_internal(
+ Self::set_blob_internal(
tx,
key_id.id(),
SubComponentType::CERT_CHAIN,
- cert_chain,
+ Some(&cert_chain),
)
.context("Trying to insert the certificate chain.")?;
}
Self::insert_keyparameter_internal(tx, &key_id, params)
.context("Trying to insert key parameters.")?;
- metadata.store_in_db(key_id.id(), tx).context("Tryin to insert key metadata.")?;
+ metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
let need_gc = Self::rebind_alias(tx, &key_id, &alias, domain, namespace)
.context("Trying to rebind alias.")?;
Ok((need_gc, key_id))
@@ -1138,6 +1170,42 @@
.context("In store_new_key.")
}
+ /// Store a new certificate
+ /// The function creates a new key entry, populates the blob field and metadata, and rebinds
+ /// the given alias to the new cert.
+ pub fn store_new_certificate(&mut self, key: KeyDescriptor, cert: &[u8]) -> Result<KeyIdGuard> {
+ let (alias, domain, namespace) = match key {
+ KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None }
+ | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => {
+ (alias, key.domain, nspace)
+ }
+ _ => {
+ return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)).context(
+ "In store_new_certificate: Need alias and domain must be APP or SELINUX.",
+ )
+ }
+ };
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let key_id = Self::create_key_entry_internal(tx, domain, namespace)
+ .context("Trying to create new key entry.")?;
+
+ Self::set_blob_internal(tx, key_id.id(), SubComponentType::CERT_CHAIN, Some(cert))
+ .context("Trying to insert certificate.")?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(
+ DateTime::now().context("Trying to make creation time.")?,
+ ));
+
+ metadata.store_in_db(key_id.id(), tx).context("Trying to insert key metadata.")?;
+
+ Self::rebind_alias(tx, &key_id, &alias, domain, namespace)
+ .context("Trying to rebind alias.")?;
+ Ok(key_id)
+ })
+ .context("In store_new_certificate.")
+ }
+
// Helper function loading the key_id given the key descriptor
// tuple comprising domain, namespace, and alias.
// Requires a valid transaction.
@@ -1294,7 +1362,7 @@
key_id: i64,
load_bits: KeyEntryLoadBits,
tx: &Transaction,
- ) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> {
+ ) -> Result<(bool, Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> {
let mut stmt = tx
.prepare(
"SELECT MAX(id), subcomponent_type, blob FROM persistent.blobentry
@@ -1308,9 +1376,11 @@
let mut km_blob: Option<Vec<u8>> = None;
let mut cert_blob: Option<Vec<u8>> = None;
let mut cert_chain_blob: Option<Vec<u8>> = None;
+ let mut has_km_blob: bool = false;
db_utils::with_rows_extract_all(&mut rows, |row| {
let sub_type: SubComponentType =
row.get(1).context("Failed to extract subcomponent_type.")?;
+ has_km_blob = has_km_blob || sub_type == SubComponentType::KEY_BLOB;
match (sub_type, load_bits.load_public(), load_bits.load_km()) {
(SubComponentType::KEY_BLOB, _, true) => {
km_blob = Some(row.get(2).context("Failed to extract KM blob.")?);
@@ -1332,7 +1402,7 @@
})
.context("In load_blob_components.")?;
- Ok((km_blob, cert_blob, cert_chain_blob))
+ Ok((has_km_blob, km_blob, cert_blob, cert_chain_blob))
}
fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> {
@@ -1529,7 +1599,7 @@
) -> Result<KeyEntry> {
let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?;
- let (km_blob, cert_blob, cert_chain_blob) =
+ let (has_km_blob, km_blob, cert_blob, cert_chain_blob) =
Self::load_blob_components(key_id, load_bits, &tx)
.context("In load_key_components.")?;
@@ -1554,6 +1624,7 @@
sec_level,
parameters,
metadata,
+ pure_cert: !has_km_blob,
})
}
@@ -2258,12 +2329,12 @@
static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
#[test]
- fn test_insert_blob() -> Result<()> {
+ fn test_set_blob() -> Result<()> {
let key_id = KEY_ID_LOCK.get(3000);
let mut db = new_test_db()?;
- db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?;
- db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?;
- db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?;
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB))?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB))?;
drop(key_id);
let mut stmt = db.conn.prepare(
@@ -2344,6 +2415,75 @@
}
#[test]
+ fn test_insert_and_load_certificate_entry_domain_app() -> Result<()> {
+ let mut db = new_test_db()?;
+
+ db.store_new_certificate(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ TEST_CERT_BLOB,
+ )
+ .expect("Trying to insert cert.");
+
+ let (_key_guard, mut key_entry) = db
+ .load_key_entry(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::PUBLIC,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .expect("Trying to read certificate entry.");
+
+ assert!(key_entry.pure_cert());
+ assert!(key_entry.cert().is_none());
+ assert_eq!(key_entry.take_cert_chain(), Some(TEST_CERT_BLOB.to_vec()));
+
+ db.unbind_key(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ 1,
+ |_, _| Ok(()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
+ db.load_key_entry(
+ KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 1,
+ alias: Some(TEST_ALIAS.to_string()),
+ blob: None,
+ },
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ 1,
+ |_k, _av| Ok(()),
+ )
+ .unwrap_err()
+ .root_cause()
+ .downcast_ref::<KsError>()
+ );
+
+ Ok(())
+ }
+
+ #[test]
fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
let mut db = new_test_db()?;
let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS, None)
@@ -3084,9 +3224,9 @@
max_usage_count: Option<i32>,
) -> Result<KeyIdGuard> {
let key_id = db.create_key_entry(domain, namespace)?;
- db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?;
- db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?;
- db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?;
+ db.set_blob(&key_id, SubComponentType::KEY_BLOB, Some(TEST_KEY_BLOB))?;
+ db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB))?;
+ db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB))?;
let params = make_test_params(max_usage_count);
db.insert_keyparameter(&key_id, ¶ms)?;
@@ -3118,6 +3258,7 @@
sec_level: SecurityLevel::TRUSTED_ENVIRONMENT,
parameters: params,
metadata,
+ pure_cert: false,
}
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 12b75bf..994b2c8 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -535,10 +535,10 @@
.context("In upgrade_keyblob_if_required_with: Upgrade failed.")?;
key_id_guard.map_or(Ok(()), |key_id_guard| {
DB.with(|db| {
- db.borrow_mut().insert_blob(
+ db.borrow_mut().set_blob(
&key_id_guard,
SubComponentType::KEY_BLOB,
- &upgraded_blob,
+ Some(&upgraded_blob),
)
})
.context(concat!(
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index f8650e6..e57a9b7 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -111,21 +111,26 @@
})
.context("In get_key_entry, while trying to load key info.")?;
- let i_sec_level = self
- .get_security_level_internal(key_entry.sec_level())
- .context("In get_key_entry.")?
- .ok_or_else(|| {
- anyhow!(error::Error::sys()).context(format!(
- concat!(
- "Found key with security level {:?} ",
- "but no KeyMint instance of that security level."
- ),
- key_entry.sec_level()
- ))
- })?;
+ let i_sec_level = if !key_entry.pure_cert() {
+ Some(
+ self.get_security_level_internal(key_entry.sec_level())
+ .context("In get_key_entry.")?
+ .ok_or_else(|| {
+ anyhow!(error::Error::sys()).context(format!(
+ concat!(
+ "Found key with security level {:?} ",
+ "but no KeyMint instance of that security level."
+ ),
+ key_entry.sec_level()
+ ))
+ })?,
+ )
+ } else {
+ None
+ };
Ok(KeyEntryResponse {
- iSecurityLevel: Some(i_sec_level),
+ iSecurityLevel: i_sec_level,
metadata: KeyMetadata {
key: KeyDescriptor {
domain: Domain::KEY_ID,
@@ -154,28 +159,61 @@
) -> Result<()> {
DB.with::<_, Result<()>>(|db| {
let mut db = db.borrow_mut();
- let (key_id_guard, key_entry) = db
- .load_key_entry(
- key.clone(),
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- ThreadState::get_calling_uid(),
- |k, av| {
- check_key_permission(KeyPerm::update(), k, &av)
- .context("In update_subcomponent.")
- },
- )
- .context("Failed to load key_entry.")?;
+ let entry = match db.load_key_entry(
+ key.clone(),
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ ThreadState::get_calling_uid(),
+ |k, av| {
+ check_key_permission(KeyPerm::update(), k, &av)
+ .context("In update_subcomponent.")
+ },
+ ) {
+ Err(e) => match e.root_cause().downcast_ref::<Error>() {
+ Some(Error::Rc(ResponseCode::KEY_NOT_FOUND)) => Ok(None),
+ _ => Err(e),
+ },
+ Ok(v) => Ok(Some(v)),
+ }
+ .context("Failed to load key entry.")?;
- if let Some(cert) = public_cert {
- db.insert_blob(&key_id_guard, SubComponentType::CERT, cert)
+ if let Some((key_id_guard, key_entry)) = entry {
+ db.set_blob(&key_id_guard, SubComponentType::CERT, public_cert)
.context("Failed to update cert subcomponent.")?;
+
+ db.set_blob(&key_id_guard, SubComponentType::CERT_CHAIN, certificate_chain)
+ .context("Failed to update cert chain subcomponent.")?;
+ return Ok(());
}
- if let Some(cert_chain) = certificate_chain {
- db.insert_blob(&key_id_guard, SubComponentType::CERT_CHAIN, cert_chain)
- .context("Failed to update cert chain subcomponent.")?;
+ // If we reach this point we have to check the special condition where a certificate
+ // entry may be made.
+ if !(public_cert.is_none() && certificate_chain.is_some()) {
+ return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("No key to update.");
}
+
+ // So we know that we have a certificate chain and no public cert.
+ // Now check that we have everything we need to make a new certificate entry.
+ let key = match (key.domain, &key.alias) {
+ (Domain::APP, Some(ref alias)) => KeyDescriptor {
+ domain: Domain::APP,
+ nspace: ThreadState::get_calling_uid() as i64,
+ alias: Some(alias.clone()),
+ blob: None,
+ },
+ (Domain::SELINUX, Some(_)) => key.clone(),
+ _ => {
+ return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT))
+ .context("Domain must be APP or SELINUX to insert a certificate.")
+ }
+ };
+
+ // Security critical: This must return on failure. Do not remove the `?`;
+ check_key_permission(KeyPerm::rebind(), &key, &None)
+ .context("Caller does not have permission to insert this certificate.")?;
+
+ db.store_new_certificate(key, certificate_chain.unwrap())
+ .context("Failed to insert new certificate.")?;
Ok(())
})
.context("In update_subcomponent.")