Migrate off keystore
Implement our own keypair generation and signing (using BoringSSL) and
our own private key blob protection (using Ring). This includes
replacing the old compos_key_service with the new signing_key.
Use DICE as the source of the VM secret used to protect the private
key instead of assuming keystore has one.
Changed compsvc to return the RSAPublicKey directly. Previously we
returned the self-signed cert from Keystore, and composd then
extracted the public key. As a result composd no longer needs any
native helper code to call BoringSSL; however now compsvc does.
Removed similarly redundant key-extraction code from compos_key_cmd.
Create SystemRandom when we need it rather than having it as a field;
it's stateless anyway.
Bug: 214233409
Test: atest ComposKeyTestCase compsvc_device_tests
Change-Id: I8b14fe2acdf43f49d45e2d32d4b6f482bd420eee
diff --git a/compos/src/artifact_signer.rs b/compos/src/artifact_signer.rs
index a4b47d6..e1b0efa 100644
--- a/compos/src/artifact_signer.rs
+++ b/compos/src/artifact_signer.rs
@@ -19,8 +19,8 @@
#![allow(dead_code)] // Will be used soon
-use crate::compos_key_service::Signer;
use crate::fsverity;
+use crate::signing_key::Signer;
use anyhow::{anyhow, Context, Result};
use odsign_proto::odsign_info::OdsignInfo;
use protobuf::Message;
diff --git a/compos/src/blob_encryption.rs b/compos/src/blob_encryption.rs
new file mode 100644
index 0000000..0db09ba
--- /dev/null
+++ b/compos/src/blob_encryption.rs
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+//! Allows for data to be encrypted and authenticated (AEAD) with a key derived from some secret.
+//! The encrypted blob can be passed to the untrusted host without revealing the encrypted data
+//! but with the key the data can be retrieved as long as the blob has not been tampered with.
+
+use anyhow::{bail, Context, Result};
+use ring::{
+ aead::{Aad, LessSafeKey, Nonce, AES_256_GCM, NONCE_LEN},
+ hkdf::{Salt, HKDF_SHA256},
+ rand::{SecureRandom, SystemRandom},
+};
+
+// Non-secret input to the AEAD key derivation
+const KDF_INFO: &[u8] = b"CompOS blob sealing key";
+
+pub fn derive_aead_key(input_keying_material: &[u8]) -> Result<LessSafeKey> {
+ // Derive key using HKDF - see https://datatracker.ietf.org/doc/html/rfc5869#section-2
+ let salt = [];
+ let prk = Salt::new(HKDF_SHA256, &salt).extract(input_keying_material);
+ let okm = prk.expand(&[KDF_INFO], &AES_256_GCM).context("HKDF failed")?;
+ // LessSafeKey is only less safe in that it has less inherent protection against nonce
+ // reuse; we are safe because we use a new random nonce for each sealing operation.
+ // (See https://github.com/briansmith/ring/issues/899.)
+ Ok(LessSafeKey::new(okm.into()))
+}
+
+pub fn encrypt_bytes(key: LessSafeKey, bytes: &[u8]) -> Result<Vec<u8>> {
+ let mut output = Vec::with_capacity(bytes.len() + NONCE_LEN + key.algorithm().tag_len());
+
+ // Generate a unique nonce, since we may use the same key more than once, and put it at the
+ // start of the output blob.
+ let mut nonce_bytes = [0u8; NONCE_LEN];
+ SystemRandom::new().fill(&mut nonce_bytes).context("Failed to generate random nonce")?;
+ output.extend_from_slice(&nonce_bytes);
+
+ // Copy input to output then encrypt & seal it in place.
+ output.extend_from_slice(bytes);
+ let nonce = Nonce::assume_unique_for_key(nonce_bytes);
+ let tag = key
+ .seal_in_place_separate_tag(nonce, Aad::empty(), &mut output[NONCE_LEN..])
+ .context("Failed to seal blob")?;
+ output.extend_from_slice(tag.as_ref());
+
+ Ok(output)
+}
+
+pub fn decrypt_bytes(key: LessSafeKey, bytes: &[u8]) -> Result<Vec<u8>> {
+ if bytes.len() < NONCE_LEN + key.algorithm().tag_len() {
+ bail!("Encrypted blob is too small");
+ }
+
+ // We expect the nonce at the start followed by the sealed data (encrypted data +
+ // authentication tag).
+ let nonce = Nonce::try_assume_unique_for_key(&bytes[..NONCE_LEN]).unwrap();
+ let mut output = bytes[NONCE_LEN..].to_vec();
+
+ // Verify & decrypt the data in place
+ let unsealed_size =
+ key.open_in_place(nonce, Aad::empty(), &mut output).context("Failed to unseal blob")?.len();
+
+ // Remove the tag after the plaintext
+ output.truncate(unsealed_size);
+
+ Ok(output)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_round_trip_data() -> Result<()> {
+ let input_keying_material = b"Key is derived from this";
+ let original_bytes = b"This is the secret data";
+
+ let key = derive_aead_key(input_keying_material)?;
+ let blob = encrypt_bytes(key, original_bytes)?;
+
+ let key = derive_aead_key(input_keying_material)?;
+ let decoded_bytes = decrypt_bytes(key, &blob)?;
+
+ assert_eq!(decoded_bytes, original_bytes);
+ Ok(())
+ }
+
+ #[test]
+ fn test_modified_data_detected() -> Result<()> {
+ let input_keying_material = b"Key is derived from this";
+ let original_bytes = b"This is the secret data";
+
+ let key = derive_aead_key(input_keying_material)?;
+ let mut blob = encrypt_bytes(key, original_bytes)?;
+
+ // Flip a bit.
+ blob[0] ^= 1;
+
+ let key = derive_aead_key(input_keying_material)?;
+ let decoded_bytes = decrypt_bytes(key, &blob);
+
+ assert!(decoded_bytes.is_err());
+ Ok(())
+ }
+}
diff --git a/compos/src/blob_encryptor.rs b/compos/src/blob_encryptor.rs
deleted file mode 100644
index ed4484e..0000000
--- a/compos/src/blob_encryptor.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.
- */
-
-//! Allows for data to be encrypted and authenticated (AEAD) with a key derived from some secret.
-//! The encrypted blob can be passed to the untrusted host without revealing the encrypted data
-//! but with the key the data can be retrieved as long as the blob has not been tampered with.
-
-use anyhow::{bail, Context, Result};
-use ring::{
- aead::{Aad, LessSafeKey, Nonce, AES_256_GCM, NONCE_LEN},
- hkdf::{Salt, HKDF_SHA256},
- rand::{SecureRandom, SystemRandom},
-};
-
-pub struct BlobEncryptor {
- random: SystemRandom,
-}
-
-// Non-secret input to the AEAD key derivation
-const KDF_INFO: &[u8] = b"CompOS blob sealing key";
-
-impl BlobEncryptor {
- pub fn new() -> Self {
- Self { random: SystemRandom::new() }
- }
-
- pub fn derive_aead_key(&self, input_keying_material: &[u8]) -> Result<LessSafeKey> {
- // Derive key using HKDF - see https://datatracker.ietf.org/doc/html/rfc5869#section-2
- let salt = [];
- let prk = Salt::new(HKDF_SHA256, &salt).extract(input_keying_material);
- let okm = prk.expand(&[KDF_INFO], &AES_256_GCM).context("HKDF failed")?;
- // LessSafeKey is only less safe in that it has less inherent protection against nonce
- // reuse; we are safe because we use a new random nonce for each sealing operation.
- // (See https://github.com/briansmith/ring/issues/899.)
- Ok(LessSafeKey::new(okm.into()))
- }
-
- pub fn encrypt_bytes(&self, key: LessSafeKey, bytes: &[u8]) -> Result<Vec<u8>> {
- let mut output = Vec::with_capacity(bytes.len() + NONCE_LEN + key.algorithm().tag_len());
-
- // Generate a unique nonce, since we may use the same key more than once, and put it at the
- // start of the output blob.
- let mut nonce_bytes = [0u8; NONCE_LEN];
- self.random.fill(&mut nonce_bytes).context("Failed to generate random nonce")?;
- output.extend_from_slice(&nonce_bytes);
-
- // Copy input to output then encrypt & seal it in place.
- output.extend_from_slice(bytes);
- let nonce = Nonce::assume_unique_for_key(nonce_bytes);
- let tag = key
- .seal_in_place_separate_tag(nonce, Aad::empty(), &mut output[NONCE_LEN..])
- .context("Failed to seal blob")?;
- output.extend_from_slice(tag.as_ref());
-
- Ok(output)
- }
-
- pub fn decrypt_bytes(&self, key: LessSafeKey, bytes: &[u8]) -> Result<Vec<u8>> {
- if bytes.len() < NONCE_LEN + key.algorithm().tag_len() {
- bail!("Encrypted blob is too small");
- }
-
- // We expect the nonce at the start followed by the sealed data (encrypted data +
- // authentication tag).
- let nonce = Nonce::try_assume_unique_for_key(&bytes[..NONCE_LEN]).unwrap();
- let mut output = bytes[NONCE_LEN..].to_vec();
-
- // Verify & decrypt the data in place
- let unsealed_size = key
- .open_in_place(nonce, Aad::empty(), &mut output)
- .context("Failed to unseal blob")?
- .len();
-
- // Remove the tag after the plaintext
- output.truncate(unsealed_size);
-
- Ok(output)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_round_trip_data() -> Result<()> {
- let encryptor = BlobEncryptor::new();
- let input_keying_material = b"Key is derived from this";
- let original_bytes = b"This is the secret data";
-
- let key = encryptor.derive_aead_key(input_keying_material)?;
- let blob = encryptor.encrypt_bytes(key, original_bytes)?;
-
- let key = encryptor.derive_aead_key(input_keying_material)?;
- let decoded_bytes = encryptor.decrypt_bytes(key, &blob)?;
-
- assert_eq!(decoded_bytes, original_bytes);
- Ok(())
- }
-
- #[test]
- fn test_modified_data_detected() -> Result<()> {
- let encryptor = BlobEncryptor::new();
- let input_keying_material = b"Key is derived from this";
- let original_bytes = b"This is the secret data";
-
- let key = encryptor.derive_aead_key(input_keying_material)?;
- let mut blob = encryptor.encrypt_bytes(key, original_bytes)?;
-
- // Flip a bit.
- blob[0] ^= 1;
-
- let key = encryptor.derive_aead_key(input_keying_material)?;
- let decoded_bytes = encryptor.decrypt_bytes(key, &blob);
-
- assert!(decoded_bytes.is_err());
- Ok(())
- }
-}
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 6db31b6..850a0a8 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -26,7 +26,7 @@
use std::process::Command;
use crate::artifact_signer::ArtifactSigner;
-use crate::compos_key_service::Signer;
+use crate::signing_key::Signer;
use authfs_aidl_interface::aidl::com::android::virt::fs::{
AuthFsConfig::{
AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
diff --git a/compos/src/compos_key_service.rs b/compos/src/compos_key_service.rs
deleted file mode 100644
index 086a162..0000000
--- a/compos/src/compos_key_service.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2021, 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.
-
-//! Provides a binder service for key generation & verification for CompOs. We assume we have
-//! access to Keystore in the VM, but not persistent storage; instead the host stores the key
-//! on our behalf via this service.
-
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, Digest::Digest, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel, Tag::Tag,
-};
-use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
- IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
-};
-use android_system_keystore2::binder::{wait_for_interface, Strong};
-use anyhow::{anyhow, Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::CompOsKeyData::CompOsKeyData;
-use ring::rand::{SecureRandom, SystemRandom};
-use ring::signature;
-use scopeguard::ScopeGuard;
-
-/// Keystore2 namespace ID, used for access control to keys. In a VM we can use the generic ID
-/// allocated for payloads. See microdroid's keystore2_key_contexts.
-const KEYSTORE_NAMESPACE: i64 = 140;
-
-const KEYSTORE_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
-const PURPOSE_SIGN: KeyParameter =
- KeyParameter { tag: Tag::PURPOSE, value: KeyParameterValue::KeyPurpose(KeyPurpose::SIGN) };
-const ALGORITHM: KeyParameter =
- KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA) };
-const PADDING: KeyParameter = KeyParameter {
- tag: Tag::PADDING,
- value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN),
-};
-const DIGEST: KeyParameter =
- KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) };
-const KEY_SIZE: KeyParameter =
- KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) };
-const EXPONENT: KeyParameter =
- KeyParameter { tag: Tag::RSA_PUBLIC_EXPONENT, value: KeyParameterValue::LongInteger(65537) };
-const NO_AUTH_REQUIRED: KeyParameter =
- KeyParameter { tag: Tag::NO_AUTH_REQUIRED, value: KeyParameterValue::BoolValue(true) };
-
-const BLOB_KEY_DESCRIPTOR: KeyDescriptor =
- KeyDescriptor { domain: Domain::BLOB, nspace: KEYSTORE_NAMESPACE, alias: None, blob: None };
-
-/// An internal service for CompOS key management.
-#[derive(Clone)]
-pub struct CompOsKeyService {
- random: SystemRandom,
- security_level: Strong<dyn IKeystoreSecurityLevel>,
-}
-
-impl CompOsKeyService {
- pub fn new() -> Result<Self> {
- let keystore_service = wait_for_interface::<dyn IKeystoreService>(KEYSTORE_SERVICE_NAME)
- .context("No Keystore service")?;
-
- Ok(CompOsKeyService {
- random: SystemRandom::new(),
- security_level: keystore_service
- .getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT)
- .context("Getting SecurityLevel failed")?,
- })
- }
-
- pub fn generate(&self) -> Result<CompOsKeyData> {
- let key_descriptor = BLOB_KEY_DESCRIPTOR;
- let key_parameters =
- [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST, KEY_SIZE, EXPONENT, NO_AUTH_REQUIRED];
- let attestation_key = None;
- let flags = 0;
- let entropy = [];
-
- let key_metadata = self
- .security_level
- .generateKey(&key_descriptor, attestation_key, &key_parameters, flags, &entropy)
- .context("Generating key failed")?;
-
- if let (Some(certificate), Some(blob)) = (key_metadata.certificate, key_metadata.key.blob) {
- Ok(CompOsKeyData { certificate, keyBlob: blob })
- } else {
- Err(anyhow!("Missing cert or blob"))
- }
- }
-
- pub fn verify(&self, key_blob: &[u8], public_key: &[u8]) -> Result<()> {
- let mut data = [0u8; 32];
- self.random.fill(&mut data).context("No random data")?;
-
- let signature = self.new_signer(key_blob).sign(&data)?;
-
- let public_key =
- signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256, public_key);
- public_key.verify(&data, &signature).context("Signature verification failed")?;
-
- Ok(())
- }
-
- pub fn new_signer(&self, key_blob: &[u8]) -> Signer {
- Signer { key_blob: key_blob.to_vec(), security_level: self.security_level.clone() }
- }
-}
-
-pub struct Signer {
- key_blob: Vec<u8>,
- security_level: Strong<dyn IKeystoreSecurityLevel>,
-}
-
-impl Signer {
- pub fn sign(self, data: &[u8]) -> Result<Vec<u8>> {
- let key_descriptor = KeyDescriptor { blob: Some(self.key_blob), ..BLOB_KEY_DESCRIPTOR };
- let operation_parameters = [PURPOSE_SIGN, ALGORITHM, PADDING, DIGEST];
- let forced = false;
-
- let response = self
- .security_level
- .createOperation(&key_descriptor, &operation_parameters, forced)
- .context("Creating key failed")?;
- let operation = scopeguard::guard(
- response.iOperation.ok_or_else(|| anyhow!("No operation created"))?,
- |op| op.abort().unwrap_or_default(),
- );
-
- if response.operationChallenge.is_some() {
- return Err(anyhow!("Key requires user authorization"));
- }
-
- let signature = operation.finish(Some(data), None).context("Signing failed")?;
- // Operation has finished, we're no longer responsible for aborting it
- ScopeGuard::into_inner(operation);
-
- signature.ok_or_else(|| anyhow!("No signature returned"))
- }
-}
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 60e77a7..e4cdb40 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -27,8 +27,7 @@
use std::sync::RwLock;
use crate::compilation::{odrefresh, OdrefreshContext};
-use crate::compos_key_service::{CompOsKeyService, Signer};
-use crate::dice::Dice;
+use crate::signing_key::{Signer, SigningKey};
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
use compos_aidl_interface::aidl::com::android::compos::{
CompOsKeyData::CompOsKeyData,
@@ -45,8 +44,7 @@
pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
let service = CompOsService {
odrefresh_path: PathBuf::from(ODREFRESH_PATH),
- key_service: CompOsKeyService::new()?,
- dice: Dice::new()?,
+ signing_key: SigningKey::new()?,
key_blob: RwLock::new(Vec::new()),
};
Ok(BnCompOsService::new_binder(service, BinderFeatures::default()))
@@ -54,8 +52,7 @@
struct CompOsService {
odrefresh_path: PathBuf,
- key_service: CompOsKeyService,
- dice: Dice,
+ signing_key: SigningKey,
key_blob: RwLock<Vec<u8>>,
}
@@ -65,13 +62,9 @@
if key.is_empty() {
Err(new_binder_exception(ExceptionCode::ILLEGAL_STATE, "Key is not initialized"))
} else {
- Ok(self.key_service.new_signer(key))
+ Ok(self.signing_key.new_signer(key))
}
}
-
- fn get_boot_certificate_chain(&self) -> Result<Vec<u8>> {
- self.dice.get_boot_certificate_chain()
- }
}
impl Interface for CompOsService {}
@@ -114,11 +107,11 @@
}
fn generateSigningKey(&self) -> BinderResult<CompOsKeyData> {
- to_binder_result(self.key_service.generate())
+ to_binder_result(self.signing_key.generate())
}
fn verifySigningKey(&self, key_blob: &[u8], public_key: &[u8]) -> BinderResult<bool> {
- Ok(if let Err(e) = self.key_service.verify(key_blob, public_key) {
+ Ok(if let Err(e) = self.signing_key.verify(key_blob, public_key) {
warn!("Signing key verification failed: {:?}", e);
false
} else {
@@ -127,7 +120,7 @@
}
fn getBootCertificateChain(&self) -> BinderResult<Vec<u8>> {
- to_binder_result(self.get_boot_certificate_chain())
+ to_binder_result(self.signing_key.get_boot_certificate_chain())
}
}
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index 23a6ed0..c2923f0 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -17,9 +17,8 @@
//! A tool to start a standalone compsvc server that serves over RPC binder.
mod artifact_signer;
-mod blob_encryptor;
+mod blob_encryption;
mod compilation;
-mod compos_key_service;
mod compsvc;
mod dice;
mod fsverity;
diff --git a/compos/src/dice.rs b/compos/src/dice.rs
index cdfc387..d9bb1db 100644
--- a/compos/src/dice.rs
+++ b/compos/src/dice.rs
@@ -20,6 +20,7 @@
use android_security_dice::binder::{wait_for_interface, Strong};
use anyhow::{Context, Result};
+#[derive(Clone)]
pub struct Dice {
node: Strong<dyn IDiceNode>,
}
diff --git a/compos/src/signing_key.rs b/compos/src/signing_key.rs
index 62c7a40..990f9b0 100644
--- a/compos/src/signing_key.rs
+++ b/compos/src/signing_key.rs
@@ -19,60 +19,83 @@
#![allow(dead_code, unused_variables)]
-use crate::blob_encryptor::BlobEncryptor;
+use crate::blob_encryption;
use crate::dice::Dice;
-use anyhow::{bail, Result};
+use anyhow::{bail, Context, Result};
use compos_aidl_interface::aidl::com::android::compos::CompOsKeyData::CompOsKeyData;
-use ring::rand::SystemRandom;
+use ring::{
+ rand::{SecureRandom, SystemRandom},
+ signature,
+};
pub struct SigningKey {
- random: SystemRandom,
dice: Dice,
- blob_encryptor: BlobEncryptor,
}
impl SigningKey {
pub fn new() -> Result<Self> {
- Ok(Self {
- random: SystemRandom::new(),
- dice: Dice::new()?,
- blob_encryptor: BlobEncryptor::new(),
- })
+ Ok(Self { dice: Dice::new()? })
+ }
+
+ pub fn get_boot_certificate_chain(&self) -> Result<Vec<u8>> {
+ Dice::new()?.get_boot_certificate_chain()
}
pub fn generate(&self) -> Result<CompOsKeyData> {
- // TODO: generate key pair; get aead key; generate random nonce; encrypt private key;
- // generate self-signed cert
- bail!("Not implemented")
+ let key_result = compos_native::generate_key_pair();
+ if key_result.public_key.is_empty() || key_result.private_key.is_empty() {
+ bail!("Failed to generate key pair: {}", key_result.error);
+ }
+
+ let encrypted = encrypt_private_key(&self.dice, &key_result.private_key)?;
+ Ok(CompOsKeyData { publicKey: key_result.public_key, keyBlob: encrypted })
}
pub fn verify(&self, key_blob: &[u8], public_key: &[u8]) -> Result<()> {
- bail!("Not implemented")
+ // We verify the private key by verifying the AEAD authentication tag in the signer.
+ // To verify the public key matches, we sign a block of random data with the private key,
+ // and then check that the signature matches the purported key.
+ let mut data = [0u8; 32]; // Size is fairly arbitrary.
+ SystemRandom::new().fill(&mut data).context("No random data")?;
+
+ let signature = self.new_signer(key_blob).sign(&data)?;
+
+ let public_key =
+ signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256, public_key);
+ public_key.verify(&data, &signature).context("Signature verification failed")?;
+
+ Ok(())
}
pub fn new_signer(&self, key_blob: &[u8]) -> Signer {
- Signer { key_blob: key_blob.to_owned() }
- }
-
- fn encrypt_private_key(&self, private_key: &[u8]) -> Result<Vec<u8>> {
- let cdi = self.dice.get_sealing_cdi()?;
- let aead_key = self.blob_encryptor.derive_aead_key(&cdi)?;
- self.blob_encryptor.encrypt_bytes(aead_key, private_key)
- }
-
- fn decrypt_private_key(&self, blob: &[u8]) -> Result<Vec<u8>> {
- let cdi = self.dice.get_sealing_cdi()?;
- let aead_key = self.blob_encryptor.derive_aead_key(&cdi)?;
- self.blob_encryptor.decrypt_bytes(aead_key, blob)
+ Signer { key_blob: key_blob.to_owned(), dice: self.dice.clone() }
}
}
pub struct Signer {
key_blob: Vec<u8>,
+ dice: Dice,
}
impl Signer {
pub fn sign(self, data: &[u8]) -> Result<Vec<u8>> {
- bail!("Not implemented")
+ let private_key = decrypt_private_key(&self.dice, &self.key_blob)?;
+ let sign_result = compos_native::sign(&private_key, data);
+ if sign_result.signature.is_empty() {
+ bail!("Failed to sign: {}", sign_result.error);
+ }
+ Ok(sign_result.signature)
}
}
+
+fn encrypt_private_key(dice: &Dice, private_key: &[u8]) -> Result<Vec<u8>> {
+ let cdi = dice.get_sealing_cdi()?;
+ let aead_key = blob_encryption::derive_aead_key(&cdi)?;
+ blob_encryption::encrypt_bytes(aead_key, private_key)
+}
+
+fn decrypt_private_key(dice: &Dice, blob: &[u8]) -> Result<Vec<u8>> {
+ let cdi = dice.get_sealing_cdi()?;
+ let aead_key = blob_encryption::derive_aead_key(&cdi)?;
+ blob_encryption::decrypt_bytes(aead_key, blob)
+}