Merge "Added keystore2_unsafe_fuzzer"
diff --git a/OWNERS b/OWNERS
index 752b728..03e5769 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,7 @@
alanstokes@google.com
cbrubaker@google.com
drysdale@google.com
+eranm@google.com
hasinitg@google.com
jbires@google.com
jeffv@google.com
diff --git a/diced/open_dice_cbor/lib.rs b/diced/open_dice_cbor/lib.rs
index 2859a61..ffb8a48 100644
--- a/diced/open_dice_cbor/lib.rs
+++ b/diced/open_dice_cbor/lib.rs
@@ -74,7 +74,7 @@
pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
/// Open dice wrapper error type.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
/// The libopen-dice backend reported InvalidInput.
#[error("Open dice backend: Invalid input")]
diff --git a/diced/src/permission.rs b/diced/src/permission.rs
index 116df1b..62ca653 100644
--- a/diced/src/permission.rs
+++ b/diced/src/permission.rs
@@ -21,7 +21,7 @@
implement_class!(
/// Permission provides a convenient abstraction from the SELinux class `diced`.
#[selinux(class_name = diced)]
- #[derive(Clone, Copy, Debug, PartialEq)]
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Permission {
/// Checked when a client attempts to call seal or unseal.
#[selinux(name = use_seal)]
diff --git a/identity/Android.bp b/identity/Android.bp
index 5b8a10e..512e3ad 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -59,7 +59,7 @@
],
static_libs: [
"android.hardware.identity-V4-cpp",
- "android.hardware.keymaster-V4-cpp",
+ "android.hardware.keymaster-V3-cpp",
"libcppbor_external",
],
}
diff --git a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
index 6da90e5..2561fcc 100644
--- a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
+++ b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
@@ -145,14 +145,38 @@
// encoded DeviceEngagement
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
- ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
- // X and Y are always positive so for interop we remove any leading zeroes
- // inserted by the BigInteger encoder.
- byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
- byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
baos.write(new byte[]{41});
- baos.write(x);
- baos.write(y);
+ ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+ // Each coordinate may be encoded in 33*, 32, or fewer bytes.
+ //
+ // * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
+ // minimum number of bytes required to represent this BigInteger, including at
+ // least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
+ // the MSB is always 0x00. This is taken care of by calling calling
+ // stripLeadingZeroes().
+ //
+ // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
+ // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
+ // where X and Y are encoded in exactly 32 byte, big endian integer values each.
+ //
+ byte[] xBytes = stripLeadingZeroes(w.getAffineX().toByteArray());
+ if (xBytes.length > 32) {
+ throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - xBytes.length; n++) {
+ baos.write(0x00);
+ }
+ baos.write(xBytes);
+
+ byte[] yBytes = stripLeadingZeroes(w.getAffineY().toByteArray());
+ if (yBytes.length > 32) {
+ throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - yBytes.length; n++) {
+ baos.write(0x00);
+ }
+ baos.write(yBytes);
+
baos.write(new byte[]{42, 44});
} catch (IOException e) {
e.printStackTrace();
diff --git a/identity/util/src/java/com/android/security/identity/internal/Util.java b/identity/util/src/java/com/android/security/identity/internal/Util.java
index 4ec54a7..94d7d15 100644
--- a/identity/util/src/java/com/android/security/identity/internal/Util.java
+++ b/identity/util/src/java/com/android/security/identity/internal/Util.java
@@ -1141,14 +1141,38 @@
// encoded DeviceEngagement
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
- ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
- // X and Y are always positive so for interop we remove any leading zeroes
- // inserted by the BigInteger encoder.
- byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
- byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
baos.write(new byte[]{42});
- baos.write(x);
- baos.write(y);
+ ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+ // Each coordinate may be encoded in 33*, 32, or fewer bytes.
+ //
+ // * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
+ // minimum number of bytes required to represent this BigInteger, including at
+ // least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
+ // the MSB is always 0x00. This is taken care of by calling calling
+ // stripLeadingZeroes().
+ //
+ // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
+ // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
+ // where X and Y are encoded in exactly 32 byte, big endian integer values each.
+ //
+ byte[] xBytes = stripLeadingZeroes(w.getAffineX().toByteArray());
+ if (xBytes.length > 32) {
+ throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - xBytes.length; n++) {
+ baos.write(0x00);
+ }
+ baos.write(xBytes);
+
+ byte[] yBytes = stripLeadingZeroes(w.getAffineY().toByteArray());
+ if (yBytes.length > 32) {
+ throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - yBytes.length; n++) {
+ baos.write(0x00);
+ }
+ baos.write(yBytes);
+
baos.write(new byte[]{43, 44});
} catch (IOException e) {
e.printStackTrace();
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 1e9126d..d01c67d 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -1025,7 +1025,7 @@
return 1;
}
- auto listener = std::make_shared<ConfirmationListener>();
+ auto listener = ndk::SharedRefBase::make<ConfirmationListener>();
auto future = listener->get_future();
auto rc = apcService->presentPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index e2d952d..95f917a 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -108,6 +108,12 @@
.prepare("SELECT alias FROM profiles WHERE owner = ? ORDER BY alias ASC;")
.context("In list: Failed to prepare statement.")?;
+ // This allow is necessary to avoid the following error:
+ //
+ // error[E0597]: `stmt` does not live long enough
+ //
+ // See: https://github.com/rust-lang/rust-clippy/issues/8114
+ #[allow(clippy::let_and_return)]
let aliases = stmt
.query_map(params![caller_uid], |row| row.get(0))?
.collect::<rusqlite::Result<Vec<String>>>()
@@ -172,7 +178,7 @@
/// This is the main LegacyKeystore error type, it wraps binder exceptions and the
/// LegacyKeystore errors.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
/// Wraps a LegacyKeystore error code.
#[error("Error::Error({0:?})")]
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index c0593b7..e5c3091 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -65,7 +65,7 @@
}
/// Selinux Error code.
-#[derive(thiserror::Error, Debug, PartialEq)]
+#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum Error {
/// Indicates that an access check yielded no access.
#[error("Permission Denied")]
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 7d56dc9..1dc14ea 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -39,7 +39,7 @@
/// This is the main APC error type, it wraps binder exceptions and the
/// APC ResponseCode.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
/// Wraps an Android Protected Confirmation (APC) response code as defined by the
/// android.security.apc AIDL interface specification.
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 8265dd0..666daeb 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -38,7 +38,7 @@
/// This is the Authorization error type, it wraps binder exceptions and the
/// Authorization ResponseCode
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
/// Wraps an IKeystoreAuthorization response code as defined by
/// android.security.authorization AIDL interface specification.
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index e925180..7ba47c8 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -190,7 +190,7 @@
fn get_key(&'a self) -> &'a [u8] {
match self {
Self::Ref(b) => b,
- Self::Owned(z) => &*z,
+ Self::Owned(z) => z,
}
}
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index f34c5da..b60b64f 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -41,7 +41,7 @@
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
-#[derive(Debug, thiserror::Error, PartialEq)]
+#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum Error {
/// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
#[error("Error::Rc({0:?})")]
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index 70b78ba..edbe6ce 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -186,7 +186,7 @@
Box::new(|uuid, blob| {
let km_dev = get_keymint_dev_by_uuid(uuid).map(|(dev, _)| dev)?;
let _wp = wd::watch_millis("In invalidate key closure: calling deleteKey", 500);
- map_km_error(km_dev.deleteKey(&*blob))
+ map_km_error(km_dev.deleteKey(blob))
.context("In invalidate key closure: Trying to invalidate key blob.")
}),
KeystoreDB::new(&DB_PATH.read().expect("Could not get the database directory."), None)
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 5e88052..62a7d13 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -600,10 +600,8 @@
/// Log error events related to Remote Key Provisioning (RKP).
pub fn log_rkp_error_stats(rkp_error: MetricsRkpError, sec_level: &SecurityLevel) {
- let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(RkpErrorStats {
- rkpError: rkp_error,
- security_level: process_security_level(*sec_level),
- });
+ let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(
+ RkpErrorStats { rkpError: rkp_error, security_level: process_security_level(*sec_level) });
METRICS_STORE.insert_atom(AtomID::RKP_ERROR_STATS, rkp_error_stats);
}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 5da3b32..4f33ba6 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -790,7 +790,7 @@
Ok(mut mutex_guard) => {
let result = match &*mutex_guard {
Some(op) => {
- let result = f(&*op);
+ let result = f(op);
// Any error here means we can discard the operation.
if result.is_err() {
delete_op = true;
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 3cc116b..f012c1b 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -54,7 +54,7 @@
/// the SELinux permissions.
#[repr(i32)]
#[selinux(class_name = keystore2_key)]
- #[derive(Clone, Copy, Debug, PartialEq)]
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeyPerm {
/// Checked when convert_storage_key_to_ephemeral is called.
#[selinux(name = convert_storage_key_to_ephemeral)]
@@ -100,7 +100,7 @@
/// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
/// Using the implement_permission macro we get the same features as `KeyPerm`.
#[selinux(class_name = keystore2)]
- #[derive(Clone, Copy, Debug, PartialEq)]
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeystorePerm {
/// Checked when a new auth token is installed.
#[selinux(name = add_auth)]
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 8ed2be4..ea2698f 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -159,10 +159,8 @@
if self.is_rkp_only() {
return Err(e);
}
- log_rkp_error_stats(
- MetricsRkpError::FALL_BACK_DURING_HYBRID,
- &self.security_level,
- );
+ log_rkp_error_stats(MetricsRkpError::FALL_BACK_DURING_HYBRID,
+ &self.security_level);
Ok(None)
}
Ok(v) => match v {
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index d634e0c..8d2e5ad 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -266,7 +266,7 @@
..Default::default()
},
Domain::SELINUX => KeyDescriptor{domain, nspace: namespace, ..Default::default()},
- _ => return Err(Error::perm()).context(
+ _ => return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
"In list_entries: List entries is only supported for Domain::APP and Domain::SELINUX."
),
};
@@ -278,8 +278,8 @@
// selected.
if let Err(e) = check_key_permission(KeyPerm::GetInfo, &k, &None) {
if let Some(selinux::Error::PermissionDenied) =
- e.root_cause().downcast_ref::<selinux::Error>() {
-
+ e.root_cause().downcast_ref::<selinux::Error>()
+ {
check_keystore_permission(KeystorePerm::List)
.context("In list_entries: While checking keystore permission.")?;
if namespace != -1 {
diff --git a/keystore2/test_utils/authorizations.rs b/keystore2/test_utils/authorizations.rs
index d5a7b7b..c2f0279 100644
--- a/keystore2/test_utils/authorizations.rs
+++ b/keystore2/test_utils/authorizations.rs
@@ -17,8 +17,9 @@
use std::ops::Deref;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, Tag::Tag,
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, Tag::Tag,
};
/// Helper struct to create set of Authorizations.
@@ -87,6 +88,79 @@
});
self
}
+
+ /// Add RSA_public_exponent.
+ pub fn rsa_public_exponent(mut self, e: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::RSA_PUBLIC_EXPONENT,
+ value: KeyParameterValue::LongInteger(e),
+ });
+ self
+ }
+
+ /// Add key size.
+ pub fn key_size(mut self, s: i32) -> Self {
+ self.0.push(KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(s) });
+ self
+ }
+
+ /// Add block mode.
+ pub fn block_mode(mut self, b: BlockMode) -> Self {
+ self.0.push(KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(b) });
+ self
+ }
+
+ /// Add certificate_not_before.
+ pub fn cert_not_before(mut self, b: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::CERTIFICATE_NOT_BEFORE,
+ value: KeyParameterValue::DateTime(b),
+ });
+ self
+ }
+
+ /// Add certificate_not_after.
+ pub fn cert_not_after(mut self, a: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::CERTIFICATE_NOT_AFTER,
+ value: KeyParameterValue::DateTime(a),
+ });
+ self
+ }
+
+ /// Add padding mode.
+ pub fn padding_mode(mut self, p: PaddingMode) -> Self {
+ self.0.push(KeyParameter { tag: Tag::PADDING, value: KeyParameterValue::PaddingMode(p) });
+ self
+ }
+
+ /// Add mgf_digest.
+ pub fn mgf_digest(mut self, d: Digest) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::RSA_OAEP_MGF_DIGEST,
+ value: KeyParameterValue::Digest(d),
+ });
+ self
+ }
+
+ /// Add nonce.
+ pub fn nonce(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter { tag: Tag::NONCE, value: KeyParameterValue::Blob(b) });
+ self
+ }
+
+ /// Add MAC length.
+ pub fn mac_length(mut self, l: i32) -> Self {
+ self.0.push(KeyParameter { tag: Tag::MAC_LENGTH, value: KeyParameterValue::Integer(l) });
+ self
+ }
+
+ /// Add min MAC length.
+ pub fn min_mac_length(mut self, l: i32) -> Self {
+ self.0
+ .push(KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KeyParameterValue::Integer(l) });
+ self
+ }
}
impl Deref for AuthSetBuilder {
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index b1405c7..c25d928 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -17,8 +17,8 @@
use anyhow::Result;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
- KeyPurpose::KeyPurpose,
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
@@ -39,6 +39,26 @@
/// Vold context
pub const TARGET_VOLD_CTX: &str = "u:r:vold:s0";
+/// Key parameters to generate a key.
+pub struct KeyParams {
+ /// Key Size.
+ pub key_size: i32,
+ /// Key Purposes.
+ pub purpose: Vec<KeyPurpose>,
+ /// Padding Mode.
+ pub padding: Option<PaddingMode>,
+ /// Digest.
+ pub digest: Option<Digest>,
+ /// MFG Digest.
+ pub mgf_digest: Option<Digest>,
+ /// Block Mode.
+ pub block_mode: Option<BlockMode>,
+ /// Attestation challenge.
+ pub att_challenge: Option<Vec<u8>>,
+ /// Attestation app id.
+ pub att_app_id: Option<Vec<u8>>,
+}
+
/// To map Keystore errors.
#[derive(thiserror::Error, Debug, Eq, PartialEq)]
pub enum Error {
@@ -168,3 +188,112 @@
}
Ok(key_metadata)
}
+
+/// Generate a RSA key with the given key parameters, alias, domain and namespace.
+pub fn generate_rsa_key(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ domain: Domain,
+ nspace: i64,
+ alias: Option<String>,
+ key_params: &KeyParams,
+ attest_key: Option<&KeyDescriptor>,
+) -> binder::Result<KeyMetadata> {
+ let mut gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .rsa_public_exponent(65537)
+ .key_size(key_params.key_size);
+
+ for purpose in &key_params.purpose {
+ gen_params = gen_params.purpose(*purpose);
+ }
+ if let Some(value) = key_params.digest {
+ gen_params = gen_params.digest(value)
+ }
+ if let Some(value) = key_params.padding {
+ gen_params = gen_params.padding_mode(value);
+ }
+ if let Some(value) = key_params.mgf_digest {
+ gen_params = gen_params.mgf_digest(value);
+ }
+ if let Some(value) = key_params.block_mode {
+ gen_params = gen_params.block_mode(value)
+ }
+ if let Some(value) = &key_params.att_challenge {
+ gen_params = gen_params.attestation_challenge(value.to_vec())
+ }
+ if let Some(value) = &key_params.att_app_id {
+ gen_params = gen_params.attestation_app_id(value.to_vec())
+ }
+
+ let key_metadata = sec_level.generateKey(
+ &KeyDescriptor { domain, nspace, alias, blob: None },
+ attest_key,
+ &gen_params,
+ 0,
+ b"entropy",
+ )?;
+
+ // Must have a public key.
+ assert!(key_metadata.certificate.is_some());
+
+ if attest_key.is_none() && key_params.att_challenge.is_some() && key_params.att_app_id.is_some()
+ {
+ // Should have an attestation record.
+ assert!(key_metadata.certificateChain.is_some());
+ } else {
+ // Should not have an attestation record.
+ assert!(key_metadata.certificateChain.is_none());
+ }
+
+ assert!(
+ (domain == Domain::BLOB && key_metadata.key.blob.is_some())
+ || key_metadata.key.blob.is_none()
+ );
+
+ Ok(key_metadata)
+}
+
+/// Generate AES/3DES key.
+pub fn generate_sym_key(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ algorithm: Algorithm,
+ size: i32,
+ alias: &str,
+ padding_mode: &PaddingMode,
+ block_mode: &BlockMode,
+ min_mac_len: Option<i32>,
+) -> binder::Result<KeyMetadata> {
+ let mut gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(algorithm)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .key_size(size)
+ .padding_mode(*padding_mode)
+ .block_mode(*block_mode);
+
+ if let Some(val) = min_mac_len {
+ gen_params = gen_params.min_mac_length(val);
+ }
+
+ let key_metadata = sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ )?;
+
+ // Should not have public certificate.
+ assert!(key_metadata.certificate.is_none());
+
+ // Should not have an attestation record.
+ assert!(key_metadata.certificateChain.is_none());
+ Ok(key_metadata)
+}
diff --git a/keystore2/tests/keystore2_client_3des_key_tests.rs b/keystore2/tests/keystore2_client_3des_key_tests.rs
new file mode 100644
index 0000000..eda24db
--- /dev/null
+++ b/keystore2/tests/keystore2_client_3des_key_tests.rs
@@ -0,0 +1,218 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{
+ perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
+
+/// Generate a 3DES key. Create encryption and decryption operations using the generated key.
+fn create_3des_key_and_operation(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ padding_mode: PaddingMode,
+ block_mode: BlockMode,
+ nonce: &mut Option<Vec<u8>>,
+) -> Result<(), binder::Status> {
+ let alias = format!("ks_3des_test_key_{}{}", block_mode.0, padding_mode.0);
+
+ let key_metadata = key_generations::generate_sym_key(
+ sec_level,
+ Algorithm::TRIPLE_DES,
+ 168,
+ &alias,
+ &padding_mode,
+ &block_mode,
+ None,
+ )?;
+
+ // Encrypts `SAMPLE_PLAIN_TEXT` whose length is multiple of DES block size.
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ sec_level,
+ padding_mode,
+ block_mode,
+ nonce,
+ None,
+ &key_metadata.key,
+ )?;
+ assert!(cipher_text.is_some());
+
+ let plain_text = perform_sample_sym_key_decrypt_op(
+ sec_level,
+ &cipher_text.unwrap(),
+ padding_mode,
+ block_mode,
+ nonce,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+ assert!(plain_text.is_some());
+ assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+ Ok(())
+}
+
+/// Generate 3DES keys with various block modes and paddings.
+/// - Block Modes: ECB, CBC
+/// - Padding Modes: NONE, PKCS7
+/// Test should generate keys and perform operation successfully.
+#[test]
+fn keystore2_3des_ecb_cbc_generate_key_success() {
+ let keystore2 = get_keystore_service();
+ let block_modes = [BlockMode::ECB, BlockMode::CBC];
+ let padding_modes = [PaddingMode::PKCS7, PaddingMode::NONE];
+
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ for block_mode in block_modes {
+ for padding_mode in padding_modes {
+ assert_eq!(
+ Ok(()),
+ create_3des_key_and_operation(&sec_level, padding_mode, block_mode, &mut None)
+ );
+ }
+ }
+}
+
+/// Try to generate 3DES key with invalid key size. Test should fail to generate a key with
+/// an error code `UNSUPPORTED_KEY_SIZE`.
+#[test]
+fn keystore2_3des_key_fails_unsupported_key_size() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "3des_key_test_invalid_1";
+ let invalid_key_size = 128;
+
+ let result = key_generations::map_ks_error(key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::TRIPLE_DES,
+ invalid_key_size,
+ alias,
+ &PaddingMode::PKCS7,
+ &BlockMode::CBC,
+ None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE), result.unwrap_err());
+}
+
+/// Generate a 3DES key without providing padding mode and try to use the generated key to create
+/// an operation. Test should fail to create an operation with an error code
+/// `UNSUPPORTED_PADDING_MODE`.
+#[test]
+fn keystore2_3des_key_fails_missing_padding() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "3des_key_test_missing_padding";
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::TRIPLE_DES)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .key_size(168)
+ .block_mode(BlockMode::ECB);
+
+ let key_metadata = sec_level
+ .generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ )
+ .unwrap();
+
+ let op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::ENCRYPT)
+ .block_mode(BlockMode::ECB);
+
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &op_params,
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a 3DES key with padding mode NONE. Try to encrypt a text whose length isn't a
+/// multiple of the DES block size.
+#[test]
+fn keystore2_3des_key_encrypt_fails_invalid_input_length() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "3des_key_test_invalid_input_len";
+
+ let key_metadata = key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::TRIPLE_DES,
+ 168,
+ alias,
+ &PaddingMode::NONE,
+ &BlockMode::ECB,
+ None,
+ )
+ .unwrap();
+
+ let op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::ENCRYPT)
+ .padding_mode(PaddingMode::NONE)
+ .block_mode(BlockMode::ECB);
+
+ let op_response = sec_level
+ .createOperation(&key_metadata.key, &op_params, false)
+ .expect("Error in creation of operation using rebound key.");
+ assert!(op_response.iOperation.is_some());
+
+ let op = op_response.iOperation.unwrap();
+ // 3DES expects input should be multiple of DES block size (64-bits) length. Try with invalid
+ // length of input.
+ let invalid_block_size_msg = b"my message 111";
+ let result = key_generations::map_ks_error(op.finish(Some(invalid_block_size_msg), None));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_INPUT_LENGTH), result.unwrap_err());
+}
+
+/// Try to generate 3DES key with BlockMode::CTR. Test should fail to create an operation with an
+/// error code `UNSUPPORTED_BLOCK_MODE`.
+#[test]
+fn keystore2_3des_key_fails_unsupported_block_mode() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let result = key_generations::map_ks_error(create_3des_key_and_operation(
+ &sec_level,
+ PaddingMode::NONE,
+ BlockMode::CTR,
+ &mut None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_BLOCK_MODE), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_aes_key_tests.rs b/keystore2/tests/keystore2_client_aes_key_tests.rs
new file mode 100644
index 0000000..c56eef6
--- /dev/null
+++ b/keystore2/tests/keystore2_client_aes_key_tests.rs
@@ -0,0 +1,472 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{
+ perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
+};
+
+/// Generate a AES key. Create encrypt and decrypt operations using the generated key.
+fn create_aes_key_and_operation(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ key_size: i32,
+ padding_mode: PaddingMode,
+ block_mode: BlockMode,
+ mac_len: Option<i32>,
+ min_mac_len: Option<i32>,
+ nonce: &mut Option<Vec<u8>>,
+) -> Result<(), binder::Status> {
+ let alias = format!("ks_aes_test_key_{}{}{}", key_size, block_mode.0, padding_mode.0);
+
+ let key_metadata = key_generations::generate_sym_key(
+ sec_level,
+ Algorithm::AES,
+ key_size,
+ &alias,
+ &padding_mode,
+ &block_mode,
+ min_mac_len,
+ )?;
+
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ sec_level,
+ padding_mode,
+ block_mode,
+ nonce,
+ mac_len,
+ &key_metadata.key,
+ )?;
+
+ assert!(cipher_text.is_some());
+
+ let plain_text = perform_sample_sym_key_decrypt_op(
+ sec_level,
+ &cipher_text.unwrap(),
+ padding_mode,
+ block_mode,
+ nonce,
+ mac_len,
+ &key_metadata.key,
+ )
+ .unwrap();
+ assert!(plain_text.is_some());
+ assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+ Ok(())
+}
+
+/// Generate AES keys with various block modes and paddings.
+/// - Block Modes: ECB, CBC
+/// - Padding Modes: NONE, PKCS7
+/// Test should generate keys and perform operation successfully.
+#[test]
+fn keystore2_aes_ecb_cbc_generate_key() {
+ let keystore2 = get_keystore_service();
+ let key_sizes = [128, 256];
+ let block_modes = [BlockMode::ECB, BlockMode::CBC];
+ let padding_modes = [PaddingMode::PKCS7, PaddingMode::NONE];
+
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ for key_size in key_sizes {
+ for block_mode in block_modes {
+ for padding_mode in padding_modes {
+ assert_eq!(
+ Ok(()),
+ create_aes_key_and_operation(
+ &sec_level,
+ key_size,
+ padding_mode,
+ block_mode,
+ None,
+ None,
+ &mut None,
+ )
+ );
+ }
+ }
+ }
+}
+
+/// Generate AES keys with -
+/// - Block Modes: `CTR, GCM`
+/// - Padding Modes: `NONE`
+/// Test should generate keys and perform operation successfully.
+#[test]
+fn keystore2_aes_ctr_gcm_generate_key_success() {
+ let keystore2 = get_keystore_service();
+ let key_sizes = [128, 256];
+ let key_params = [(BlockMode::CTR, None, None), (BlockMode::GCM, Some(128), Some(128))];
+
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ for key_size in key_sizes {
+ for (block_mode, mac_len, min_mac_len) in key_params {
+ let result = key_generations::map_ks_error(create_aes_key_and_operation(
+ &sec_level,
+ key_size,
+ PaddingMode::NONE,
+ block_mode,
+ mac_len,
+ min_mac_len,
+ &mut None,
+ ));
+
+ assert_eq!(Ok(()), result);
+ } // End of block mode.
+ } // End of key size.
+}
+
+/// Generate AES keys with -
+/// - Block Modes: `CTR, GCM`
+/// - Padding Modes: `PKCS7`
+/// Try to create an operation using generated keys, test should fail to create an operation
+/// with an error code `INCOMPATIBLE_PADDING_MODE`.
+#[test]
+fn keystore2_aes_ctr_gcm_generate_key_fails_incompatible() {
+ let keystore2 = get_keystore_service();
+ let key_sizes = [128, 256];
+ let key_params = [(BlockMode::CTR, None, None), (BlockMode::GCM, Some(128), Some(128))];
+
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ for key_size in key_sizes {
+ for (block_mode, mac_len, min_mac_len) in key_params {
+ let result = key_generations::map_ks_error(create_aes_key_and_operation(
+ &sec_level,
+ key_size,
+ PaddingMode::PKCS7,
+ block_mode,
+ mac_len,
+ min_mac_len,
+ &mut None,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PADDING_MODE), result.unwrap_err());
+ } // End of block mode.
+ } // End of key size.
+}
+
+/// Try to generate AES key with invalid key size. Test should fail to generate a key with
+/// an error code `UNSUPPORTED_KEY_SIZE`.
+#[test]
+fn keystore2_aes_key_fails_unsupported_key_size() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_invalid_1";
+
+ let result = key_generations::map_ks_error(key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::AES,
+ 1024,
+ alias,
+ &PaddingMode::NONE,
+ &BlockMode::ECB,
+ None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE), result.unwrap_err());
+}
+
+/// Try to generate AES key with GCM block mode without providing `MIN_MAC_LENGTH`.
+/// Test should fail to generate a key with an error code `MISSING_MIN_MAC_LENGTH`.
+#[test]
+fn keystore2_aes_gcm_key_fails_missing_min_mac_len() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_invalid_1";
+
+ let result = key_generations::map_ks_error(key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::AES,
+ 128,
+ alias,
+ &PaddingMode::NONE,
+ &BlockMode::GCM,
+ None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::MISSING_MIN_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Try to create an operation using AES key with multiple block modes. Test should fail to create
+/// an operation with `UNSUPPORTED_BLOCK_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_multi_block_modes() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_invalid_1";
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::AES)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .key_size(128)
+ .block_mode(BlockMode::ECB)
+ .block_mode(BlockMode::CBC)
+ .padding_mode(PaddingMode::NONE);
+
+ let key_metadata = sec_level
+ .generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ )
+ .unwrap();
+
+ let op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::ENCRYPT)
+ .block_mode(BlockMode::ECB)
+ .block_mode(BlockMode::CBC)
+ .padding_mode(PaddingMode::NONE);
+
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &op_params,
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_BLOCK_MODE), result.unwrap_err());
+}
+
+/// Try to create an operation using AES key with multiple padding modes. Test should fail to create
+/// an operation with `UNSUPPORTED_PADDING_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_multi_padding_modes() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_invalid_1";
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::AES)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .key_size(128)
+ .block_mode(BlockMode::ECB)
+ .padding_mode(PaddingMode::PKCS7)
+ .padding_mode(PaddingMode::NONE);
+
+ let key_metadata = sec_level
+ .generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ )
+ .unwrap();
+
+ let op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::ENCRYPT)
+ .block_mode(BlockMode::ECB)
+ .padding_mode(PaddingMode::PKCS7)
+ .padding_mode(PaddingMode::NONE);
+
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &op_params,
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a AES-ECB key with unpadded mode. Try to create an operation using generated key
+/// with PKCS7 padding mode. Test should fail to create an Operation with
+/// `INCOMPATIBLE_PADDING_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_incompatible_padding() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_invalid_1";
+
+ let key_metadata = key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::AES,
+ 128,
+ alias,
+ &PaddingMode::NONE,
+ &BlockMode::ECB,
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PADDING_MODE), result.unwrap_err());
+}
+
+/// Generate a AES-ECB key with unpadded mode. Try to create an operation using generated key
+/// with CBC block mode. Test should fail to create an Operation with
+/// `INCOMPATIBLE_BLOCK_MODE` error code.
+#[test]
+fn keystore2_aes_key_op_fails_incompatible_blockmode() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_invalid_1";
+
+ let key_metadata = key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::AES,
+ 128,
+ alias,
+ &PaddingMode::NONE,
+ &BlockMode::ECB,
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::NONE,
+ BlockMode::CBC,
+ &mut None,
+ None,
+ &key_metadata.key,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_BLOCK_MODE), result.unwrap_err());
+}
+
+/// Generate a AES-GCM key with `MIN_MAC_LENGTH`. Try to create an operation using this
+/// generated key without providing `MAC_LENGTH`. Test should fail to create an operation with
+/// `MISSING_MAC_LENGTH` error code.
+#[test]
+fn keystore2_aes_gcm_op_fails_missing_mac_len() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let mac_len = None;
+ let min_mac_len = Some(128);
+
+ let result = key_generations::map_ks_error(create_aes_key_and_operation(
+ &sec_level,
+ 128,
+ PaddingMode::NONE,
+ BlockMode::GCM,
+ mac_len,
+ min_mac_len,
+ &mut None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::MISSING_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Generate a AES-GCM key with `MIN_MAC_LENGTH`. Try to create an operation using this
+/// generated key and provide `MAC_LENGTH` < key's `MIN_MAC_LENGTH`. Test should fail to create
+/// an operation with `INVALID_MAC_LENGTH` error code.
+#[test]
+fn keystore2_aes_gcm_op_fails_invalid_mac_len() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let mac_len = Some(96);
+ let min_mac_len = Some(104);
+
+ let result = key_generations::map_ks_error(create_aes_key_and_operation(
+ &sec_level,
+ 128,
+ PaddingMode::NONE,
+ BlockMode::GCM,
+ mac_len,
+ min_mac_len,
+ &mut None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Generate a AES-GCM key with `MIN_MAC_LENGTH`. Try to create an operation using this
+/// generated key and provide `MAC_LENGTH` > 128. Test should fail to create an operation with
+/// `UNSUPPORTED_MAC_LENGTH` error code.
+#[test]
+fn keystore2_aes_gcm_op_fails_unsupported_mac_len() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let result = key_generations::map_ks_error(create_aes_key_and_operation(
+ &sec_level,
+ 128,
+ PaddingMode::NONE,
+ BlockMode::GCM,
+ Some(256),
+ Some(128),
+ &mut None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_MAC_LENGTH), result.unwrap_err());
+}
+
+/// Generate a AES-CBC-PKCS7 key without `CALLER_NONCE` authorization. Try to set nonce while
+/// creating an operation using this generated key. Test should fail to create an operation with
+/// `CALLER_NONCE_PROHIBITED` error code.
+#[test]
+fn keystore2_aes_key_op_fails_nonce_prohibited() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "aes_key_test_nonce_1";
+ let mut nonce = Some(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ let key_metadata = key_generations::generate_sym_key(
+ &sec_level,
+ Algorithm::AES,
+ 128,
+ alias,
+ &PaddingMode::PKCS7,
+ &BlockMode::CBC,
+ None,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::NONE,
+ BlockMode::CBC,
+ &mut nonce,
+ None,
+ &key_metadata.key,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::CALLER_NONCE_PROHIBITED), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_ec_key_tests.rs b/keystore2/tests/keystore2_client_ec_key_tests.rs
new file mode 100644
index 0000000..60bcddf
--- /dev/null
+++ b/keystore2/tests/keystore2_client_ec_key_tests.rs
@@ -0,0 +1,412 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
+ KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+use crate::keystore2_client_test_utils::{
+ execute_op_run_as_child, perform_sample_sign_operation, BarrierReached, ForcedOp, TestOutcome,
+};
+
+/// This test will try to load the key with Domain::BLOB.
+/// INVALID_ARGUMENT error is expected.
+#[test]
+fn keystore2_get_key_entry_blob_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Generate a key with domain as BLOB.
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ None,
+ None,
+ None,
+ )
+ .unwrap();
+
+ // Try to load the key using above generated KeyDescriptor.
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+
+ // Delete the generated key blob.
+ sec_level.deleteKey(&key_metadata.key).unwrap();
+}
+
+/// Try to generate a key with invalid Domain. `INVALID_ARGUMENT` error response is expected.
+#[test]
+fn keystore2_generate_key_invalid_domain() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_invalid_test_key_{}", getuid());
+
+ let result = key_generations::map_ks_error(key_generations::generate_ec_key(
+ &*sec_level,
+ Domain(99), // Invalid domain.
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Try to generate a EC key without providing the curve.
+/// `UNSUPPORTED_EC_CURVE or UNSUPPORTED_KEY_SIZE` error response is expected.
+#[test]
+fn keystore2_generate_ec_key_missing_curve() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
+
+ // Don't provide EC curve.
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256);
+
+ let result = key_generations::map_ks_error(sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ ));
+ assert!(result.is_err());
+ let err = result.unwrap_err();
+ assert!(matches!(
+ err,
+ Error::Km(ErrorCode::UNSUPPORTED_EC_CURVE) | Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE)
+ ));
+}
+
+/// Try to generate a EC key with curve `CURVE_25519` having `SIGN and AGREE_KEY` purposes.
+/// `INCOMPATIBLE_PURPOSE` error response is expected.
+#[test]
+fn keystore2_generate_ec_key_25519_multi_purpose() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
+
+ // Specify `SIGN and AGREE_KEY` purposes.
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .ec_curve(EcCurve::CURVE_25519)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::AGREE_KEY)
+ .digest(Digest::SHA_2_256);
+
+ let result = key_generations::map_ks_error(sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate EC keys with curves EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521 and
+/// various digest modes. Try to create operations using generated keys. Operations with digest
+/// modes `SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512` should be created successfully.
+/// Creation of operations with digest modes NONE and MD5 should fail with an error code
+/// `UNSUPPORTED_DIGEST`.
+#[test]
+fn keystore2_ec_generate_key() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::NONE,
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let ec_curves = [EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521];
+
+ for ec_curve in ec_curves {
+ for digest in digests {
+ let alias = format!("ks_ec_test_key_gen_{}{}{}", getuid(), ec_curve.0, digest.0);
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ ec_curve,
+ digest,
+ )
+ .unwrap();
+
+ match key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+ false,
+ )) {
+ Ok(op_response) => {
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+ }
+ Err(e) => {
+ assert_eq!(e, Error::Km(ErrorCode::UNSUPPORTED_DIGEST));
+ assert!(digest == Digest::NONE || digest == Digest::MD5);
+ }
+ }
+ }
+ }
+}
+
+/// Generate EC key with curve `CURVE_25519` and digest mode NONE. Try to create an operation using
+/// generated key. `CURVE_25519` key should support `Digest::NONE` digest mode and test should be
+/// able to create an operation successfully.
+#[test]
+fn keystore2_ec_25519_generate_key_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("ks_ec_25519_none_test_key_gen_{}", getuid());
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ EcCurve::CURVE_25519,
+ Digest::NONE,
+ )
+ .unwrap();
+
+ let op_response = sec_level
+ .createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::NONE),
+ false,
+ )
+ .unwrap();
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+}
+
+/// Generate EC keys with curve `CURVE_25519` and digest modes `MD5, SHA1, SHA-2 224, SHA-2 256,
+/// SHA-2 384 and SHA-2 512`. Try to create operations using generated keys. `CURVE_25519` keys
+/// shouldn't support these digest modes. Test should fail to create operations with an error
+/// `UNSUPPORTED_DIGEST`.
+#[test]
+fn keystore2_ec_25519_generate_key_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ for digest in digests {
+ let alias = format!("ks_ec_25519_test_key_gen_{}{}", getuid(), digest.0);
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ EcCurve::CURVE_25519,
+ digest,
+ )
+ .unwrap();
+
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+ }
+}
+
+/// Generate a EC key with `SHA_2_256` digest mode. Try to create an operation with digest mode
+/// other than `SHA_2_256`. Creation of an operation with generated key should fail with
+/// `INCOMPATIBLE_DIGEST` error as there is a mismatch of digest mode in key authorizations.
+#[test]
+fn keystore2_create_op_with_incompatible_key_digest() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_ec_test_incomp_key_digest";
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .unwrap();
+
+ let digests =
+ [Digest::NONE, Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_384, Digest::SHA_2_512];
+
+ for digest in digests {
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
+ }
+}
+
+/// Generate a key in client#1 and try to use it in other client#2.
+/// Client#2 should fail to load the key as the it doesn't own the client#1 generated key.
+#[test]
+fn keystore2_key_owner_validation() {
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID_1: u32 = 10601;
+
+ let uid1 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_1;
+ let gid1 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_1;
+ let alias = "ks_owner_check_test_key";
+
+ // Client#1: Generate a key and create an operation using generated key.
+ // Wait until the parent notifies to continue. Once the parent notifies, this operation
+ // is expected to be completed successfully.
+ let mut child_handle = execute_op_run_as_child(
+ TARGET_CTX,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ Uid::from_raw(uid1),
+ Gid::from_raw(gid1),
+ ForcedOp(false),
+ );
+
+ // Wait until (client#1) child process notifies us to continue, so that there will be a key
+ // generated by client#1.
+ child_handle.recv();
+
+ // Client#2: This child will try to load the key generated by client#1.
+ const APPLICATION_ID_2: u32 = 10602;
+ let uid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
+ let gid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
+ unsafe {
+ run_as::run_as(TARGET_CTX, Uid::from_raw(uid2), Gid::from_raw(gid2), move || {
+ let keystore2_inst = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2_inst.getKeyEntry(&KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+ });
+ };
+
+ // Notify the child process (client#1) to resume and finish.
+ child_handle.send(&BarrierReached {});
+ assert!(
+ (child_handle.get_result() == TestOutcome::Ok),
+ "Client#1 failed to complete the operation."
+ );
+}
+
+/// Generate EC key with BLOB as domain. Generated key should be returned to caller as key blob.
+/// Verify that `blob` field in the `KeyDescriptor` is not empty and should have the key blob.
+/// Try to use this key for performing a sample operation and the operation should complete
+/// successfully.
+#[test]
+fn keystore2_generate_key_with_blob_domain() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ None,
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .unwrap();
+
+ assert!(key_metadata.certificate.is_some());
+ assert!(key_metadata.certificateChain.is_none());
+
+ // Must have the key blob.
+ assert!(key_metadata.key.blob.is_some());
+
+ let op_response = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ ))
+ .unwrap();
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+
+ // Delete the generated key blob.
+ sec_level.deleteKey(&key_metadata.key).unwrap();
+}
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
new file mode 100644
index 0000000..827a0de
--- /dev/null
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -0,0 +1,206 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Digest::Digest, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+ ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+
+/// Generate an EC signing key and grant it to the user with given access vector.
+fn generate_ec_key_and_grant_to_user(
+ grantee_uid: i32,
+ access_vector: i32,
+) -> binder::Result<KeyDescriptor> {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("{}{}", "ks_grant_test_key_1", getuid());
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ None,
+ None,
+ )
+ .unwrap();
+
+ keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+}
+
+/// Try to grant a key with permission that does not map to any of the `KeyPermission` values.
+/// An error is expected with values that does not map to set of permissions listed in
+/// `KeyPermission`.
+#[test]
+fn keystore2_grant_key_with_invalid_perm_expecting_syserror() {
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ let grantee_uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ let invalid_access_vector = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0 << 19;
+
+ let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
+ grantee_uid.try_into().unwrap(),
+ invalid_access_vector,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::SYSTEM_ERROR), result.unwrap_err());
+}
+
+/// Try to grant a key with empty access vector `KeyPermission::NONE`, should be able to grant a
+/// key with empty access vector successfully. In grantee context try to use the granted key, it
+/// should fail to load the key with permission denied error.
+#[test]
+fn keystore2_grant_key_with_perm_none() {
+ static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ let grant_key_nspace = unsafe {
+ run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let empty_access_vector = KeyPermission::NONE.0;
+
+ let grant_key = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
+ GRANTEE_UID.try_into().unwrap(),
+ empty_access_vector,
+ ))
+ .unwrap();
+
+ assert_eq!(grant_key.domain, Domain::GRANT);
+
+ grant_key.nspace
+ })
+ };
+
+ // In grantee context try to load the key, it should fail to load the granted key as it is
+ // granted with empty access vector.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ },
+ )
+ };
+}
+
+/// Grant a key to the user (grantee) with `GET_INFO|USE` key permissions. Verify whether grantee
+/// can succeed in loading the granted key and try to perform simple operation using this granted
+/// key. Grantee should be able to load the key and use the key to perform crypto operation
+/// successfully. Try to delete the granted key in grantee context where it is expected to fail to
+/// delete it as `DELETE` permission is not granted.
+#[test]
+fn keystore2_grant_get_info_use_key_perm() {
+ static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO|USE key permissions.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
+ let grant_key = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
+ GRANTEE_UID.try_into().unwrap(),
+ access_vector,
+ ))
+ .unwrap();
+
+ assert_eq!(grant_key.domain, Domain::GRANT);
+
+ grant_key.nspace
+ })
+ };
+
+ // In grantee context load the key and try to perform crypto operation.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let sec_level =
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Load the granted key.
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+
+ // Perform sample crypto operation using granted key.
+ let op_response = sec_level
+ .createOperation(
+ &key_entry_response.metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256),
+ false,
+ )
+ .unwrap();
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+
+ // Try to delete the key, it is expected to be fail with permission denied error.
+ let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ }));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ },
+ )
+ };
+}
diff --git a/keystore2/tests/keystore2_client_key_id_domain_tests.rs b/keystore2/tests/keystore2_client_key_id_domain_tests.rs
new file mode 100644
index 0000000..2a1d990
--- /dev/null
+++ b/keystore2/tests/keystore2_client_key_id_domain_tests.rs
@@ -0,0 +1,257 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::perform_sample_sign_operation;
+
+/// Try to generate a key with `Domain::KEY_ID`, test should fail with an error code
+/// `SYSTEM_ERROR`. `Domain::KEY_ID` is not allowed to use for generating a key. Key id is returned
+/// by Keystore2 after a key has been mapped from an alias.
+#[test]
+fn keystore2_generate_key_with_key_id_domain_expect_sys_error() {
+ let alias = "ks_gen_key_id_test_key";
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let result = key_generations::map_ks_error(key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::KEY_ID,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::SYSTEM_ERROR), result.unwrap_err());
+}
+
+/// Generate a key and try to load the generated key using KEY_ID as domain. Create an
+/// operation using key which is loaded with domain as KEY_ID. Test should create an operation
+/// successfully.
+#[test]
+fn keystore2_find_key_with_key_id_as_domain() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "ks_key_id_test_key";
+
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .expect("Failed to generate a EC key.");
+
+ // Try to load the above generated key with KEY_ID as domain.
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_metadata.key.nspace,
+ alias: Some(alias.to_string()),
+ blob: None,
+ })
+ .expect("Error in getKeyEntry to load a key with domain KEY_ID.");
+
+ // Verify above found key is same the one generated.
+ assert_eq!(key_metadata.key, key_entry_response.metadata.key);
+ assert_eq!(key_metadata.certificate, key_entry_response.metadata.certificate);
+ assert_eq!(key_metadata.certificateChain, key_entry_response.metadata.certificateChain);
+ assert_eq!(key_metadata.key.nspace, key_entry_response.metadata.key.nspace);
+
+ // Try to create an operation using above loaded key, operation should be created
+ // successfully.
+ let op_response = sec_level
+ .createOperation(
+ &key_entry_response.metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256),
+ false,
+ )
+ .expect("Error in creation of operation.");
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+}
+
+/// Generate a key with an alias. Generate another key and bind it to the same alias.
+/// Try to create an operation using previously generated key. Creation of an operation should
+/// fail because previously generated key material is no longer accessible. Test should successfully
+/// create an operation using the rebound key.
+#[test]
+fn keystore2_key_id_alias_rebind_verify_by_alias() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_key_id_test_alias_rebind_1_{}", getuid());
+
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .expect("Failed to generate a EC key.");
+
+ // Generate a key with same alias as above generated key, so that alias will be rebound
+ // to this key.
+ let new_key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .expect("Failed to generate a rebound EC key.");
+
+ assert_ne!(key_metadata.key, new_key_metadata.key);
+ assert_ne!(key_metadata.certificate, new_key_metadata.certificate);
+ assert_ne!(key_metadata.key.nspace, new_key_metadata.key.nspace);
+
+ // Try to create an operation using previously generated key_metadata.
+ // It should fail as previously generated key material is no longer remains valid.
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+ // Try to create an operation using rebound key, operation should be created
+ // successfully.
+ let op_response = sec_level
+ .createOperation(
+ &new_key_metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256),
+ false,
+ )
+ .expect("Error in creation of operation using rebound key.");
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+}
+
+/// Generate a key with an alias. Load the generated key with `Domain::KEY_ID`. Generate another
+/// key and bind it to the same alias. Try to create an operation using the key loaded with domain
+/// `KEY_ID`. Creation of an operation should fail because originally loaded key no longer exists.
+/// Test should successfully create an operation using the rebound key.
+#[test]
+fn keystore2_key_id_alias_rebind_verify_by_key_id() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_key_id_test_alias_rebind_2_{}", getuid());
+
+ let key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .expect("Failed to generate a EC key.");
+
+ // Load the above generated key with KEY_ID as domain.
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::KEY_ID,
+ nspace: key_metadata.key.nspace,
+ alias: Some(alias.to_string()),
+ blob: None,
+ })
+ .expect("Error in getKeyEntry to load a key with domain KEY_ID.");
+
+ // Verify above found key is same the one generated.
+ assert_eq!(key_metadata.key, key_entry_response.metadata.key);
+ assert_eq!(key_metadata.certificate, key_entry_response.metadata.certificate);
+ assert_eq!(key_metadata.certificateChain, key_entry_response.metadata.certificateChain);
+ assert_eq!(key_metadata.key.nspace, key_entry_response.metadata.key.nspace);
+
+ // Generate another key with same alias as above generated key, so that alias will be rebound
+ // to this key.
+ let new_key_metadata = key_generations::generate_ec_key(
+ &*sec_level,
+ Domain::APP,
+ -1,
+ Some(alias),
+ EcCurve::P_256,
+ Digest::SHA_2_256,
+ )
+ .expect("Failed to generate a rebound EC key.");
+
+ // Verify that an alias is rebound to a new key.
+ assert_eq!(key_metadata.key.alias, new_key_metadata.key.alias);
+ assert_ne!(key_metadata.key, new_key_metadata.key);
+ assert_ne!(key_metadata.certificate, new_key_metadata.certificate);
+ assert_ne!(key_metadata.key.nspace, new_key_metadata.key.nspace);
+
+ // Try to create an operation using previously loaded key_entry_response.
+ // It should fail as previously generated key material is no longer valid.
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_entry_response.metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+ // Try to create an operation using rebound key, operation should be created
+ // successfully.
+ let op_response = sec_level
+ .createOperation(
+ &new_key_metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256),
+ false,
+ )
+ .expect("Error in creation of operation using rebound key.");
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+}
diff --git a/keystore2/tests/keystore2_client_list_entries_tests.rs b/keystore2/tests/keystore2_client_list_entries_tests.rs
new file mode 100644
index 0000000..d50ef5c
--- /dev/null
+++ b/keystore2/tests/keystore2_client_list_entries_tests.rs
@@ -0,0 +1,187 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
+ KeyPermission::KeyPermission, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
+
+/// Try to find a key with given key parameters using `listEntries` API.
+fn is_key_alias_exist(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ domain: Domain,
+ nspace: i64,
+ alias: String,
+) -> bool {
+ let key_descriptors = keystore2.listEntries(domain, nspace).unwrap();
+ let alias_count = key_descriptors
+ .into_iter()
+ .map(|key| key.alias.unwrap())
+ .filter(|key_alias| *key_alias == alias)
+ .count();
+
+ alias_count != 0
+}
+
+/// List key entries with domain as SELINUX and APP.
+/// 1. Generate a key with domain as SELINUX and find this key entry in list of keys retrieved from
+/// `listEntries` with domain SELINUX. Test should be able find this key entry successfully.
+/// 2. Grant above generated Key to a user.
+/// 3. In a user context, generate a new key with domain as APP. Try to list the key entries with
+/// domain APP. Test should find only one key entry that should be the key generated in user
+/// context. GRANT keys shouldn't be part of this list.
+#[test]
+fn keystore2_list_entries_success() {
+ static GRANTOR_SU_CTX: &str = "u:r:su:s0";
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID: u32 = 91;
+ const APPLICATION_ID: u32 = 10006;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ unsafe {
+ run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = format!("list_entries_grant_key1_{}", getuid());
+
+ // Make sure there is no key exist with this `alias` in `SELINUX` domain and
+ // `SELINUX_SHELL_NAMESPACE` namespace.
+ if is_key_alias_exist(
+ &keystore2,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ alias.to_string(),
+ ) {
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias.to_string()),
+ blob: None,
+ })
+ .unwrap();
+ }
+
+ // Generate a key with above defined `alias`.
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ None,
+ None,
+ )
+ .unwrap();
+
+ // Verify that above generated key entry is listed with domain SELINUX and
+ // namespace SELINUX_SHELL_NAMESPACE
+ assert!(is_key_alias_exist(
+ &keystore2,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ alias,
+ ));
+
+ // Grant a key with GET_INFO permission.
+ let access_vector = KeyPermission::GET_INFO.0;
+ keystore2
+ .grant(&key_metadata.key, GRANTEE_UID.try_into().unwrap(), access_vector)
+ .unwrap();
+ })
+ };
+
+ // In user context validate list of key entries associated with it.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ let sec_level =
+ keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("list_entries_success_key{}", getuid());
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ None,
+ None,
+ )
+ .unwrap();
+
+ // Make sure there is only one key entry exist and that should be the same key
+ // generated in this user context. Granted key shouldn't be included in this list.
+ let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ assert_eq!(1, key_descriptors.len());
+
+ let key = key_descriptors.get(0).unwrap();
+ assert_eq!(key.alias, Some(alias));
+ assert_eq!(key.nspace, GRANTEE_UID.try_into().unwrap());
+ assert_eq!(key.domain, Domain::APP);
+
+ keystore2.deleteKey(&key_metadata.key).unwrap();
+
+ let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ assert_eq!(0, key_descriptors.len());
+ },
+ )
+ };
+}
+
+/// Try to list the key entries with domain SELINUX from user context where user doesn't possesses
+/// `GET_INFO` permission for specified namespace. Test should fail to list key entries with error
+/// response code `PERMISSION_DENIED`.
+#[test]
+fn keystore2_list_entries_fails_perm_denied() {
+ let auid = 91 * AID_USER_OFFSET + 10001;
+ let agid = 91 * AID_USER_OFFSET + 10001;
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ unsafe {
+ run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(
+ keystore2.listEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ })
+ };
+}
+
+/// Try to list key entries with domain BLOB. Test should fail with error repose code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_list_entries_fails_invalid_arg() {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(
+ keystore2.listEntries(Domain::BLOB, key_generations::SELINUX_SHELL_NAMESPACE),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_operation_tests.rs b/keystore2/tests/keystore2_client_operation_tests.rs
new file mode 100644
index 0000000..d8b85f6
--- /dev/null
+++ b/keystore2/tests/keystore2_client_operation_tests.rs
@@ -0,0 +1,404 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{getuid, Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+use crate::keystore2_client_test_utils::{
+ create_signing_operation, execute_op_run_as_child, perform_sample_sign_operation,
+ BarrierReached, ForcedOp, TestOutcome,
+};
+
+/// Create `max_ops` number child processes with the given context and perform an operation under each
+/// child process.
+pub fn create_operations(
+ target_ctx: &'static str,
+ forced_op: ForcedOp,
+ max_ops: i32,
+) -> Vec<run_as::ChildHandle<TestOutcome, BarrierReached>> {
+ let alias = format!("ks_op_test_key_{}", getuid());
+ let base_gid = 99 * AID_USER_OFFSET + 10001;
+ let base_uid = 99 * AID_USER_OFFSET + 10001;
+ (0..max_ops)
+ .into_iter()
+ .map(|i| {
+ execute_op_run_as_child(
+ target_ctx,
+ Domain::APP,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ Uid::from_raw(base_uid + (i as u32)),
+ Gid::from_raw(base_gid + (i as u32)),
+ forced_op,
+ )
+ })
+ .collect()
+}
+
+/// This test verifies that backend service throws BACKEND_BUSY error when all
+/// operations slots are full. This test creates operations in child processes and
+/// collects the status of operations performed in each child proc and determines
+/// whether any child proc exited with error status.
+#[test]
+fn keystore2_backend_busy_test() {
+ const MAX_OPS: i32 = 100;
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
+
+ // Wait until all child procs notifies us to continue,
+ // so that there are definitely enough operations outstanding to trigger a BACKEND_BUSY.
+ for ch in child_handles.iter_mut() {
+ ch.recv();
+ }
+ // Notify each child to resume and finish.
+ for ch in child_handles.iter_mut() {
+ ch.send(&BarrierReached {});
+ }
+
+ // Collect the result and validate whether backend busy has occurred.
+ let mut busy_count = 0;
+ for ch in child_handles.into_iter() {
+ if ch.get_result() == TestOutcome::BackendBusy {
+ busy_count += 1;
+ }
+ }
+ assert!(busy_count > 0)
+}
+
+/// This test confirms that forced operation is having high pruning power.
+/// 1. Initially create regular operations such that there are enough operations outstanding
+/// to trigger BACKEND_BUSY.
+/// 2. Then, create a forced operation. System should be able to prune one of the regular
+/// operations and create a slot for forced operation successfully.
+#[test]
+fn keystore2_forced_op_after_backendbusy_test() {
+ const MAX_OPS: i32 = 100;
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ // Create regular operations.
+ let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
+
+ // Wait until all child procs notifies us to continue, so that there are enough
+ // operations outstanding to trigger a BACKEND_BUSY.
+ for ch in child_handles.iter_mut() {
+ ch.recv();
+ }
+
+ // Create a forced operation.
+ let auid = 99 * AID_USER_OFFSET + 10604;
+ let agid = 99 * AID_USER_OFFSET + 10604;
+ unsafe {
+ run_as::run_as(
+ key_generations::TARGET_VOLD_CTX,
+ Uid::from_raw(auid),
+ Gid::from_raw(agid),
+ move || {
+ let alias = format!("ks_prune_forced_op_key_{}", getuid());
+
+ // To make room for this forced op, system should be able to prune one of the
+ // above created regular operations and create a slot for this forced operation
+ // successfully.
+ create_signing_operation(
+ ForcedOp(true),
+ KeyPurpose::SIGN,
+ Digest::SHA_2_256,
+ Domain::SELINUX,
+ 100,
+ Some(alias),
+ )
+ .expect("Client failed to create forced operation after BACKEND_BUSY state.");
+ },
+ );
+ };
+
+ // Notify each child to resume and finish.
+ for ch in child_handles.iter_mut() {
+ ch.send(&BarrierReached {});
+ }
+
+ // Collect the results of above created regular operations.
+ let mut pruned_count = 0;
+ let mut busy_count = 0;
+ let mut _other_err = 0;
+ for ch in child_handles.into_iter() {
+ match ch.get_result() {
+ TestOutcome::BackendBusy => {
+ busy_count += 1;
+ }
+ TestOutcome::InvalidHandle => {
+ pruned_count += 1;
+ }
+ _ => {
+ _other_err += 1;
+ }
+ }
+ }
+ // Verify that there should be at least one backend busy has occurred while creating
+ // above regular operations.
+ assert!(busy_count > 0);
+
+ // Verify that there should be at least one pruned operation which should have failed while
+ // performing operation.
+ assert!(pruned_count > 0);
+}
+
+/// This test confirms that forced operations can't be pruned.
+/// 1. Creates an initial forced operation and tries to complete the operation after BACKEND_BUSY
+/// error is triggered.
+/// 2. Create MAX_OPS number of forced operations so that definitely enough number of operations
+/// outstanding to trigger a BACKEND_BUSY.
+/// 3. Try to use initially created forced operation (in step #1) and able to perform the
+/// operation successfully. This confirms that none of the later forced operations evicted the
+/// initial forced operation.
+#[test]
+fn keystore2_max_forced_ops_test() {
+ const MAX_OPS: i32 = 100;
+ let auid = 99 * AID_USER_OFFSET + 10205;
+ let agid = 99 * AID_USER_OFFSET + 10205;
+
+ // Create initial forced operation in a child process
+ // and wait for the parent to notify to perform operation.
+ let alias = format!("ks_forced_op_key_{}", getuid());
+ let mut first_op_handle = execute_op_run_as_child(
+ key_generations::TARGET_SU_CTX,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ Uid::from_raw(auid),
+ Gid::from_raw(agid),
+ ForcedOp(true),
+ );
+
+ // Wait until above child proc notifies us to continue, so that there is definitely a forced
+ // operation outstanding to perform a operation.
+ first_op_handle.recv();
+
+ // Create MAX_OPS number of forced operations.
+ let mut child_handles =
+ create_operations(key_generations::TARGET_SU_CTX, ForcedOp(true), MAX_OPS);
+
+ // Wait until all child procs notifies us to continue, so that there are enough operations
+ // outstanding to trigger a BACKEND_BUSY.
+ for ch in child_handles.iter_mut() {
+ ch.recv();
+ }
+
+ // Notify initial created forced operation to continue performing the operations.
+ first_op_handle.send(&BarrierReached {});
+
+ // Collect initially created forced operation result and is expected to complete operation
+ // successfully.
+ let first_op_result = first_op_handle.get_result();
+ assert_eq!(first_op_result, TestOutcome::Ok);
+
+ // Notify each child to resume and finish.
+ for ch in child_handles.iter_mut() {
+ ch.send(&BarrierReached {});
+ }
+
+ // Collect the result and validate whether backend busy has occurred with MAX_OPS number
+ // of forced operations.
+ let busy_count = child_handles
+ .into_iter()
+ .map(|ch| ch.get_result())
+ .filter(|r| *r == TestOutcome::BackendBusy)
+ .count();
+ assert!(busy_count > 0);
+}
+
+/// This test will verify the use case with the same owner(UID) requesting `n` number of operations.
+/// This test confirms that when all operation slots are full and a new operation is requested,
+/// an operation which is least recently used and lived longest will be pruned to make a room
+/// for a new operation. Pruning strategy should prevent the operations of the other owners(UID)
+/// from being pruned.
+///
+/// 1. Create an operation in a child process with `untrusted_app` context and wait for parent
+/// notification to complete the operation.
+/// 2. Let parent process create `n` number of operations such that there are enough operations
+/// outstanding to trigger cannibalizing their own sibling operations.
+/// 3. Sequentially try to use above created `n` number of operations and also add a new operation,
+/// so that it should trigger cannibalizing one of their own sibling operations.
+/// 3.1 While trying to use these pruned operations an `INVALID_OPERATION_HANDLE` error is
+/// expected as they are already pruned.
+/// 4. Notify the child process to resume and complete the operation. It is expected to complete the
+/// operation successfully.
+/// 5. Try to use the latest operation of parent. It is expected to complete the operation
+/// successfully.
+#[test]
+fn keystore2_ops_prune_test() {
+ const MAX_OPS: usize = 40; // This should be at least 32 with sec_level TEE.
+
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10601;
+
+ let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+
+ // Create an operation in an untrusted_app context. Wait until the parent notifies to continue.
+ // Once the parent notifies, this operation is expected to be completed successfully.
+ let alias = format!("ks_reg_op_key_{}", getuid());
+ let mut child_handle = execute_op_run_as_child(
+ TARGET_CTX,
+ Domain::APP,
+ -1,
+ Some(alias),
+ Uid::from_raw(uid),
+ Gid::from_raw(gid),
+ ForcedOp(false),
+ );
+
+ // Wait until child process notifies us to continue, so that an operation from child process is
+ // outstanding to complete the operation.
+ child_handle.recv();
+
+ // Generate a key to use in below operations.
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = format!("ks_prune_op_test_key_{}", getuid());
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ None,
+ None,
+ )
+ .unwrap();
+
+ // Create multiple operations in this process to trigger cannibalizing sibling operations.
+ let mut ops: Vec<binder::Result<CreateOperationResponse>> = (0..MAX_OPS)
+ .into_iter()
+ .map(|_| {
+ sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256),
+ false,
+ )
+ })
+ .collect();
+
+ // Sequentially try to use operation handles created above and also add a new operation.
+ for vec_index in 0..MAX_OPS {
+ match &ops[vec_index] {
+ Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
+ // Older operation handle is pruned, if we try to use that an error is expected.
+ assert_eq!(
+ Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
+ key_generations::map_ks_error(op.update(b"my message"))
+ );
+ }
+ _ => panic!("Operation should have created successfully."),
+ }
+
+ // Create a new operation, it should trigger to cannibalize one of their own sibling
+ // operations.
+ ops.push(
+ sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256),
+ false,
+ ),
+ );
+ }
+
+ // Notify child process to continue the operation.
+ child_handle.send(&BarrierReached {});
+ assert!((child_handle.get_result() == TestOutcome::Ok), "Failed to perform an operation");
+
+ // Try to use the latest operation created by parent, should be able to use it successfully.
+ match ops.last() {
+ Some(Ok(CreateOperationResponse { iOperation: Some(op), .. })) => {
+ assert_eq!(Ok(()), key_generations::map_ks_error(perform_sample_sign_operation(op)));
+ }
+ _ => panic!("Operation should have created successfully."),
+ }
+}
+
+/// Try to create forced operations with various contexts -
+/// - untrusted_app
+/// - system_server
+/// - priv_app
+/// `PERMISSION_DENIED` error response is expected.
+#[test]
+fn keystore2_forced_op_perm_denied_test() {
+ static TARGET_CTXS: &[&str] =
+ &["u:r:untrusted_app:s0", "u:r:system_server:s0", "u:r:priv_app:s0"];
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10601;
+
+ let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+
+ for context in TARGET_CTXS.iter() {
+ unsafe {
+ run_as::run_as(context, Uid::from_raw(uid), Gid::from_raw(gid), move || {
+ let alias = format!("ks_app_forced_op_test_key_{}", getuid());
+ let result = key_generations::map_ks_error(create_signing_operation(
+ ForcedOp(true),
+ KeyPurpose::SIGN,
+ Digest::SHA_2_256,
+ Domain::APP,
+ -1,
+ Some(alias),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ });
+ }
+ }
+}
+
+/// Try to create a forced operation with `vold` context.
+/// Should be able to create forced operation with `vold` context successfully.
+#[test]
+fn keystore2_forced_op_success_test() {
+ static TARGET_CTX: &str = "u:r:vold:s0";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10601;
+
+ let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+
+ unsafe {
+ run_as::run_as(TARGET_CTX, Uid::from_raw(uid), Gid::from_raw(gid), move || {
+ let alias = format!("ks_vold_forced_op_key_{}", getuid());
+ create_signing_operation(
+ ForcedOp(true),
+ KeyPurpose::SIGN,
+ Digest::SHA_2_256,
+ Domain::SELINUX,
+ key_generations::SELINUX_VOLD_NAMESPACE,
+ Some(alias),
+ )
+ .expect("Client with vold context failed to create forced operation.");
+ });
+ }
+}
diff --git a/keystore2/tests/keystore2_client_rsa_key_tests.rs b/keystore2/tests/keystore2_client_rsa_key_tests.rs
new file mode 100644
index 0000000..aa822b9
--- /dev/null
+++ b/keystore2/tests/keystore2_client_rsa_key_tests.rs
@@ -0,0 +1,848 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::getuid;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ BlockMode::BlockMode, Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain,
+ IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{perform_sample_sign_operation, ForcedOp};
+
+/// Generate a RSA key and create an operation using the generated key.
+fn create_rsa_key_and_operation(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ domain: Domain,
+ nspace: i64,
+ alias: Option<String>,
+ key_params: &key_generations::KeyParams,
+ op_purpose: KeyPurpose,
+ forced_op: ForcedOp,
+) -> binder::Result<CreateOperationResponse> {
+ let key_metadata =
+ key_generations::generate_rsa_key(sec_level, domain, nspace, alias, key_params, None)?;
+
+ let mut op_params = authorizations::AuthSetBuilder::new().purpose(op_purpose);
+
+ if let Some(value) = key_params.digest {
+ op_params = op_params.digest(value)
+ }
+ if let Some(value) = key_params.padding {
+ op_params = op_params.padding_mode(value);
+ }
+ if let Some(value) = key_params.mgf_digest {
+ op_params = op_params.mgf_digest(value);
+ }
+ if let Some(value) = key_params.block_mode {
+ op_params = op_params.block_mode(value)
+ }
+
+ sec_level.createOperation(&key_metadata.key, &op_params, forced_op.0)
+}
+
+/// Generate RSA signing keys with -
+/// Padding mode: RSA_PKCS1_1_5_SIGN
+/// Digest modes: `NONE, MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// Create operations with these generated keys. Test should create operations successfully.
+#[test]
+fn keystore2_rsa_generate_signing_key_padding_pkcs1_1_5() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::NONE,
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for digest in digests {
+ let alias = format!("ks_rsa_key_test_{}{}{}", getuid(), key_size, digest.0);
+ let op_response = create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ digest: Some(digest),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ )
+ .unwrap();
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+ } // End of digests.
+ } // End of key-sizes.
+}
+
+/// Generate RSA signing keys with -
+/// Padding mode: RSA_PSS
+/// Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// Create operations with these generated keys. Test should create operations successfully.
+#[test]
+fn keystore2_rsa_generate_signing_key_padding_pss_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for digest in digests {
+ let alias = format!("ks_rsa_key_test_{}{}{}", getuid(), key_size, digest.0);
+ let op_response = create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::RSA_PSS),
+ digest: Some(digest),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ )
+ .unwrap();
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+ } // End of digests.
+ } // End of key-sizes.
+}
+
+/// Generate RSA signing key with -
+/// Padding mode: RSA_PSS
+/// Digest mode: `NONE`.
+/// Try to create an operation with this generated key. Test should fail to create an operation with
+/// `INCOMPATIBLE_DIGEST` error code.
+#[test]
+fn keystore2_rsa_generate_signing_key_padding_pss_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ let alias = format!("ks_rsa_pss_none_key_test_{}{}", getuid(), key_size);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::RSA_PSS),
+ digest: Some(Digest::NONE),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
+ }
+}
+
+/// Generate RSA signing key with -
+/// Padding mode: `NONE`
+/// Digest mode `NONE`
+/// Try to create an operation with this generated key. Test should create an operation successfully.
+#[test]
+fn keystore2_rsa_generate_signing_key_padding_none_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ let alias = format!("ks_rsa_pad_none_key_test_{}{}", getuid(), key_size);
+ let op_response = create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::NONE),
+ digest: Some(Digest::NONE),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ )
+ .unwrap();
+
+ assert!(op_response.iOperation.is_some());
+ assert_eq!(
+ Ok(()),
+ key_generations::map_ks_error(perform_sample_sign_operation(
+ &op_response.iOperation.unwrap()
+ ))
+ );
+ }
+}
+
+/// Generate RSA signing keys with -
+/// Padding mode: `NONE`
+/// Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// Create operations with these generated keys. Test should fail to create operations with
+/// an error code `UNKNOWN_ERROR`.
+#[test]
+fn keystore2_rsa_generate_signing_key_padding_none_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for digest in digests {
+ let alias = format!("ks_rsa_key_test_{}{}{}", getuid(), key_size, digest.0);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::NONE),
+ digest: Some(digest),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNKNOWN_ERROR), result.unwrap_err());
+ }
+ }
+}
+
+/// Generate RSA keys with -
+/// Padding Mode: `RSA_OAEP`
+/// Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// mgf-digests: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// Create a decrypt operations using generated keys. Test should create operations successfully.
+#[test]
+fn keystore2_rsa_generate_key_with_oaep_padding_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let mgf_digests = [
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for digest in digests {
+ for mgf_digest in mgf_digests {
+ let alias =
+ format!("ks_rsa_key_pair_oaep_test_{}{}{}", getuid(), key_size, digest.0);
+ let result = create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(PaddingMode::RSA_OAEP),
+ digest: Some(digest),
+ mgf_digest: Some(mgf_digest),
+ block_mode: Some(BlockMode::ECB),
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ );
+ assert!(result.is_ok());
+ } // End of mgf-digests.
+ } // End of digests.
+ } // End of key-sizes.
+}
+
+/// Generate RSA keys with -
+/// Padding mode: `RSA_OAEP`
+/// Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// Create a decrypt operations using generated keys. Test should create operations successfully.
+#[test]
+fn keystore2_rsa_generate_key_with_oaep_padding_and_digests_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for digest in digests {
+ let alias = format!("ks_rsa_key_pair_oaep_test_{}{}{}", getuid(), key_size, digest.0);
+ let result = create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(PaddingMode::RSA_OAEP),
+ digest: Some(digest),
+ mgf_digest: None,
+ block_mode: Some(BlockMode::ECB),
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ );
+ assert!(result.is_ok());
+ } // End of digests.
+ } // End of key-sizes.
+}
+
+/// Generate RSA encryption key with -
+/// Digest mode: `NONE`
+/// Padding mode: `RSA_OAEP`
+/// Try to create an operation using generated key. Test should fail to create an operation
+/// with an error code `INCOMPATIBLE_DIGEST`.
+#[test]
+fn keystore2_rsa_generate_key_with_oaep_padding_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ let alias = format!("ks_rsa_key_padding_{}{}", getuid(), key_size);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(PaddingMode::RSA_OAEP),
+ digest: Some(Digest::NONE),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
+ }
+}
+
+/// Generate RSA encryption keys with various digest mode and padding mode combinations.
+/// Digest modes: `MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512`
+/// Padding modes: `NONE, RSA_PKCS1_1_5_ENCRYPT`
+/// Try to create operations using generated keys, test should create operations successfully.
+#[test]
+fn keystore2_rsa_generate_keys_with_digest_paddings() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digests = [
+ Digest::NONE,
+ Digest::MD5,
+ Digest::SHA1,
+ Digest::SHA_2_224,
+ Digest::SHA_2_256,
+ Digest::SHA_2_384,
+ Digest::SHA_2_512,
+ ];
+
+ let paddings = [PaddingMode::NONE, PaddingMode::RSA_PKCS1_1_5_ENCRYPT];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for digest in digests {
+ for padding in paddings {
+ let alias = format!("ks_rsa_key_padding_{}{}{}", getuid(), key_size, digest.0);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(padding),
+ digest: Some(digest),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+
+ assert!(result.is_ok());
+ } // End of paddings.
+ } // End of digests.
+ } // End of key-sizes.
+}
+
+/// Generate RSA encryption keys with only padding modes.
+/// Padding modes: `NONE, RSA_PKCS1_1_5_ENCRYPT`
+/// Try to create operations using generated keys, test should create operations successfully.
+#[test]
+fn keystore2_rsa_generate_keys_with_paddings() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let paddings = [PaddingMode::NONE, PaddingMode::RSA_PKCS1_1_5_ENCRYPT];
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ for padding in paddings {
+ let alias = format!("ks_rsa_key_padding_{}{}", getuid(), key_size);
+ let result = create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(padding),
+ digest: None,
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ );
+ assert!(result.is_ok());
+ } // End of paddings.
+ } // End of key-sizes.
+}
+
+/// Generate RSA keys without padding and digest modes. Try to create decrypt operation without
+/// digest and padding. Creation of an operation should fail with an error code
+/// `UNSUPPORTED_PADDING_MODE`.
+#[test]
+fn keystore2_rsa_generate_keys() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_sizes = [2048, 3072, 4096];
+
+ for key_size in key_sizes {
+ let alias = format!("ks_rsa_key_test_{}{}", getuid(), key_size);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: None,
+ digest: None,
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+ }
+}
+
+/// Generate a RSA encryption key. Try to create a signing operation with it, an error
+/// `INCOMPATIBLE_PURPOSE` is expected as the generated key doesn't support sign operation.
+#[test]
+fn keystore2_rsa_encrypt_key_op_invalid_purpose() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_test_key_1";
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA signing key. Try to create a decrypt operation with it, an error
+/// `INCOMPATIBLE_PURPOSE` is expected as the generated key doesn't support decrypt operation.
+#[test]
+fn keystore2_rsa_sign_key_op_invalid_purpose() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_test_key_2";
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA key with SIGN and AGREE_KEY purposes. Try to perform an operation using the
+/// generated key, an error `UNSUPPORTED_PURPOSE` is expected as RSA doesn't support AGREE_KEY.
+#[test]
+fn keystore2_rsa_key_unsupported_purpose() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_key_test_3";
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::AGREE_KEY],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::AGREE_KEY,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA encrypt key with padding mode supported for signing. Try to create an operation
+/// using generated key, an error `UNSUPPORTED_PADDING_MODE` is expected with unsupported padding
+/// mode.
+#[test]
+fn keystore2_rsa_encrypt_key_unsupported_padding() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let paddings = [PaddingMode::RSA_PKCS1_1_5_SIGN, PaddingMode::RSA_PSS];
+
+ for padding in paddings {
+ let alias = format!("ks_rsa_key_test_4_{}{}", getuid(), padding.0);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(padding),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+ }
+}
+
+/// Generate a RSA signing key with padding mode supported for encryption. Try to create an
+/// operation using generated key, an error `UNSUPPORTED_PADDING_MODE` is expected with
+/// unsupported padding mode.
+#[test]
+fn keystore2_rsa_signing_key_unsupported_padding() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let paddings = [PaddingMode::RSA_PKCS1_1_5_ENCRYPT, PaddingMode::RSA_OAEP];
+
+ for padding in paddings {
+ let alias = format!("ks_rsa_key_test_4_{}{}", getuid(), padding.0);
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(padding),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::SIGN,
+ ForcedOp(false),
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PADDING_MODE), result.unwrap_err());
+ }
+}
+
+/// Generate a RSA encryption key. Try to perform encrypt operation using the generated
+/// key, an error `UNSUPPORTED_PURPOSE` is expected as encrypt operation is not supported
+/// with RSA key.
+#[test]
+fn keystore2_rsa_key_unsupported_op() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_key_test_5";
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::ENCRYPT,
+ ForcedOp(false),
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_PURPOSE), result.unwrap_err());
+}
+
+/// Generate a RSA key with encrypt, sign and verify purpose. Try to perform decrypt operation
+/// using the generated key, an error `INCOMPATIBLE_PURPOSE` is expected as the key is not
+/// generated with decrypt purpose.
+#[test]
+fn keystore2_rsa_key_missing_purpose() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_key_test_6";
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
+}
+
+/// Generate RSA encryption keys with OAEP padding mode and without digest mode. Try to create an
+/// operation with generated key, unsupported digest error is expected.
+#[test]
+fn keystore2_rsa_gen_keys_with_oaep_paddings_without_digest() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_key_padding_fail";
+ let result = key_generations::map_ks_error(create_rsa_key_and_operation(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 2048,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT],
+ padding: Some(PaddingMode::RSA_OAEP),
+ digest: None,
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ KeyPurpose::DECRYPT,
+ ForcedOp(false),
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+}
+
+/// Generate RSA keys with unsupported key size, an error `UNSUPPORTED_KEY_SIZE` is expected.
+#[test]
+fn keystore2_rsa_gen_keys_unsupported_size() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "ks_rsa_key_padding_fail";
+ let result = key_generations::map_ks_error(key_generations::generate_rsa_key(
+ &sec_level,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ &key_generations::KeyParams {
+ key_size: 5120,
+ purpose: vec![KeyPurpose::ENCRYPT, KeyPurpose::SIGN, KeyPurpose::VERIFY],
+ padding: Some(PaddingMode::RSA_PKCS1_1_5_ENCRYPT),
+ digest: Some(Digest::SHA_2_256),
+ mgf_digest: None,
+ block_mode: None,
+ att_challenge: None,
+ att_app_id: None,
+ },
+ None,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
new file mode 100644
index 0000000..006f2f9
--- /dev/null
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -0,0 +1,204 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{Gid, Uid};
+use serde::{Deserialize, Serialize};
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ BlockMode::BlockMode, Digest::Digest, ErrorCode::ErrorCode,
+ KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel, Tag::Tag,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain,
+ IKeystoreOperation::IKeystoreOperation, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+ KeyDescriptor::KeyDescriptor, KeyParameters::KeyParameters, ResponseCode::ResponseCode,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
+};
+
+/// This enum is used to communicate between parent and child processes.
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub enum TestOutcome {
+ Ok,
+ BackendBusy,
+ InvalidHandle,
+ OtherErr,
+}
+
+/// This is used to notify the child or parent process that the expected state is reched.
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct BarrierReached;
+
+/// Forced operation.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ForcedOp(pub bool);
+
+/// Sample plain text input for encrypt operation.
+pub const SAMPLE_PLAIN_TEXT: &[u8] = b"my message 11111";
+
+/// Generate a EC_P256 key using given domain, namespace and alias.
+/// Create an operation using the generated key and perform sample signing operation.
+pub fn create_signing_operation(
+ forced_op: ForcedOp,
+ op_purpose: KeyPurpose,
+ op_digest: Digest,
+ domain: Domain,
+ nspace: i64,
+ alias: Option<String>,
+) -> binder::Result<CreateOperationResponse> {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let key_metadata = key_generations::generate_ec_p256_signing_key(
+ &sec_level, domain, nspace, alias, None, None,
+ )
+ .unwrap();
+
+ sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(op_purpose).digest(op_digest),
+ forced_op.0,
+ )
+}
+
+/// Performs sample signing operation.
+pub fn perform_sample_sign_operation(
+ op: &binder::Strong<dyn IKeystoreOperation>,
+) -> Result<(), binder::Status> {
+ op.update(b"my message")?;
+ let sig = op.finish(None, None)?;
+ assert!(sig.is_some());
+ Ok(())
+}
+
+/// Create new operation on child proc and perform simple operation after parent notification.
+pub fn execute_op_run_as_child(
+ target_ctx: &'static str,
+ domain: Domain,
+ nspace: i64,
+ alias: Option<String>,
+ auid: Uid,
+ agid: Gid,
+ forced_op: ForcedOp,
+) -> run_as::ChildHandle<TestOutcome, BarrierReached> {
+ unsafe {
+ run_as::run_as_child(target_ctx, auid, agid, move |reader, writer| {
+ let result = key_generations::map_ks_error(create_signing_operation(
+ forced_op,
+ KeyPurpose::SIGN,
+ Digest::SHA_2_256,
+ domain,
+ nspace,
+ alias,
+ ));
+
+ // Let the parent know that an operation has been started, then
+ // wait until the parent notifies us to continue, so the operation
+ // remains open.
+ writer.send(&BarrierReached {});
+ reader.recv();
+
+ // Continue performing the operation after parent notifies.
+ match &result {
+ Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
+ match key_generations::map_ks_error(perform_sample_sign_operation(op)) {
+ Ok(()) => TestOutcome::Ok,
+ Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {
+ TestOutcome::InvalidHandle
+ }
+ Err(e) => panic!("Error in performing op: {:#?}", e),
+ }
+ }
+ Ok(_) => TestOutcome::OtherErr,
+ Err(Error::Rc(ResponseCode::BACKEND_BUSY)) => TestOutcome::BackendBusy,
+ _ => TestOutcome::OtherErr,
+ }
+ })
+ .expect("Failed to create an operation.")
+ }
+}
+
+/// Get NONCE value from given key parameters list.
+pub fn get_op_nonce(parameters: &KeyParameters) -> Option<Vec<u8>> {
+ for key_param in ¶meters.keyParameter {
+ if key_param.tag == Tag::NONCE {
+ if let KeyParameterValue::Blob(val) = &key_param.value {
+ return Some(val.clone());
+ }
+ }
+ }
+ None
+}
+
+/// This performs sample encryption operation with given symmetric key (AES/3DES).
+/// It encrypts `SAMPLE_PLAIN_TEXT` of length 128-bits.
+pub fn perform_sample_sym_key_encrypt_op(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ padding_mode: PaddingMode,
+ block_mode: BlockMode,
+ nonce: &mut Option<Vec<u8>>,
+ mac_len: Option<i32>,
+ key: &KeyDescriptor,
+) -> binder::Result<Option<Vec<u8>>> {
+ let mut op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::ENCRYPT)
+ .padding_mode(padding_mode)
+ .block_mode(block_mode);
+ if let Some(value) = nonce {
+ op_params = op_params.nonce(value.to_vec());
+ }
+
+ if let Some(val) = mac_len {
+ op_params = op_params.mac_length(val);
+ }
+
+ let op_response = sec_level.createOperation(key, &op_params, false)?;
+ assert!(op_response.iOperation.is_some());
+ let op = op_response.iOperation.unwrap();
+ if op_response.parameters.is_some() && nonce.is_none() {
+ *nonce = get_op_nonce(&op_response.parameters.unwrap());
+ }
+ op.finish(Some(SAMPLE_PLAIN_TEXT), None)
+}
+
+/// This performs sample decryption operation with given symmetric key (AES/3DES).
+pub fn perform_sample_sym_key_decrypt_op(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ input: &[u8],
+ padding_mode: PaddingMode,
+ block_mode: BlockMode,
+ nonce: &mut Option<Vec<u8>>,
+ mac_len: Option<i32>,
+ key: &KeyDescriptor,
+) -> binder::Result<Option<Vec<u8>>> {
+ let mut op_params = authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::DECRYPT)
+ .padding_mode(padding_mode)
+ .block_mode(block_mode);
+ if let Some(value) = nonce {
+ op_params = op_params.nonce(value.to_vec());
+ }
+
+ if let Some(val) = mac_len {
+ op_params = op_params.mac_length(val);
+ }
+
+ let op_response = sec_level.createOperation(key, &op_params, false)?;
+ assert!(op_response.iOperation.is_some());
+ let op = op_response.iOperation.unwrap();
+ op.finish(Some(input), None)
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 671b4ae..734faf6 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -12,1273 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use nix::unistd::{getuid, Gid, Uid};
-use rustutils::users::AID_USER_OFFSET;
-use serde::{Deserialize, Serialize};
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
- KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
-};
-use android_system_keystore2::aidl::android::system::keystore2::{
- CreateOperationResponse::CreateOperationResponse, Domain::Domain,
- IKeystoreOperation::IKeystoreOperation, KeyDescriptor::KeyDescriptor,
- KeyPermission::KeyPermission, ResponseCode::ResponseCode,
-};
-
-use keystore2_test_utils::authorizations;
-use keystore2_test_utils::get_keystore_service;
-use keystore2_test_utils::key_generations;
-use keystore2_test_utils::key_generations::Error;
-use keystore2_test_utils::run_as;
-
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
-enum TestOutcome {
- Ok,
- BackendBusy,
- InvalidHandle,
- OtherErr,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
-struct BarrierReached;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-struct ForcedOp(pub bool);
-
-/// Generate a EC_P256 key using given domain, namespace and alias.
-/// Create an operation using the generated key and perform sample signing operation.
-fn create_signing_operation(
- forced_op: ForcedOp,
- op_purpose: KeyPurpose,
- op_digest: Digest,
- domain: Domain,
- nspace: i64,
- alias: Option<String>,
-) -> binder::Result<CreateOperationResponse> {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level, domain, nspace, alias, None, None,
- )
- .unwrap();
-
- sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(op_purpose).digest(op_digest),
- forced_op.0,
- )
-}
-
-/// Generate an EC signing key and grant it to the user with given access vector.
-fn generate_ec_key_and_grant_to_user(
- grantee_uid: i32,
- access_vector: i32,
-) -> binder::Result<KeyDescriptor> {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("{}{}", "ks_grant_test_key_1", getuid());
-
- let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
- Domain::SELINUX,
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias),
- None,
- None,
- )
- .unwrap();
-
- keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
-}
-
-/// Performs sample signing operation.
-fn perform_sample_sign_operation(
- op: &binder::Strong<dyn IKeystoreOperation>,
-) -> Result<(), binder::Status> {
- op.update(b"my message")?;
- let sig = op.finish(None, None)?;
- assert!(sig.is_some());
- Ok(())
-}
-
-/// Create new operation on child proc and perform simple operation after parent notification.
-fn execute_op_run_as_child(
- target_ctx: &'static str,
- domain: Domain,
- nspace: i64,
- alias: Option<String>,
- auid: Uid,
- agid: Gid,
- forced_op: ForcedOp,
-) -> run_as::ChildHandle<TestOutcome, BarrierReached> {
- unsafe {
- run_as::run_as_child(target_ctx, auid, agid, move |reader, writer| {
- let result = key_generations::map_ks_error(create_signing_operation(
- forced_op,
- KeyPurpose::SIGN,
- Digest::SHA_2_256,
- domain,
- nspace,
- alias,
- ));
-
- // Let the parent know that an operation has been started, then
- // wait until the parent notifies us to continue, so the operation
- // remains open.
- writer.send(&BarrierReached {});
- reader.recv();
-
- // Continue performing the operation after parent notifies.
- match &result {
- Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
- match key_generations::map_ks_error(perform_sample_sign_operation(op)) {
- Ok(()) => TestOutcome::Ok,
- Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)) => {
- TestOutcome::InvalidHandle
- }
- Err(e) => panic!("Error in performing op: {:#?}", e),
- }
- }
- Ok(_) => TestOutcome::OtherErr,
- Err(Error::Rc(ResponseCode::BACKEND_BUSY)) => TestOutcome::BackendBusy,
- _ => TestOutcome::OtherErr,
- }
- })
- .expect("Failed to create an operation.")
- }
-}
-
-fn create_operations(
- target_ctx: &'static str,
- forced_op: ForcedOp,
- max_ops: i32,
-) -> Vec<run_as::ChildHandle<TestOutcome, BarrierReached>> {
- let alias = format!("ks_op_test_key_{}", getuid());
- let base_gid = 99 * AID_USER_OFFSET + 10001;
- let base_uid = 99 * AID_USER_OFFSET + 10001;
- (0..max_ops)
- .into_iter()
- .map(|i| {
- execute_op_run_as_child(
- target_ctx,
- Domain::APP,
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias.to_string()),
- Uid::from_raw(base_uid + (i as u32)),
- Gid::from_raw(base_gid + (i as u32)),
- forced_op,
- )
- })
- .collect()
-}
-
-/// This test verifies that backend service throws BACKEND_BUSY error when all
-/// operations slots are full. This test creates operations in child processes and
-/// collects the status of operations performed in each child proc and determines
-/// whether any child proc exited with error status.
-#[test]
-fn keystore2_backend_busy_test() {
- const MAX_OPS: i32 = 100;
- static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
-
- let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
-
- // Wait until all child procs notifies us to continue,
- // so that there are definitely enough operations outstanding to trigger a BACKEND_BUSY.
- for ch in child_handles.iter_mut() {
- ch.recv();
- }
- // Notify each child to resume and finish.
- for ch in child_handles.iter_mut() {
- ch.send(&BarrierReached {});
- }
-
- // Collect the result and validate whether backend busy has occurred.
- let mut busy_count = 0;
- for ch in child_handles.into_iter() {
- if ch.get_result() == TestOutcome::BackendBusy {
- busy_count += 1;
- }
- }
- assert!(busy_count > 0)
-}
-
-/// This test confirms that forced operation is having high pruning power.
-/// 1. Initially create regular operations such that there are enough operations outstanding
-/// to trigger BACKEND_BUSY.
-/// 2. Then, create a forced operation. System should be able to prune one of the regular
-/// operations and create a slot for forced operation successfully.
-#[test]
-fn keystore2_forced_op_after_backendbusy_test() {
- const MAX_OPS: i32 = 100;
- static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
-
- // Create regular operations.
- let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
-
- // Wait until all child procs notifies us to continue, so that there are enough
- // operations outstanding to trigger a BACKEND_BUSY.
- for ch in child_handles.iter_mut() {
- ch.recv();
- }
-
- // Create a forced operation.
- let auid = 99 * AID_USER_OFFSET + 10604;
- let agid = 99 * AID_USER_OFFSET + 10604;
- unsafe {
- run_as::run_as(
- key_generations::TARGET_VOLD_CTX,
- Uid::from_raw(auid),
- Gid::from_raw(agid),
- move || {
- let alias = format!("ks_prune_forced_op_key_{}", getuid());
-
- // To make room for this forced op, system should be able to prune one of the
- // above created regular operations and create a slot for this forced operation
- // successfully.
- create_signing_operation(
- ForcedOp(true),
- KeyPurpose::SIGN,
- Digest::SHA_2_256,
- Domain::SELINUX,
- 100,
- Some(alias),
- )
- .expect("Client failed to create forced operation after BACKEND_BUSY state.");
- },
- );
- };
-
- // Notify each child to resume and finish.
- for ch in child_handles.iter_mut() {
- ch.send(&BarrierReached {});
- }
-
- // Collect the results of above created regular operations.
- let mut pruned_count = 0;
- let mut busy_count = 0;
- let mut _other_err = 0;
- for ch in child_handles.into_iter() {
- match ch.get_result() {
- TestOutcome::BackendBusy => {
- busy_count += 1;
- }
- TestOutcome::InvalidHandle => {
- pruned_count += 1;
- }
- _ => {
- _other_err += 1;
- }
- }
- }
- // Verify that there should be at least one backend busy has occurred while creating
- // above regular operations.
- assert!(busy_count > 0);
-
- // Verify that there should be at least one pruned operation which should have failed while
- // performing operation.
- assert!(pruned_count > 0);
-}
-
-/// This test confirms that forced operations can't be pruned.
-/// 1. Creates an initial forced operation and tries to complete the operation after BACKEND_BUSY
-/// error is triggered.
-/// 2. Create MAX_OPS number of forced operations so that definitely enough number of operations
-/// outstanding to trigger a BACKEND_BUSY.
-/// 3. Try to use initially created forced operation (in step #1) and able to perform the
-/// operation successfully. This confirms that none of the later forced operations evicted the
-/// initial forced operation.
-#[test]
-fn keystore2_max_forced_ops_test() {
- const MAX_OPS: i32 = 100;
- let auid = 99 * AID_USER_OFFSET + 10205;
- let agid = 99 * AID_USER_OFFSET + 10205;
-
- // Create initial forced operation in a child process
- // and wait for the parent to notify to perform operation.
- let alias = format!("ks_forced_op_key_{}", getuid());
- let mut first_op_handle = execute_op_run_as_child(
- key_generations::TARGET_SU_CTX,
- Domain::SELINUX,
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias),
- Uid::from_raw(auid),
- Gid::from_raw(agid),
- ForcedOp(true),
- );
-
- // Wait until above child proc notifies us to continue, so that there is definitely a forced
- // operation outstanding to perform a operation.
- first_op_handle.recv();
-
- // Create MAX_OPS number of forced operations.
- let mut child_handles =
- create_operations(key_generations::TARGET_SU_CTX, ForcedOp(true), MAX_OPS);
-
- // Wait until all child procs notifies us to continue, so that there are enough operations
- // outstanding to trigger a BACKEND_BUSY.
- for ch in child_handles.iter_mut() {
- ch.recv();
- }
-
- // Notify initial created forced operation to continue performing the operations.
- first_op_handle.send(&BarrierReached {});
-
- // Collect initially created forced operation result and is expected to complete operation
- // successfully.
- let first_op_result = first_op_handle.get_result();
- assert_eq!(first_op_result, TestOutcome::Ok);
-
- // Notify each child to resume and finish.
- for ch in child_handles.iter_mut() {
- ch.send(&BarrierReached {});
- }
-
- // Collect the result and validate whether backend busy has occurred with MAX_OPS number
- // of forced operations.
- let busy_count = child_handles
- .into_iter()
- .map(|ch| ch.get_result())
- .filter(|r| *r == TestOutcome::BackendBusy)
- .count();
- assert!(busy_count > 0);
-}
-
-/// This test will verify the use case with the same owner(UID) requesting `n` number of operations.
-/// This test confirms that when all operation slots are full and a new operation is requested,
-/// an operation which is least recently used and lived longest will be pruned to make a room
-/// for a new operation. Pruning strategy should prevent the operations of the other owners(UID)
-/// from being pruned.
-///
-/// 1. Create an operation in a child process with `untrusted_app` context and wait for parent
-/// notification to complete the operation.
-/// 2. Let parent process create `n` number of operations such that there are enough operations
-/// outstanding to trigger cannibalizing their own sibling operations.
-/// 3. Sequentially try to use above created `n` number of operations and also add a new operation,
-/// so that it should trigger cannibalizing one of their own sibling operations.
-/// 3.1 While trying to use these pruned operations an `INVALID_OPERATION_HANDLE` error is
-/// expected as they are already pruned.
-/// 4. Notify the child process to resume and complete the operation. It is expected to complete the
-/// operation successfully.
-/// 5. Try to use the latest operation of parent. It is expected to complete the operation
-/// successfully.
-#[test]
-fn keystore2_ops_prune_test() {
- const MAX_OPS: usize = 40; // This should be at least 32 with sec_level TEE.
-
- static TARGET_CTX: &str = "u:r:untrusted_app:s0";
- const USER_ID: u32 = 99;
- const APPLICATION_ID: u32 = 10601;
-
- let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
- let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
-
- // Create an operation in an untrusted_app context. Wait until the parent notifies to continue.
- // Once the parent notifies, this operation is expected to be completed successfully.
- let alias = format!("ks_reg_op_key_{}", getuid());
- let mut child_handle = execute_op_run_as_child(
- TARGET_CTX,
- Domain::APP,
- -1,
- Some(alias),
- Uid::from_raw(uid),
- Gid::from_raw(gid),
- ForcedOp(false),
- );
-
- // Wait until child process notifies us to continue, so that an operation from child process is
- // outstanding to complete the operation.
- child_handle.recv();
-
- // Generate a key to use in below operations.
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("ks_prune_op_test_key_{}", getuid());
- let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
- Domain::SELINUX,
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias),
- None,
- None,
- )
- .unwrap();
-
- // Create multiple operations in this process to trigger cannibalizing sibling operations.
- let mut ops: Vec<binder::Result<CreateOperationResponse>> = (0..MAX_OPS)
- .into_iter()
- .map(|_| {
- sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new()
- .purpose(KeyPurpose::SIGN)
- .digest(Digest::SHA_2_256),
- false,
- )
- })
- .collect();
-
- // Sequentially try to use operation handles created above and also add a new operation.
- for vec_index in 0..MAX_OPS {
- match &ops[vec_index] {
- Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
- // Older operation handle is pruned, if we try to use that an error is expected.
- assert_eq!(
- Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
- key_generations::map_ks_error(op.update(b"my message"))
- );
- }
- _ => panic!("Operation should have created successfully."),
- }
-
- // Create a new operation, it should trigger to cannibalize one of their own sibling
- // operations.
- ops.push(
- sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new()
- .purpose(KeyPurpose::SIGN)
- .digest(Digest::SHA_2_256),
- false,
- ),
- );
- }
-
- // Notify child process to continue the operation.
- child_handle.send(&BarrierReached {});
- assert!((child_handle.get_result() == TestOutcome::Ok), "Failed to perform an operation");
-
- // Try to use the latest operation created by parent, should be able to use it successfully.
- match ops.last() {
- Some(Ok(CreateOperationResponse { iOperation: Some(op), .. })) => {
- assert_eq!(Ok(()), key_generations::map_ks_error(perform_sample_sign_operation(op)));
- }
- _ => panic!("Operation should have created successfully."),
- }
-}
-
-/// This test will try to load the key with Domain::BLOB.
-/// INVALID_ARGUMENT error is expected.
-#[test]
-fn keystore2_get_key_entry_blob_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- // Generate a key with domain as BLOB.
- let key_metadata = key_generations::generate_ec_p256_signing_key(
- &sec_level,
- Domain::BLOB,
- key_generations::SELINUX_SHELL_NAMESPACE,
- None,
- None,
- None,
- )
- .unwrap();
-
- // Try to load the key using above generated KeyDescriptor.
- let result = key_generations::map_ks_error(keystore2.getKeyEntry(&key_metadata.key));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
-
- // Delete the generated key blob.
- sec_level.deleteKey(&key_metadata.key).unwrap();
-}
-
-/// Try to create forced operations with various contexts -
-/// - untrusted_app
-/// - system_server
-/// - priv_app
-/// `PERMISSION_DENIED` error response is expected.
-#[test]
-fn keystore2_forced_op_perm_denied_test() {
- static TARGET_CTXS: &[&str] =
- &["u:r:untrusted_app:s0", "u:r:system_server:s0", "u:r:priv_app:s0"];
- const USER_ID: u32 = 99;
- const APPLICATION_ID: u32 = 10601;
-
- let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
- let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
-
- for context in TARGET_CTXS.iter() {
- unsafe {
- run_as::run_as(context, Uid::from_raw(uid), Gid::from_raw(gid), move || {
- let alias = format!("ks_app_forced_op_test_key_{}", getuid());
- let result = key_generations::map_ks_error(create_signing_operation(
- ForcedOp(true),
- KeyPurpose::SIGN,
- Digest::SHA_2_256,
- Domain::APP,
- -1,
- Some(alias),
- ));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
- });
- }
- }
-}
-
-/// Try to create a forced operation with `vold` context.
-/// Should be able to create forced operation with `vold` context successfully.
-#[test]
-fn keystore2_forced_op_success_test() {
- static TARGET_CTX: &str = "u:r:vold:s0";
- const USER_ID: u32 = 99;
- const APPLICATION_ID: u32 = 10601;
-
- let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
- let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
-
- unsafe {
- run_as::run_as(TARGET_CTX, Uid::from_raw(uid), Gid::from_raw(gid), move || {
- let alias = format!("ks_vold_forced_op_key_{}", getuid());
- create_signing_operation(
- ForcedOp(true),
- KeyPurpose::SIGN,
- Digest::SHA_2_256,
- Domain::SELINUX,
- key_generations::SELINUX_VOLD_NAMESPACE,
- Some(alias),
- )
- .expect("Client with vold context failed to create forced operation.");
- });
- }
-}
-
-/// Try to grant a key with permission that does not map to any of the `KeyPermission` values.
-/// An error is expected with values that does not map to set of permissions listed in
-/// `KeyPermission`.
-#[test]
-fn keystore2_grant_key_with_invalid_perm_expecting_syserror() {
- const USER_ID: u32 = 99;
- const APPLICATION_ID: u32 = 10001;
- let grantee_uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
- let invalid_access_vector = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0 << 19;
-
- let result = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
- grantee_uid.try_into().unwrap(),
- invalid_access_vector,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::SYSTEM_ERROR), result.unwrap_err());
-}
-
-/// Try to grant a key with empty access vector `KeyPermission::NONE`, should be able to grant a
-/// key with empty access vector successfully. In grantee context try to use the granted key, it
-/// should fail to load the key with permission denied error.
-#[test]
-fn keystore2_grant_key_with_perm_none() {
- static TARGET_SU_CTX: &str = "u:r:su:s0";
-
- static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
- const USER_ID: u32 = 99;
- const APPLICATION_ID: u32 = 10001;
- static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
- static GRANTEE_GID: u32 = GRANTEE_UID;
-
- let grant_key_nspace = unsafe {
- run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let empty_access_vector = KeyPermission::NONE.0;
-
- let grant_key = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
- GRANTEE_UID.try_into().unwrap(),
- empty_access_vector,
- ))
- .unwrap();
-
- assert_eq!(grant_key.domain, Domain::GRANT);
-
- grant_key.nspace
- })
- };
-
- // In grantee context try to load the key, it should fail to load the granted key as it is
- // granted with empty access vector.
- unsafe {
- run_as::run_as(
- GRANTEE_CTX,
- Uid::from_raw(GRANTEE_UID),
- Gid::from_raw(GRANTEE_GID),
- move || {
- let keystore2 = get_keystore_service();
-
- let result = key_generations::map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
- domain: Domain::GRANT,
- nspace: grant_key_nspace,
- alias: None,
- blob: None,
- }));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
- },
- )
- };
-}
-
-/// Grant a key to the user (grantee) with `GET_INFO|USE` key permissions. Verify whether grantee
-/// can succeed in loading the granted key and try to perform simple operation using this granted
-/// key. Grantee should be able to load the key and use the key to perform crypto operation
-/// successfully. Try to delete the granted key in grantee context where it is expected to fail to
-/// delete it as `DELETE` permission is not granted.
-#[test]
-fn keystore2_grant_get_info_use_key_perm() {
- static TARGET_SU_CTX: &str = "u:r:su:s0";
-
- static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
- const USER_ID: u32 = 99;
- const APPLICATION_ID: u32 = 10001;
- static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
- static GRANTEE_GID: u32 = GRANTEE_UID;
-
- // Generate a key and grant it to a user with GET_INFO|USE key permissions.
- let grant_key_nspace = unsafe {
- run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
- let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
- let grant_key = key_generations::map_ks_error(generate_ec_key_and_grant_to_user(
- GRANTEE_UID.try_into().unwrap(),
- access_vector,
- ))
- .unwrap();
-
- assert_eq!(grant_key.domain, Domain::GRANT);
-
- grant_key.nspace
- })
- };
-
- // In grantee context load the key and try to perform crypto operation.
- unsafe {
- run_as::run_as(
- GRANTEE_CTX,
- Uid::from_raw(GRANTEE_UID),
- Gid::from_raw(GRANTEE_GID),
- move || {
- let keystore2 = get_keystore_service();
- let sec_level =
- keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- // Load the granted key.
- let key_entry_response = keystore2
- .getKeyEntry(&KeyDescriptor {
- domain: Domain::GRANT,
- nspace: grant_key_nspace,
- alias: None,
- blob: None,
- })
- .unwrap();
-
- // Perform sample crypto operation using granted key.
- let op_response = sec_level
- .createOperation(
- &key_entry_response.metadata.key,
- &authorizations::AuthSetBuilder::new()
- .purpose(KeyPurpose::SIGN)
- .digest(Digest::SHA_2_256),
- false,
- )
- .unwrap();
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
-
- // Try to delete the key, it is expected to be fail with permission denied error.
- let result = key_generations::map_ks_error(keystore2.deleteKey(&KeyDescriptor {
- domain: Domain::GRANT,
- nspace: grant_key_nspace,
- alias: None,
- blob: None,
- }));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
- },
- )
- };
-}
-
-/// Try to generate a key with invalid Domain. `INVALID_ARGUMENT` error response is expected.
-#[test]
-fn keystore2_generate_key_invalid_domain() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("ks_invalid_test_key_{}", getuid());
-
- let result = key_generations::map_ks_error(key_generations::generate_ec_key(
- &*sec_level,
- Domain(99), // Invalid domain.
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias),
- EcCurve::P_256,
- Digest::SHA_2_256,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
-}
-
-/// Try to generate a EC key without providing the curve.
-/// `UNSUPPORTED_EC_CURVE or UNSUPPORTED_KEY_SIZE` error response is expected.
-#[test]
-fn keystore2_generate_ec_key_missing_curve() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
-
- // Don't provide EC curve.
- let gen_params = authorizations::AuthSetBuilder::new()
- .no_auth_required()
- .algorithm(Algorithm::EC)
- .purpose(KeyPurpose::SIGN)
- .purpose(KeyPurpose::VERIFY)
- .digest(Digest::SHA_2_256);
-
- let result = key_generations::map_ks_error(sec_level.generateKey(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: key_generations::SELINUX_SHELL_NAMESPACE,
- alias: Some(alias),
- blob: None,
- },
- None,
- &gen_params,
- 0,
- b"entropy",
- ));
- assert!(result.is_err());
- let err = result.unwrap_err();
- assert!(matches!(
- err,
- Error::Km(ErrorCode::UNSUPPORTED_EC_CURVE) | Error::Km(ErrorCode::UNSUPPORTED_KEY_SIZE)
- ));
-}
-
-/// Try to generate a EC key with curve `CURVE_25519` having `SIGN and AGREE_KEY` purposes.
-/// `INCOMPATIBLE_PURPOSE` error response is expected.
-#[test]
-fn keystore2_generate_ec_key_25519_multi_purpose() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("ks_ec_no_curve_test_key_{}", getuid());
-
- // Specify `SIGN and AGREE_KEY` purposes.
- let gen_params = authorizations::AuthSetBuilder::new()
- .no_auth_required()
- .algorithm(Algorithm::EC)
- .ec_curve(EcCurve::CURVE_25519)
- .purpose(KeyPurpose::SIGN)
- .purpose(KeyPurpose::AGREE_KEY)
- .digest(Digest::SHA_2_256);
-
- let result = key_generations::map_ks_error(sec_level.generateKey(
- &KeyDescriptor {
- domain: Domain::SELINUX,
- nspace: key_generations::SELINUX_SHELL_NAMESPACE,
- alias: Some(alias),
- blob: None,
- },
- None,
- &gen_params,
- 0,
- b"entropy",
- ));
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
-}
-
-/// Generate EC keys with curves EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521 and
-/// various digest modes. Try to create operations using generated keys. Operations with digest
-/// modes `SHA1, SHA-2 224, SHA-2 256, SHA-2 384 and SHA-2 512` should be created successfully.
-/// Creation of operations with digest modes NONE and MD5 should fail with an error code
-/// `UNSUPPORTED_DIGEST`.
-#[test]
-fn keystore2_ec_generate_key() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let digests = [
- Digest::NONE,
- Digest::MD5,
- Digest::SHA1,
- Digest::SHA_2_224,
- Digest::SHA_2_256,
- Digest::SHA_2_384,
- Digest::SHA_2_512,
- ];
-
- let ec_curves = [EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521];
-
- for ec_curve in ec_curves {
- for digest in digests {
- let alias = format!("ks_ec_test_key_gen_{}{}{}", getuid(), ec_curve.0, digest.0);
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- ec_curve,
- digest,
- )
- .unwrap();
-
- match key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
- false,
- )) {
- Ok(op_response) => {
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
- }
- Err(e) => {
- assert_eq!(e, Error::Km(ErrorCode::UNSUPPORTED_DIGEST));
- assert!(digest == Digest::NONE || digest == Digest::MD5);
- }
- }
- }
- }
-}
-
-/// Generate EC key with curve `CURVE_25519` and digest mode NONE. Try to create an operation using
-/// generated key. `CURVE_25519` key should support `Digest::NONE` digest mode and test should be
-/// able to create an operation successfully.
-#[test]
-fn keystore2_ec_25519_generate_key_success() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let alias = format!("ks_ec_25519_none_test_key_gen_{}", getuid());
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias),
- EcCurve::CURVE_25519,
- Digest::NONE,
- )
- .unwrap();
-
- let op_response = sec_level
- .createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::NONE),
- false,
- )
- .unwrap();
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
-}
-
-/// Generate EC keys with curve `CURVE_25519` and digest modes `MD5, SHA1, SHA-2 224, SHA-2 256,
-/// SHA-2 384 and SHA-2 512`. Try to create operations using generated keys. `CURVE_25519` keys
-/// shouldn't support these digest modes. Test should fail to create operations with an error
-/// `UNSUPPORTED_DIGEST`.
-#[test]
-fn keystore2_ec_25519_generate_key_fail() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let digests = [
- Digest::MD5,
- Digest::SHA1,
- Digest::SHA_2_224,
- Digest::SHA_2_256,
- Digest::SHA_2_384,
- Digest::SHA_2_512,
- ];
-
- for digest in digests {
- let alias = format!("ks_ec_25519_test_key_gen_{}{}", getuid(), digest.0);
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- EcCurve::CURVE_25519,
- digest,
- )
- .unwrap();
-
- let result = key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
- false,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
- }
-}
-
-/// Generate a EC key with `SHA_2_256` digest mode. Try to create an operation with digest mode
-/// other than `SHA_2_256`. Creation of an operation with generated key should fail with
-/// `INCOMPATIBLE_DIGEST` error as there is a mismatch of digest mode in key authorizations.
-#[test]
-fn keystore2_create_op_with_incompatible_key_digest() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let alias = "ks_ec_test_incomp_key_digest";
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .unwrap();
-
- let digests =
- [Digest::NONE, Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_384, Digest::SHA_2_512];
-
- for digest in digests {
- let result = key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
- false,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_DIGEST), result.unwrap_err());
- }
-}
-
-/// Generate a key in client#1 and try to use it in other client#2.
-/// Client#2 should fail to load the key as the it doesn't own the client#1 generated key.
-#[test]
-fn keystore2_key_owner_validation() {
- static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
- const USER_ID: u32 = 99;
- const APPLICATION_ID_1: u32 = 10601;
-
- let uid1 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_1;
- let gid1 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_1;
- let alias = "ks_owner_check_test_key";
-
- // Client#1: Generate a key and create an operation using generated key.
- // Wait until the parent notifies to continue. Once the parent notifies, this operation
- // is expected to be completed successfully.
- let mut child_handle = execute_op_run_as_child(
- TARGET_CTX,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- Uid::from_raw(uid1),
- Gid::from_raw(gid1),
- ForcedOp(false),
- );
-
- // Wait until (client#1) child process notifies us to continue, so that there will be a key
- // generated by client#1.
- child_handle.recv();
-
- // Client#2: This child will try to load the key generated by client#1.
- const APPLICATION_ID_2: u32 = 10602;
- let uid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
- let gid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
- unsafe {
- run_as::run_as(TARGET_CTX, Uid::from_raw(uid2), Gid::from_raw(gid2), move || {
- let keystore2_inst = get_keystore_service();
- let result =
- key_generations::map_ks_error(keystore2_inst.getKeyEntry(&KeyDescriptor {
- domain: Domain::APP,
- nspace: -1,
- alias: Some(alias.to_string()),
- blob: None,
- }));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
- });
- };
-
- // Notify the child process (client#1) to resume and finish.
- child_handle.send(&BarrierReached {});
- assert!(
- (child_handle.get_result() == TestOutcome::Ok),
- "Client#1 failed to complete the operation."
- );
-}
-
-/// Generate EC key with BLOB as domain. Generated key should be returned to caller as key blob.
-/// Verify that `blob` field in the `KeyDescriptor` is not empty and should have the key blob.
-/// Try to use this key for performing a sample operation and the operation should complete
-/// successfully.
-#[test]
-fn keystore2_generate_key_with_blob_domain() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::BLOB,
- key_generations::SELINUX_SHELL_NAMESPACE,
- None,
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .unwrap();
-
- assert!(key_metadata.certificate.is_some());
- assert!(key_metadata.certificateChain.is_none());
-
- // Must have the key blob.
- assert!(key_metadata.key.blob.is_some());
-
- let op_response = key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
- false,
- ))
- .unwrap();
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
-
- // Delete the generated key blob.
- sec_level.deleteKey(&key_metadata.key).unwrap();
-}
-
-/// Try to generate a key with `Domain::KEY_ID`, test should fail with an error code
-/// `SYSTEM_ERROR`. `Domain::KEY_ID` is not allowed to use for generating a key. Key id is returned
-/// by Keystore2 after a key has been mapped from an alias.
-#[test]
-fn keystore2_generate_key_with_key_id_domain_expect_sys_error() {
- let alias = "ks_gen_key_id_test_key";
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
-
- let result = key_generations::map_ks_error(key_generations::generate_ec_key(
- &*sec_level,
- Domain::KEY_ID,
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias.to_string()),
- EcCurve::P_256,
- Digest::SHA_2_256,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::SYSTEM_ERROR), result.unwrap_err());
-}
-
-/// Generate a key and try to load the generated key using KEY_ID as domain. Create an
-/// operation using key which is loaded with domain as KEY_ID. Test should create an operation
-/// successfully.
-#[test]
-fn keystore2_find_key_with_key_id_as_domain() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = "ks_key_id_test_key";
-
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .expect("Failed to generate a EC key.");
-
- // Try to load the above generated key with KEY_ID as domain.
- let key_entry_response = keystore2
- .getKeyEntry(&KeyDescriptor {
- domain: Domain::KEY_ID,
- nspace: key_metadata.key.nspace,
- alias: Some(alias.to_string()),
- blob: None,
- })
- .expect("Error in getKeyEntry to load a key with domain KEY_ID.");
-
- // Verify above found key is same the one generated.
- assert_eq!(key_metadata.key, key_entry_response.metadata.key);
- assert_eq!(key_metadata.certificate, key_entry_response.metadata.certificate);
- assert_eq!(key_metadata.certificateChain, key_entry_response.metadata.certificateChain);
- assert_eq!(key_metadata.key.nspace, key_entry_response.metadata.key.nspace);
-
- // Try to create an operation using above loaded key, operation should be created
- // successfully.
- let op_response = sec_level
- .createOperation(
- &key_entry_response.metadata.key,
- &authorizations::AuthSetBuilder::new()
- .purpose(KeyPurpose::SIGN)
- .digest(Digest::SHA_2_256),
- false,
- )
- .expect("Error in creation of operation.");
-
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
-}
-
-/// Generate a key with an alias. Generate another key and bind it to the same alias.
-/// Try to create an operation using previously generated key. Creation of an operation should
-/// fail because previously generated key material is no longer accessible. Test should successfully
-/// create an operation using the rebound key.
-#[test]
-fn keystore2_key_id_alias_rebind_verify_by_alias() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("ks_key_id_test_alias_rebind_1_{}", getuid());
-
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .expect("Failed to generate a EC key.");
-
- // Generate a key with same alias as above generated key, so that alias will be rebound
- // to this key.
- let new_key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias),
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .expect("Failed to generate a rebound EC key.");
-
- assert_ne!(key_metadata.key, new_key_metadata.key);
- assert_ne!(key_metadata.certificate, new_key_metadata.certificate);
- assert_ne!(key_metadata.key.nspace, new_key_metadata.key.nspace);
-
- // Try to create an operation using previously generated key_metadata.
- // It should fail as previously generated key material is no longer remains valid.
- let result = key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
- false,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
-
- // Try to create an operation using rebound key, operation should be created
- // successfully.
- let op_response = sec_level
- .createOperation(
- &new_key_metadata.key,
- &authorizations::AuthSetBuilder::new()
- .purpose(KeyPurpose::SIGN)
- .digest(Digest::SHA_2_256),
- false,
- )
- .expect("Error in creation of operation using rebound key.");
-
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
-}
-
-/// Generate a key with an alias. Load the generated key with `Domain::KEY_ID`. Generate another
-/// key and bind it to the same alias. Try to create an operation using the key loaded with domain
-/// `KEY_ID`. Creation of an operation should fail because originally loaded key no longer exists.
-/// Test should successfully create an operation using the rebound key.
-#[test]
-fn keystore2_key_id_alias_rebind_verify_by_key_id() {
- let keystore2 = get_keystore_service();
- let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
- let alias = format!("ks_key_id_test_alias_rebind_2_{}", getuid());
-
- let key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .expect("Failed to generate a EC key.");
-
- // Load the above generated key with KEY_ID as domain.
- let key_entry_response = keystore2
- .getKeyEntry(&KeyDescriptor {
- domain: Domain::KEY_ID,
- nspace: key_metadata.key.nspace,
- alias: Some(alias.to_string()),
- blob: None,
- })
- .expect("Error in getKeyEntry to load a key with domain KEY_ID.");
-
- // Verify above found key is same the one generated.
- assert_eq!(key_metadata.key, key_entry_response.metadata.key);
- assert_eq!(key_metadata.certificate, key_entry_response.metadata.certificate);
- assert_eq!(key_metadata.certificateChain, key_entry_response.metadata.certificateChain);
- assert_eq!(key_metadata.key.nspace, key_entry_response.metadata.key.nspace);
-
- // Generate another key with same alias as above generated key, so that alias will be rebound
- // to this key.
- let new_key_metadata = key_generations::generate_ec_key(
- &*sec_level,
- Domain::APP,
- -1,
- Some(alias),
- EcCurve::P_256,
- Digest::SHA_2_256,
- )
- .expect("Failed to generate a rebound EC key.");
-
- // Verify that an alias is rebound to a new key.
- assert_eq!(key_metadata.key.alias, new_key_metadata.key.alias);
- assert_ne!(key_metadata.key, new_key_metadata.key);
- assert_ne!(key_metadata.certificate, new_key_metadata.certificate);
- assert_ne!(key_metadata.key.nspace, new_key_metadata.key.nspace);
-
- // Try to create an operation using previously loaded key_entry_response.
- // It should fail as previously generated key material is no longer valid.
- let result = key_generations::map_ks_error(sec_level.createOperation(
- &key_entry_response.metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
- false,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
-
- // Try to create an operation using rebound key, operation should be created
- // successfully.
- let op_response = sec_level
- .createOperation(
- &new_key_metadata.key,
- &authorizations::AuthSetBuilder::new()
- .purpose(KeyPurpose::SIGN)
- .digest(Digest::SHA_2_256),
- false,
- )
- .expect("Error in creation of operation using rebound key.");
-
- assert!(op_response.iOperation.is_some());
- assert_eq!(
- Ok(()),
- key_generations::map_ks_error(perform_sample_sign_operation(
- &op_response.iOperation.unwrap()
- ))
- );
-}
+pub mod keystore2_client_3des_key_tests;
+pub mod keystore2_client_aes_key_tests;
+pub mod keystore2_client_ec_key_tests;
+pub mod keystore2_client_grant_key_tests;
+pub mod keystore2_client_key_id_domain_tests;
+pub mod keystore2_client_list_entries_tests;
+pub mod keystore2_client_operation_tests;
+pub mod keystore2_client_rsa_key_tests;
+pub mod keystore2_client_test_utils;