blob: 4b134e45b0803df03df472bc22b3e6c28fd715f6 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// 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.
//! Cryptographic materials for v1 advertisement-format credentials.
use core::borrow::Borrow;
use crypto_provider::{aes::Aes128Key, ed25519, CryptoProvider};
use np_hkdf::UnsignedSectionKeys;
/// Cryptographic materials necessary for determining whether or not
/// a given V1 advertisement section matches an identity.
/// Per the construction of the V1 specification, this is also
/// the information necessary to decrypt the raw byte contents
/// of an encrypted V1 section.
#[derive(Clone)]
pub struct SectionIdentityResolutionMaterial {
/// The AES key for decrypting section ciphertext
pub(crate) aes_key: Aes128Key,
/// The metadata key HMAC key for deriving and verifying the identity metadata
/// key HMAC against the expected value.
pub(crate) metadata_key_hmac_key: [u8; 32],
/// The expected metadata key HMAC to check against for an identity match.
pub(crate) expected_metadata_key_hmac: [u8; 32],
}
/// Cryptographic materials necessary for determining whether or not
/// a given V1 signed advertisement section matches an identity.
#[derive(Clone)]
pub struct SignedSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
impl SignedSectionIdentityResolutionMaterial {
#[cfg(test)]
pub(crate) fn from_raw(raw: SectionIdentityResolutionMaterial) -> Self {
Self(raw)
}
pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
&self.0
}
pub(crate) fn from_hkdf_and_expected_metadata_key_hmac<C: CryptoProvider>(
hkdf: &np_hkdf::NpKeySeedHkdf<C>,
expected_metadata_key_hmac: [u8; 32],
) -> Self {
Self(SectionIdentityResolutionMaterial {
aes_key: hkdf.extended_signed_section_aes_key(),
metadata_key_hmac_key: *hkdf.extended_signed_metadata_key_hmac_key().as_bytes(),
expected_metadata_key_hmac,
})
}
}
/// Cryptographic materials necessary for determining whether or not
/// a given V1 MIC advertisement section matches an identity.
#[derive(Clone)]
pub struct UnsignedSectionIdentityResolutionMaterial(SectionIdentityResolutionMaterial);
impl UnsignedSectionIdentityResolutionMaterial {
#[cfg(test)]
pub(crate) fn from_raw(raw: SectionIdentityResolutionMaterial) -> Self {
Self(raw)
}
pub(crate) fn as_raw_resolution_material(&self) -> &SectionIdentityResolutionMaterial {
&self.0
}
pub(crate) fn from_hkdf_and_expected_metadata_key_hmac<C: CryptoProvider>(
hkdf: &np_hkdf::NpKeySeedHkdf<C>,
expected_metadata_key_hmac: [u8; 32],
) -> Self {
Self(SectionIdentityResolutionMaterial {
aes_key: UnsignedSectionKeys::aes_key(hkdf),
metadata_key_hmac_key: *hkdf.extended_unsigned_metadata_key_hmac_key().as_bytes(),
expected_metadata_key_hmac,
})
}
}
/// Crypto materials for V1 signed sections which are not employed in identity resolution,
/// but may be necessary to verify a signed section.
#[derive(Clone)]
pub struct SignedSectionVerificationMaterial {
/// The np_ed25519 public key to be
/// used for signature verification of signed sections.
pub(crate) pub_key: ed25519::RawPublicKey,
}
impl SignedSectionVerificationMaterial {
/// Gets the np_ed25519 public key for the given identity,
/// used for signature verification of signed sections.
pub(crate) fn signature_verification_public_key<C: CryptoProvider>(
&self,
) -> np_ed25519::PublicKey<C> {
np_ed25519::PublicKey::from_bytes(&self.pub_key).expect("Should only contain valid keys")
}
}
/// Crypto materials for V1 unsigned sections which are not employed in identity resolution,
/// but may be necessary to fully decrypt an unsigned section.
#[derive(Clone)]
pub struct UnsignedSectionVerificationMaterial {
/// The MIC HMAC key for verifying the integrity of unsigned sections.
pub(crate) mic_hmac_key: [u8; 32],
}
impl UnsignedSectionVerificationMaterial {
/// Returns the MIC HMAC key for unsigned sections
pub(crate) fn mic_hmac_key<C: CryptoProvider>(&self) -> np_hkdf::NpHmacSha256Key<C> {
self.mic_hmac_key.into()
}
}
// Space-time tradeoffs:
// - Calculating an HKDF from the key seed costs about 2us on a gLinux laptop, and occupies 80b.
// - Calculating an AES (16b) or HMAC (32b) key from the HKDF costs about 700ns.
// The right tradeoff may also vary by use case. For frequently used identities we should
// probably pre-calculate everything. For occasionally used ones, or ones that are loaded from
// disk, used once, and discarded, we might want to precalculate on a separate thread or the
// like.
// The AES key and metadata key HMAC key are the most frequently used ones, as the MIC HMAC key
// is only used on the matching identity, not all identities.
/// Cryptographic material for an individual NP credential used to decrypt and verify v1 sections.
pub trait V1CryptoMaterial {
/// The return type of `Self::signed_identity_resolution_material`, which is some
/// data-type which allows borrowing `SignedSectionIdentityResolutionMaterial`
type SignedIdentityResolverReference<'a>: Borrow<SignedSectionIdentityResolutionMaterial>
where
Self: 'a;
/// The return type of `Self::unsigned_identity_resolution_material`, which is some
/// data-type which allows borrowing `UnsignedSectionIdentityResolutionMaterial`
type UnsignedIdentityResolverReference<'a>: Borrow<UnsignedSectionIdentityResolutionMaterial>
where
Self: 'a;
/// Constructs or references the identity resolution material for signed sections
fn signed_identity_resolution_material<C: CryptoProvider>(
&self,
) -> Self::SignedIdentityResolverReference<'_>;
/// Constructs or references the identity resolution material for unsigned sections
fn unsigned_identity_resolution_material<C: CryptoProvider>(
&self,
) -> Self::UnsignedIdentityResolverReference<'_>;
/// Constructs or copies non-identity-resolution deserialization material for signed
/// sections.
///
/// Note: We mandate "copies" here due to the relatively small size of verification-only crypto
/// materials (32 bytes).
fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial;
/// Constructs or copies non-identity-resolution deserialization material for unsigned
/// sections.
///
/// Note: We mandate "copies" here due to the relatively small size of verification-only crypto
/// materials (32 bytes).
fn unsigned_verification_material<C: CryptoProvider>(
&self,
) -> UnsignedSectionVerificationMaterial;
/// Constructs pre-calculated crypto material out of this crypto-material.
fn to_precalculated<C: CryptoProvider>(self) -> PrecalculatedV1CryptoMaterial
where
Self: Sized,
{
let signed_identity_resolution_material =
self.signed_identity_resolution_material::<C>().borrow().clone();
let unsigned_identity_resolution_material =
self.unsigned_identity_resolution_material::<C>().borrow().clone();
let signed_verification_material = self.signed_verification_material::<C>();
let unsigned_verification_material = self.unsigned_verification_material::<C>();
PrecalculatedV1CryptoMaterial {
signed_identity_resolution_material,
unsigned_identity_resolution_material,
signed_verification_material,
unsigned_verification_material,
}
}
}
/// [`V1CryptoMaterial`] that minimizes CPU time when providing key material at
/// the expense of occupied memory
pub struct PrecalculatedV1CryptoMaterial {
pub(crate) signed_identity_resolution_material: SignedSectionIdentityResolutionMaterial,
pub(crate) unsigned_identity_resolution_material: UnsignedSectionIdentityResolutionMaterial,
pub(crate) signed_verification_material: SignedSectionVerificationMaterial,
pub(crate) unsigned_verification_material: UnsignedSectionVerificationMaterial,
}
impl V1CryptoMaterial for PrecalculatedV1CryptoMaterial {
type SignedIdentityResolverReference<'a> = &'a SignedSectionIdentityResolutionMaterial
where Self: 'a;
type UnsignedIdentityResolverReference<'a> = &'a UnsignedSectionIdentityResolutionMaterial
where Self: 'a;
fn signed_identity_resolution_material<C: CryptoProvider>(
&self,
) -> Self::SignedIdentityResolverReference<'_> {
&self.signed_identity_resolution_material
}
fn unsigned_identity_resolution_material<C: CryptoProvider>(
&self,
) -> Self::UnsignedIdentityResolverReference<'_> {
&self.unsigned_identity_resolution_material
}
fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
self.signed_verification_material.clone()
}
fn unsigned_verification_material<C: CryptoProvider>(
&self,
) -> UnsignedSectionVerificationMaterial {
self.unsigned_verification_material.clone()
}
}
/// [`V1CryptoMaterial`] that minimizes memory footprint at the expense of CPU
/// time when providing derived key material.
pub struct MinimumFootprintV1CryptoMaterial {
key_seed: [u8; 32],
expected_unsigned_metadata_key_hmac: [u8; 32],
expected_signed_metadata_key_hmac: [u8; 32],
pub_key: ed25519::RawPublicKey,
}
impl MinimumFootprintV1CryptoMaterial {
/// Construct an [MinimumFootprintV1CryptoMaterial] from the provided identity data.
pub fn new<C: CryptoProvider>(
key_seed: [u8; 32],
expected_unsigned_metadata_key_hmac: [u8; 32],
expected_signed_metadata_key_hmac: [u8; 32],
pub_key: np_ed25519::PublicKey<C>,
) -> Self {
Self {
key_seed,
expected_unsigned_metadata_key_hmac,
expected_signed_metadata_key_hmac,
pub_key: pub_key.to_bytes(),
}
}
}
impl V1CryptoMaterial for MinimumFootprintV1CryptoMaterial {
type SignedIdentityResolverReference<'a> = SignedSectionIdentityResolutionMaterial
where Self: 'a;
type UnsignedIdentityResolverReference<'a> = UnsignedSectionIdentityResolutionMaterial
where Self: 'a;
fn signed_identity_resolution_material<C: CryptoProvider>(
&self,
) -> Self::SignedIdentityResolverReference<'_> {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
SignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
&hkdf,
self.expected_signed_metadata_key_hmac,
)
}
fn unsigned_identity_resolution_material<C: CryptoProvider>(
&self,
) -> Self::UnsignedIdentityResolverReference<'_> {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
UnsignedSectionIdentityResolutionMaterial::from_hkdf_and_expected_metadata_key_hmac(
&hkdf,
self.expected_unsigned_metadata_key_hmac,
)
}
fn signed_verification_material<C: CryptoProvider>(&self) -> SignedSectionVerificationMaterial {
SignedSectionVerificationMaterial { pub_key: self.pub_key }
}
fn unsigned_verification_material<C: CryptoProvider>(
&self,
) -> UnsignedSectionVerificationMaterial {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
let mic_hmac_key = *UnsignedSectionKeys::hmac_key(&hkdf).as_bytes();
UnsignedSectionVerificationMaterial { mic_hmac_key }
}
}