1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Cryptographic materials for v0 advertisement-format credentials. 16 use crate::credential::{protocol_version_seal, DiscoveryMetadataCryptoMaterial, ProtocolVersion}; 17 use crypto_provider::{aead::Aead, aes, aes::ctr, CryptoProvider}; 18 use ldt_np_adv::V0IdentityToken; 19 use np_hkdf::NpKeySeedHkdf; 20 21 /// Cryptographic information about a particular V0 discovery credential 22 /// necessary to match and decrypt encrypted V0 advertisements. 23 #[derive(Clone, Debug, PartialEq, Eq)] 24 pub struct V0DiscoveryCredential { 25 /// The 32-byte key-seed used for generating other key material. 26 pub key_seed: [u8; 32], 27 28 /// The (LDT-variant) HMAC of the identity token. 29 pub expected_identity_token_hmac: [u8; 32], 30 } 31 32 impl V0DiscoveryCredential { 33 /// Construct an [V0DiscoveryCredential] from the provided identity data. new(key_seed: [u8; 32], expected_identity_token_hmac: [u8; 32]) -> Self34 pub fn new(key_seed: [u8; 32], expected_identity_token_hmac: [u8; 32]) -> Self { 35 Self { key_seed, expected_identity_token_hmac } 36 } 37 } 38 39 impl DiscoveryMetadataCryptoMaterial<V0> for V0DiscoveryCredential { metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]40 fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] { 41 np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed).v0_metadata_nonce() 42 } 43 } 44 45 impl V0DiscoveryCryptoMaterial for V0DiscoveryCredential { ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>46 fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> { 47 let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed); 48 ldt_np_adv::build_np_adv_decrypter( 49 &hkdf.v0_ldt_key(), 50 self.expected_identity_token_hmac, 51 hkdf.v0_identity_token_hmac_key(), 52 ) 53 } 54 } 55 56 /// Type-level identifier for the V0 protocol version. 57 #[derive(Debug, Clone)] 58 pub enum V0 {} 59 60 impl protocol_version_seal::ProtocolVersionSeal for V0 {} 61 62 impl ProtocolVersion for V0 { 63 type DiscoveryCredential = V0DiscoveryCredential; 64 type IdentityToken = V0IdentityToken; 65 metadata_nonce_from_key_seed<C: CryptoProvider>( hkdf: &NpKeySeedHkdf<C>, ) -> <C::Aes128Gcm as Aead>::Nonce66 fn metadata_nonce_from_key_seed<C: CryptoProvider>( 67 hkdf: &NpKeySeedHkdf<C>, 68 ) -> <C::Aes128Gcm as Aead>::Nonce { 69 hkdf.v0_metadata_nonce() 70 } 71 72 // TODO should be IdP specific extract_metadata_key<C: CryptoProvider>(metadata_key: V0IdentityToken) -> aes::Aes128Key73 fn extract_metadata_key<C: CryptoProvider>(metadata_key: V0IdentityToken) -> aes::Aes128Key { 74 np_hkdf::v0_metadata_expanded_key::<C>(&metadata_key.bytes()).into() 75 } 76 } 77 78 /// Trait which exists purely to be able to restrict the protocol 79 /// version of certain type-bounds to V0. 80 pub trait V0ProtocolVersion: ProtocolVersion {} 81 82 impl V0ProtocolVersion for V0 {} 83 84 /// Cryptographic material for an individual NP credential used to decrypt v0 advertisements. 85 /// Unlike [`V0DiscoveryCredential`], derived information about cryptographic materials may 86 /// be stored in implementors of this trait. 87 // Space-time tradeoffs: 88 // - LDT keys (64b) take about 1.4us. 89 pub trait V0DiscoveryCryptoMaterial: DiscoveryMetadataCryptoMaterial<V0> { 90 /// Returns an LDT NP advertisement cipher built with the provided `Aes` ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>91 fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>; 92 } 93 94 /// [`V0DiscoveryCryptoMaterial`] that minimizes CPU time when providing key material at 95 /// the expense of occupied memory. 96 pub struct PrecalculatedV0DiscoveryCryptoMaterial { 97 pub(crate) legacy_ldt_key: ldt::LdtKey<xts_aes::XtsAes128Key>, 98 pub(crate) legacy_identity_token_hmac: [u8; 32], 99 pub(crate) legacy_identity_token_hmac_key: [u8; 32], 100 pub(crate) metadata_nonce: ctr::AesCtrNonce, 101 } 102 103 impl PrecalculatedV0DiscoveryCryptoMaterial { 104 /// Construct a new instance from the provided credential material. new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self105 pub(crate) fn new<C: CryptoProvider>(discovery_credential: &V0DiscoveryCredential) -> Self { 106 let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&discovery_credential.key_seed); 107 Self { 108 legacy_ldt_key: hkdf.v0_ldt_key(), 109 legacy_identity_token_hmac: discovery_credential.expected_identity_token_hmac, 110 legacy_identity_token_hmac_key: *hkdf.v0_identity_token_hmac_key().as_bytes(), 111 metadata_nonce: hkdf.v0_metadata_nonce(), 112 } 113 } 114 } 115 116 impl DiscoveryMetadataCryptoMaterial<V0> for PrecalculatedV0DiscoveryCryptoMaterial { metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]117 fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] { 118 self.metadata_nonce 119 } 120 } 121 122 impl V0DiscoveryCryptoMaterial for PrecalculatedV0DiscoveryCryptoMaterial { ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>123 fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> { 124 ldt_np_adv::build_np_adv_decrypter( 125 &self.legacy_ldt_key, 126 self.legacy_identity_token_hmac, 127 self.legacy_identity_token_hmac_key.into(), 128 ) 129 } 130 } 131 132 // Implementations for reference types -- we don't provide a blanket impl for references 133 // due to the potential to conflict with downstream crates' implementations. 134 135 impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a V0DiscoveryCredential { metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]136 fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] { 137 (*self).metadata_nonce::<C>() 138 } 139 } 140 141 impl<'a> V0DiscoveryCryptoMaterial for &'a V0DiscoveryCredential { ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>142 fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> { 143 (*self).ldt_adv_cipher::<C>() 144 } 145 } 146 147 impl<'a> DiscoveryMetadataCryptoMaterial<V0> for &'a PrecalculatedV0DiscoveryCryptoMaterial { metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12]148 fn metadata_nonce<C: CryptoProvider>(&self) -> [u8; 12] { 149 (*self).metadata_nonce::<C>() 150 } 151 } 152 153 impl<'a> V0DiscoveryCryptoMaterial for &'a PrecalculatedV0DiscoveryCryptoMaterial { ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C>154 fn ldt_adv_cipher<C: CryptoProvider>(&self) -> ldt_np_adv::AuthenticatedNpLdtDecryptCipher<C> { 155 (*self).ldt_adv_cipher::<C>() 156 } 157 } 158 159 /// Crypto material for creating V1 advertisements. 160 #[derive(Clone, Debug, PartialEq, Eq)] 161 pub struct V0BroadcastCredential { 162 /// The 32-byte key-seed used for generating other key material. 163 pub key_seed: [u8; 32], 164 165 /// The 14-byte identity-token which identifies the sender. 166 pub identity_token: V0IdentityToken, 167 } 168 169 impl V0BroadcastCredential { 170 /// Builds some simple broadcast crypto-materials out of 171 /// the provided key-seed and version-specific metadata-key. new(key_seed: [u8; 32], identity_token: V0IdentityToken) -> Self172 pub fn new(key_seed: [u8; 32], identity_token: V0IdentityToken) -> Self { 173 Self { key_seed, identity_token } 174 } 175 176 /// Key seed from which other keys are derived. key_seed(&self) -> [u8; 32]177 pub(crate) fn key_seed(&self) -> [u8; 32] { 178 self.key_seed 179 } 180 181 /// Identity token that will be incorporated into encrypted advertisements. identity_token(&self) -> V0IdentityToken182 pub(crate) fn identity_token(&self) -> V0IdentityToken { 183 self.identity_token 184 } 185 186 /// Derive a discovery credential with the data necessary to discover advertisements produced 187 /// by this broadcast credential. derive_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential188 pub fn derive_discovery_credential<C: CryptoProvider>(&self) -> V0DiscoveryCredential { 189 let hkdf = np_hkdf::NpKeySeedHkdf::<C>::new(&self.key_seed); 190 191 V0DiscoveryCredential::new( 192 self.key_seed, 193 hkdf.v0_identity_token_hmac_key().calculate_hmac::<C>(&self.identity_token.bytes()), 194 ) 195 } 196 } 197