Skip to content

Commit

Permalink
docs: Fill in a few missing doc comments
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Howard <[email protected]>
  • Loading branch information
paulhowardarm committed Aug 19, 2024
1 parent 093b392 commit 8a6ca86
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 5 deletions.
54 changes: 49 additions & 5 deletions rust-keybroker/keybroker-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,95 @@
// Copyright 2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

//! This library provides the common data types that are defined in the OpenAPI schema for the keybroker,
//! along with the serialization functionality that allows them to be transacted over HTTP. The small collection
//! of data types in this library are consumed by both the server and the client.

/// Represents a single attestation challenge (nonce).
///
/// Challenges are formed in response to a key access request. The purpose of the key broker is to provide
/// keys (secret strings) in exchange for a verifiable attestation token. In order to produce an attestation
/// token, the client must first be given the challenge value (commonly called a "nonce"). This structure
/// provides the nonce along with a vector of permissible evidence content types that the server will
/// accept.
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]

pub struct AttestationChallenge {
/// Base64 encoding of the challenge value (nonce). The client must incorporate this challenge value
/// into its attestation token/evidence, according to the conventions of the evidence bundle being
/// formed.
pub challenge: String,

/// List of acceptable evidence media types, such as "application/eat-collection; profile=http://arm.com/CCA-SSD/1.0.0".
pub accept: Vec<EvidenceContentType>,
}

/// A request to access a key or secret string according to the "background check" interaction pattern
/// for attestation.
///
/// The identity of the key being accessed is not part of this structure, because it is implicit in the path
/// of the API request to access a key.
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]

pub struct BackgroundCheckKeyRequest {
/// The public part of the wrapping key pair that the client has specified for encryption of the key/secret
/// in transit. The keybroker server uses this public key to wrap (encrypt) the data before returning
/// it to the client. This is in order for confidentiality to be maintained without relying solely on TLS
/// between the client and the server.
pub pubkey: PublicWrappingKey,
}

/// Represents an error occurring within the API usage.
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]

pub struct ErrorInformation {
/// Formal type string for the error.
pub r#type: String,

/// Human-readable error details, giving more information about the error.
pub detail: String,
}

pub type EvidenceBytes = String;

pub type EvidenceContentType = String;

/// The public portion of a wrapping key pair used to protect keys/secrets in transit between the client and
/// the server.
///
/// Wrapping keys are used so that confidentiality of the brokered data is maintained without relying solely
/// on TLS between the client and the server.
///
/// Only the client (within its confidential compute environment) has the private part of the key pair, with
/// which it can decrypt and use the data from the server.
///
/// Only RSA keys are currently supported for wrapping.
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]

pub struct PublicWrappingKey {
/// Public key type. This must be "RSA".
pub kty: String,

/// Encryption algorithm. This must be either "RSA1_5" or "OAEP".
pub alg: String,

/// Base64 encoding of the public key modulus.
pub n: String,

/// Base64 encoding of the public key exponent.
pub e: String,
}

/// Wrapped/encrypted secret data returned from the server in the case of a successfully-verified attestation.
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]

pub struct WrappedKeyData {
/// Base64 encoding of encrypted data. The client should Base64-decode this string, and then RSA decrypt the
/// resulting vector of bytes in order to obtain the secret data payload.
pub data: String,
}
41 changes: 41 additions & 0 deletions rust-keybroker/keybroker-server/src/challenge.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
// Copyright 2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

//! This module handles the creation and caching of challenges.
//!
//! A challenge is allocated whenever a client of the keybroker makes its initial request to access a key from the store.
//! Keys are never returned directly to the client. Instead, a challenge is created. This challenge invites the client to
//! form a bundle of attestation evidence to prove that it is trustworthy to receive the key. The client must incorporate
//! the challenge value (commonly called a "nonce") into the signed attestation evidence, according to the specific
//! conventions of the evidence type that it employs.
//!
//! When a challenge is allocated, it is cached by the server along with all of the details that the client initially
//! provided: included the identity of the key that it wants to access, and the public wrapping key that it provided in
//! order to keep the data protected in transit.
//!
//! Challenges are given their own unique identities (simple 32-bit integer values) and cached within the keybroker service,
//! with the expectation that the client will later attempt to redeem the challenge by submitting an evidence bundle.
//!
use crate::error::Result;
use keybroker_common::PublicWrappingKey;
use std::collections::HashMap;

/// Represents a single challenge, and provides the challenge value ("nonce") while also remembering the information
/// that the client provided in order to access a key.
#[derive(Debug, Clone)]
pub struct Challenge {
/// The identity of the challenge itself. A simple integer value, designed to be unique only within the keybroker
/// server instance. This value (represented in decimal) also forms part of the URL path when the client
/// redeems the challenge by supplying the attestation evidence bundle.
pub challenge_id: u32,

/// The identity of the key that the client wants to access.
pub key_id: String,

/// The public part of the wrapping key pair that the client has specified for use in order to protect the
/// secret data in transit when it is later returned.
pub wrapping_key: PublicWrappingKey,

/// The challenge value (nonce) that the client must incorporate into the evidence bundle.
pub challenge_value: Vec<u8>,
}

/// This structure provides a hash map of challenges, keyed on the integer challenge identifier.
pub struct Challenger {
challenge_table: HashMap<u32, Challenge>,
}
Expand All @@ -34,6 +62,10 @@ impl Challenger {
}
}

/// Allocate a new challenge and store it in the table.
///
/// The inputs are the identity of the key that the client wants to access, and the public wrapping
/// key that the client has specified to encrypt and protect the data in transit.
pub fn create_challenge(
&mut self,
key_id: &str,
Expand Down Expand Up @@ -62,6 +94,7 @@ impl Challenger {
challenge
}

/// Looks up a challenge in the table and returns it, failing if no such challenge is found.
pub fn get_challenge(&self, challenge_id: u32) -> Result<Challenge> {
let challenge = self.challenge_table.get(&challenge_id);
match challenge {
Expand All @@ -72,6 +105,14 @@ impl Challenger {
}
}

/// Deletes a challenge from the table, failing if no such challenge is found.
///
/// The key broker deletes challenges eagerly, rather than relying on a garbage collection mechanism.
/// This is for the sake of simplicity, since this is only a demo keybroker. Challenges are deleted
/// as soon as the client makes an attempt to redeem the challenge by providing an attestation
/// token. This happens even if the attestation verification fails, meaning that the client only
/// has one opportunity to redeem any given challenge, otherwise it needs to begin the key
/// request all over again.
pub fn delete_challenge(&mut self, challenge_id: u32) -> Result<()> {
let challenge = self.challenge_table.remove(&challenge_id);
match challenge {
Expand Down
13 changes: 13 additions & 0 deletions rust-keybroker/keybroker-server/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,35 @@ use thiserror::Error;
/// Top-level error type for the whole of the key broker service.
#[derive(Error, Debug)]
pub enum Error {
/// Represents errors resulting from the Veraison API usage (when the keybroker calls out to Veraison to verify attestation tokens).
#[error(transparent)]
VeraisonApi(#[from] veraison_apiclient::Error),

/// Represents errors from the use of the attestation results library. These errors may occur when inspecting attestation
/// results in order to implement an appraisal policy.
#[error(transparent)]
Ear(#[from] ear::Error),

/// Represents errors in verification that are not API usage errors.
#[error(transparent)]
Verification(#[from] VerificationErrorKind),

/// Represents errors related to RSA encryption or decryption. These can occur when using the RSA algorithm to wrap
/// key data from the server.
#[error(transparent)]
Rsa(#[from] rsa::Error),

/// Represents an error from the key store, such as an attempt to access a key that does not exist.
#[error(transparent)]
KeyStore(#[from] KeyStoreErrorKind),

/// Represents an error from the challenge manager, such as an attempt to lookup a challenge that was
/// never allocated.
#[error(transparent)]
Challenge(#[from] ChallengeErrorKind),

/// Represents errors related to base64 decoding, which can occur when processing the various base64 strings
/// that are transacted through the API between the client and the server, if the client provides faulty data.
#[error(transparent)]
Base64Decode(#[from] base64::DecodeError),
}
Expand All @@ -43,9 +54,11 @@ pub enum KeyStoreErrorKind {
#[error("Requested key is not in the store.")]
KeyNotFound,

/// The client provided a wrapping key that is not an RSA key.
#[error("The wrapping key type is not supported. Wrapping key must be an RSA key.")]
UnsupportedWrappingKeyType,

/// The client provided a wrapping key whose algorithm was not supported.
#[error("Thw wrapping key encryption algorithm is not supported.")]
UnsupportedWrappingKeyAlgorithm,
}
Expand Down

0 comments on commit 8a6ca86

Please sign in to comment.