Skip to content

Commit

Permalink
Sign group keys as part of Key Provisioning (#4961)
Browse files Browse the repository at this point in the history
This PR adds the ability to sign group keys in the attestation evidence as part of Key Provisioning.

Ref #4442
  • Loading branch information
ipetr0v authored Mar 27, 2024
1 parent 2a57cd6 commit 121a6b0
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 33 deletions.
46 changes: 44 additions & 2 deletions oak_attestation/src/dice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ impl DiceBuilder {
additional_claims: Vec<(ClaimName, ciborium::Value)>,
kem_public_key: &[u8],
verifying_key: &VerifyingKey,
group_kem_public_key: Option<&[u8]>,
group_verifying_key: Option<&VerifyingKey>,
) -> anyhow::Result<Evidence> {
// The last evidence layer contains the certificate for the current signing key.
// Since the builder contains an existing signing key there must be at
Expand Down Expand Up @@ -127,7 +129,7 @@ impl DiceBuilder {

let signing_public_key_certificate = generate_signing_certificate(
&self.signing_key,
issuer_id,
issuer_id.clone(),
verifying_key,
additional_claims,
)
Expand All @@ -136,9 +138,44 @@ impl DiceBuilder {
.to_vec()
.map_err(anyhow::Error::msg)?;

// Generate group keys certificates as part of Key Provisioning.
let group_encryption_public_key_certificate =
if let Some(group_kem_public_key) = group_kem_public_key {
generate_kem_certificate(
&self.signing_key,
issuer_id.clone(),
group_kem_public_key,
vec![],
)
.map_err(anyhow::Error::msg)
.context("couldn't generate encryption public key certificate")?
.to_vec()
.map_err(anyhow::Error::msg)?
} else {
vec![]
};

let group_signing_public_key_certificate =
if let Some(group_verifying_key) = group_verifying_key {
generate_signing_certificate(
&self.signing_key,
issuer_id.clone(),
group_verifying_key,
vec![],
)
.map_err(anyhow::Error::msg)
.context("couldn't generate signing public key certificate")?
.to_vec()
.map_err(anyhow::Error::msg)?
} else {
vec![]
};

evidence.application_keys = Some(ApplicationKeys {
encryption_public_key_certificate,
signing_public_key_certificate,
group_encryption_public_key_certificate,
group_signing_public_key_certificate,
});

Ok(evidence)
Expand Down Expand Up @@ -237,5 +274,10 @@ fn application_keys_to_proto(
let signing_public_key_certificate =
oak_dice::utils::cbor_encoded_bytes_to_vec(&value.signing_public_key_certificate[..])
.map_err(anyhow::Error::msg)?;
Ok(ApplicationKeys { encryption_public_key_certificate, signing_public_key_certificate })
Ok(ApplicationKeys {
encryption_public_key_certificate,
signing_public_key_certificate,
group_encryption_public_key_certificate: vec![],
group_signing_public_key_certificate: vec![],
})
}
67 changes: 39 additions & 28 deletions oak_containers_orchestrator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,78 +56,89 @@ async fn main() -> anyhow::Result<()> {
.map_err(|error| anyhow!("couldn't create client: {:?}", error))?,
);

// Get key provisioning role.
let key_provisioning_role = launcher_client
.get_key_provisioning_role()
.await
.map_err(|error| anyhow!("couldn't get key provisioning role: {:?}", error))?;

// Generate application keys.
let (instance_keys, instance_public_keys) = generate_instance_keys();
let (mut group_keys, group_public_keys) =
if key_provisioning_role == KeyProvisioningRole::Leader {
let (group_keys, group_public_keys) = instance_keys.generate_group_keys();
(Some(Arc::new(group_keys)), Some(group_public_keys))
} else {
(None, None)
};

// Load application.
let container_bundle = launcher_client
.get_container_bundle()
.await
.map_err(|error| anyhow!("couldn't get container bundle: {:?}", error))?;

let application_config = launcher_client
.get_application_config()
.await
.map_err(|error| anyhow!("couldn't get application config: {:?}", error))?;

// Generate attestation evidence and send it to the Hostlib.
let dice_builder = oak_containers_orchestrator::dice::load_stage1_dice_data()?;
let additional_claims = oak_containers_orchestrator::dice::measure_container_and_config(
&container_bundle,
&application_config,
);
let (instance_keys, instance_public_keys) = generate_instance_keys();

let evidence = dice_builder.add_application_keys(
additional_claims,
&instance_public_keys.encryption_public_key,
&instance_public_keys.signing_public_key,
if let Some(ref group_public_keys) = group_public_keys {
Some(&group_public_keys.encryption_public_key)
} else {
None
},
None,
)?;
launcher_client
.send_attestation_evidence(evidence)
.await
.map_err(|error| anyhow!("couldn't send attestation evidence: {:?}", error))?;

// Request group keys.
if key_provisioning_role == KeyProvisioningRole::Follower {
let get_group_keys_response = launcher_client
.get_group_keys()
.await
.map_err(|error| anyhow!("couldn't get group keys: {:?}", error))?;
let provisioned_group_keys = instance_keys
.provide_group_keys(get_group_keys_response)
.context("couldn't provide group keys")?;
group_keys = Some(Arc::new(provisioned_group_keys));
}

if let Some(path) = args.ipc_socket_path.parent() {
tokio::fs::create_dir_all(path).await?;
}

let key_provisioning_role = launcher_client
.get_key_provisioning_role()
.await
.map_err(|error| anyhow!("couldn't get key provisioning role: {:?}", error))?;
let group_keys = Arc::new(match key_provisioning_role {
KeyProvisioningRole::Unspecified => anyhow::bail!("unspecified key provisioning role"),
KeyProvisioningRole::Leader => {
// TODO(#4442): Sign group public keys in the enclave evidence.
let (group_keys, _) = instance_keys.generate_group_keys();
group_keys
}
KeyProvisioningRole::Dependant => {
let get_group_keys_response = launcher_client
.get_group_keys()
.await
.map_err(|error| anyhow!("couldn't get group keys: {:?}", error))?;
instance_keys
.provide_group_keys(get_group_keys_response)
.context("couldn't provide group keys")?
}
});

let _metrics = oak_containers_orchestrator::metrics::run(launcher_client.clone())?;

// Start application and gRPC servers.
let user = nix::unistd::User::from_name(&args.runtime_user)
.context(format!("error resolving user {}", args.runtime_user))?
.context(format!("user `{}` not found", args.runtime_user))?;

let cancellation_token = CancellationToken::new();
tokio::try_join!(
oak_containers_orchestrator::ipc_server::create(
&args.ipc_socket_path,
instance_keys,
group_keys.clone(),
group_keys.clone().context("group keys were not provisioned")?,
application_config,
launcher_client,
cancellation_token.clone(),
),
oak_containers_orchestrator::key_provisioning::create(
&args.orchestrator_addr,
group_keys,
group_keys.context("group keys were not provisioned")?,
cancellation_token.clone(),
),
oak_containers_orchestrator::container_runtime::run(
Expand Down
14 changes: 14 additions & 0 deletions proto/attestation/evidence.proto
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ message ApplicationKeys {
// Represented as a CBOR/COSE/CWT ECA certificate.
// <https://www.rfc-editor.org/rfc/rfc8392.html>
bytes signing_public_key_certificate = 2;

// Certificate signing the group encryption public key as part of Key
// Provisioning.
//
// Represented as a CBOR/COSE/CWT ECA certificate.
// <https://www.rfc-editor.org/rfc/rfc8392.html>
bytes group_encryption_public_key_certificate = 3;

// Certificate signing the group signing public key as part of Key
// Provisioning.
//
// Represented as a CBOR/COSE/CWT ECA certificate.
// <https://www.rfc-editor.org/rfc/rfc8392.html>
bytes group_signing_public_key_certificate = 4;
}

// Attestation Evidence used by the client to the identity of firmware and
Expand Down
6 changes: 3 additions & 3 deletions proto/containers/hostlib_key_provisioning.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import "proto/key_provisioning/key_provisioning.proto";
enum KeyProvisioningRole {
KEY_PROVISIONING_ROLE_UNSPECIFIED = 0;
LEADER = 1;
DEPENDANT = 2;
FOLLOWER = 2;
}

message GetKeyProvisioningRoleResponse {
Expand All @@ -40,11 +40,11 @@ service HostlibKeyProvisioning {
// Get the enclave role for Key Provisioning.
// Could be one of the following:
// - Leader that generates group keys and distributes them.
// - Dependant that requests group keys from the leader.
// - Follower that requests group keys from the leader.
rpc GetKeyProvisioningRole(google.protobuf.Empty) returns (GetKeyProvisioningRoleResponse) {}

// Get enclave group keys to the enclave as part of Key Provisioning.
// This method is only called by the Dependant Orchestrator.
// This method is only called by the Follower Orchestrator.
//
// This method must be called after `oak.containers.Launcher.SendAttestationEvidence`, because
// Hostlib needs to have the Attestation Evidence in order to request group keys from the leader.
Expand Down

0 comments on commit 121a6b0

Please sign in to comment.