blob: 84ff7686ecb26821cd3b4974d6df7628cb30b0e4 [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 v0 advertisement-format credentials.
use crate::credential::{protocol_version_seal, DiscoveryMetadataCryptoMaterial, ProtocolVersion};
use crypto_provider::{aead::Aead, aes, aes::ctr, CryptoProvider};
use ldt_np_adv::V0IdentityToken;
use np_hkdf::NpKeySeedHkdf;
/// Cryptographic information about a particular V0 discovery credential
/// necessary to match and decrypt encrypted V0 advertisements.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct V0DiscoveryCredential {
/// The 32-byte key-seed used for generating other key material.
pub key_seed: [u8; 32],
/// The (LDT-variant) HMAC of the identity token.
pub expected_identity_token_hmac: [u8; 32],
}
impl V0DiscoveryCredential {
/// Construct an [V0DiscoveryCredential] from the provided identity data.
pub fn new(key_seed: [u8; 32], expected_identity_token_hmac: [u8; 32]) -> Self {
Self { key_seed, expected_identity_token_hmac }
}
}
impl DiscoveryMetadataCryptoMaterial<V0> for V0DiscoveryCredential {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed).v0_metadata_nonce()
}
}
impl V0DiscoveryCryptoMaterial for V0DiscoveryCredential {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
ldt_np_adv::build_np_adv_decrypter(
&hkdf.v0_ldt_key(),
self.expected_identity_token_hmac,
hkdf.v0_identity_token_hmac_key(),
)
}
}
/// Type-level identifier for the V0 protocol version.
#[derive(Debug, Clone)]
pub enum V0 {}
impl protocol_version_seal::ProtocolVersionSeal for V0 {}
impl ProtocolVersion for V0 {
type DiscoveryCredential = V0DiscoveryCredential;
type IdentityToken = V0IdentityToken;
fn metadata_nonce_from_key_seed<C: CryptoProvider>(
hkdf: &NpKeySeedHkdf<C>,
) -> <C::Aes128Gcm as Aead>::Nonce {
hkdf.v0_metadata_nonce()
}
// TODO should be IdP specific
fn extract_metadata_key<C: CryptoProvider>(metadata_key: V0IdentityToken) -> aes::Aes128Key {
np_hkdf::v0_metadata_expanded_key::<C>(&metadata_key.bytes()).into()
}
}
/// Trait which exists purely to be able to restrict the protocol
/// version of certain type-bounds to V0.
pub trait V0ProtocolVersion: ProtocolVersion {}
impl V0ProtocolVersion for V0 {}
/// Cryptographic material for an individual NP credential used to decrypt v0 advertisements.
/// Unlike [`V0DiscoveryCredential`], derived information about cryptographic materials may
/// be stored in implementors of this trait.
// Space-time tradeoffs:
// - LDT keys (64b) take about 1.4us.
pub trait V0DiscoveryCryptoMaterial: DiscoveryMetadataCryptoMaterial<V0> {
/// Returns an LDT NP advertisement cipher built with the provided `Aes`
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>;
}
/// [`V0DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at
/// the expense of occupied memory.
pub struct PrecalculatedV0DiscoveryCryptoMaterial {
pub(crate) legacy_ldt_key: ldt::LdtKey<xts_aes::XtsAes128Key>,
pub(crate) legacy_identity_token_hmac: [u8; 32],
pub(crate) legacy_identity_token_hmac_key: [u8; 32],
pub(crate) metadata_nonce: ctr::AesCtrNonce,
}
impl PrecalculatedV0DiscoveryCryptoMaterial {
/// Construct a new instance from the provided credential material.
pub(crate) fn new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&discovery_credential.key_seed);
Self {
legacy_ldt_key: hkdf.v0_ldt_key(),
legacy_identity_token_hmac: discovery_credential.expected_identity_token_hmac,
legacy_identity_token_hmac_key: *hkdf.v0_identity_token_hmac_key().as_bytes(),
metadata_nonce: hkdf.v0_metadata_nonce(),
}
}
}
impl DiscoveryMetadataCryptoMaterial<V0> for PrecalculatedV0DiscoveryCryptoMaterial {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
self.metadata_nonce
}
}
impl V0DiscoveryCryptoMaterial for PrecalculatedV0DiscoveryCryptoMaterial {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
ldt_np_adv::build_np_adv_decrypter(
&self.legacy_ldt_key,
self.legacy_identity_token_hmac,
self.legacy_identity_token_hmac_key.into(),
)
}
}
// Implementations for reference types -- we don't provide a blanket impl for references
// due to the potential to conflict with downstream crates' implementations.
impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a V0DiscoveryCredential {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
(*self).metadata_nonce::<C>()
}
}
impl<'a> V0DiscoveryCryptoMaterial for &'a V0DiscoveryCredential {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
(*self).ldt_adv_cipher::<C>()
}
}
impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial {
fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] {
(*self).metadata_nonce::<C>()
}
}
impl<'a> V0DiscoveryCryptoMaterial for &'a PrecalculatedV0DiscoveryCryptoMaterial {
fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> {
(*self).ldt_adv_cipher::<C>()
}
}
/// Crypto material for creating V1 advertisements.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct V0BroadcastCredential {
/// The 32-byte key-seed used for generating other key material.
pub key_seed: [u8; 32],
/// The 14-byte identity-token which identifies the sender.
pub identity_token: V0IdentityToken,
}
impl V0BroadcastCredential {
/// Builds some simple broadcast crypto-materials out of
/// the provided key-seed and version-specific metadata-key.
pub fn new(key_seed: [u8; 32], identity_token: V0IdentityToken) -> Self {
Self { key_seed, identity_token }
}
/// Key seed from which other keys are derived.
pub(crate) fn key_seed(&self) -> [u8; 32] {
self.key_seed
}
/// Identity token that will be incorporated into encrypted advertisements.
pub(crate) fn identity_token(&self) -> V0IdentityToken {
self.identity_token
}
/// Derive a discovery credential with the data necessary to discover advertisements produced
/// by this broadcast credential.
pub fn derive_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential {
let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed);
V0DiscoveryCredential::new(
self.key_seed,
hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(&self.identity_token.bytes()),
)
}
}