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 //////////////////////////////////////////////////////////////////////////////// 16 17 //! Traits representing crypto abstractions and device specific functionality. 18 use crate::error::Error; 19 use crate::key::{ 20 AesKey, EcExchangeKey, EcExchangeKeyPriv, EcExchangeKeyPub, EcSignKey, EcVerifyKey, EcdhSecret, 21 HmacKey, Identity, IdentityVerificationDecision, MillisecondsSinceEpoch, Nonce12, 22 PseudoRandKey, AES_256_KEY_LEN, SHA_256_LEN, 23 }; 24 use alloc::boxed::Box; 25 use alloc::vec::Vec; 26 use authgraph_wire::SESSION_ID_LEN; 27 use coset::iana::{Algorithm, EllipticCurve}; 28 29 /// The first version of the AuthGraph key exchange protocol 30 pub const AG_KEY_EXCHANGE_PROTOCOL_VERSION_1: i32 = 1; 31 32 /// These trait implementations must be provided by an implementation of the Authgraph API. 33 pub struct CryptoTraitImpl { 34 /// Implementation of AES-GCM functionality. 35 pub aes_gcm: Box<dyn AesGcm>, 36 /// Implementation of ECDH functionality 37 pub ecdh: Box<dyn EcDh>, 38 /// Implementation of ECDSA functionality 39 pub ecdsa: Box<dyn EcDsa>, 40 /// Implementation of HMAC functionality 41 pub hmac: Box<dyn Hmac>, 42 /// Implementation of HKDF functionality 43 pub hkdf: Box<dyn Hkdf>, 44 /// Implementation of cryptographic hash function 45 pub sha256: Box<dyn Sha256>, 46 /// Implementation of secure random number generation 47 pub rng: Box<dyn Rng>, 48 } 49 50 /// Trait methods for AES-GCM with 256 bit key (and 16 bytes tag). 51 pub trait AesGcm: Send { 52 /// Generate a key generate_key(&self, rng: &mut dyn Rng) -> Result<AesKey, Error>53 fn generate_key(&self, rng: &mut dyn Rng) -> Result<AesKey, Error> { 54 let mut key = AesKey([0; AES_256_KEY_LEN]); 55 rng.fill_bytes(&mut key.0); 56 Ok(key) 57 } 58 /// Encrypt encrypt( &self, key: &AesKey, payload: &[u8], aad: &[u8], nonce: &Nonce12, ) -> Result<Vec<u8>, Error>59 fn encrypt( 60 &self, 61 key: &AesKey, 62 payload: &[u8], 63 aad: &[u8], 64 nonce: &Nonce12, 65 ) -> Result<Vec<u8>, Error>; 66 /// Decrypt decrypt( &self, key: &AesKey, cipher_text: &[u8], aad: &[u8], nonce: &Nonce12, ) -> Result<Vec<u8>, Error>67 fn decrypt( 68 &self, 69 key: &AesKey, 70 cipher_text: &[u8], 71 aad: &[u8], 72 nonce: &Nonce12, 73 ) -> Result<Vec<u8>, Error>; 74 /// Emit a copy of the trait implementation, as a boxed trait object. box_clone(&self) -> Box<dyn AesGcm>75 fn box_clone(&self) -> Box<dyn AesGcm>; 76 } 77 78 /// Trait methods for ECDH key exchange with P256 curve 79 pub trait EcDh: Send { 80 /// Generate an EC key with P256 curve for ECDH generate_key(&self) -> Result<EcExchangeKey, Error>81 fn generate_key(&self) -> Result<EcExchangeKey, Error>; 82 /// Compute the ECDH secret. You can safely assume that the `CoseKey` of `EcExchangeKeyPub` has 83 /// been checked for containing approrpriate parameters. compute_shared_secret( &self, own_key: &EcExchangeKeyPriv, peer_key: &EcExchangeKeyPub, ) -> Result<EcdhSecret, Error>84 fn compute_shared_secret( 85 &self, 86 own_key: &EcExchangeKeyPriv, 87 peer_key: &EcExchangeKeyPub, 88 ) -> Result<EcdhSecret, Error>; 89 } 90 91 /// Trait methods for EC sign/verify with EdDSA (with Ed25519 curve) and ECDSA (with P256 curve and 92 /// SHA-256 digest, and P384 curve and SHA-384 digest). 93 pub trait EcDsa: Send { 94 /// Generate an ECDSA keypair identified by the given `curve`. 95 /// 96 /// This method is included in the trait for convenience of AuthGraph users; it is not required 97 /// by AuthGraph itself. generate_key(&self, _curve: EllipticCurve) -> Result<(EcSignKey, EcVerifyKey), Error>98 fn generate_key(&self, _curve: EllipticCurve) -> Result<(EcSignKey, EcVerifyKey), Error> { 99 unimplemented!(); 100 } 101 /// Compute signature over the given data, using the given EC private key. You can mark this as 102 /// unimplemented and instead implement the `sign_data` method in the `Device` trait if the 103 /// private signing key is not readily available (i.e. `get_identity` in the `Device` trait 104 /// returns `None` for the first element of the returned tuple). 105 /// 106 /// The signature should be encoded in a form suitable for use in COSE, specifically: 107 /// - signatures from NIST curves (P-256, P-384) should be encoded as R | S, with the R and S 108 /// values being byte strings with length the same as the key size (RFC 8152 section 8.1). 109 /// Signatures should *not* be enclosed in an ASN.1 DER `Ecdsa-Sig-Value` object. 110 /// - signatures from Edwards curves (Ed25519) should be encoded as raw bytes. sign(&self, sign_key: &EcSignKey, data: &[u8]) -> Result<Vec<u8>, Error>111 fn sign(&self, sign_key: &EcSignKey, data: &[u8]) -> Result<Vec<u8>, Error>; 112 113 /// Verify the signature over the data using the given EC public key. You can safely assume that 114 /// the `Cosekey` of `EcVerifyKey` has been checked for containing the appropriate parameters. 115 /// 116 /// The signature data is present in the form described for `sign` above. verify_signature( &self, verify_key: &EcVerifyKey, data: &[u8], signature: &[u8], ) -> Result<(), Error>117 fn verify_signature( 118 &self, 119 verify_key: &EcVerifyKey, 120 data: &[u8], 121 signature: &[u8], 122 ) -> Result<(), Error>; 123 } 124 125 /// Trait method for computing HMAC with 256-bit key and SHA-256 digest 126 pub trait Hmac: Send { 127 /// Compute HMAC over the given data using the given key compute_hmac(&self, key: &HmacKey, data: &[u8]) -> Result<Vec<u8>, Error>128 fn compute_hmac(&self, key: &HmacKey, data: &[u8]) -> Result<Vec<u8>, Error>; 129 } 130 131 /// Trait methods for extract and expand used in the key derivation process 132 pub trait Hkdf: Send { 133 /// Extract step in key derivation extract(&self, salt: &[u8], ikm: &EcdhSecret) -> Result<PseudoRandKey, Error>134 fn extract(&self, salt: &[u8], ikm: &EcdhSecret) -> Result<PseudoRandKey, Error>; 135 /// Expand step in key derivation expand(&self, prk: &PseudoRandKey, context: &[u8]) -> Result<PseudoRandKey, Error>136 fn expand(&self, prk: &PseudoRandKey, context: &[u8]) -> Result<PseudoRandKey, Error>; 137 } 138 139 /// Trait method for computing cryptographic hash function on an input that outputs 256-bit hash. 140 pub trait Sha256: Send { 141 /// Compute SHA256 over an input compute_sha256(&self, data: &[u8]) -> Result<[u8; SHA_256_LEN], Error>142 fn compute_sha256(&self, data: &[u8]) -> Result<[u8; SHA_256_LEN], Error>; 143 } 144 145 /// Trait methods for generating cryptographically secure random numbers 146 pub trait Rng { 147 /// Create a cryptographically secure random bytes fill_bytes(&self, nonce: &mut [u8])148 fn fill_bytes(&self, nonce: &mut [u8]); 149 /// Emit a copy of the trait implementation, as a boxed trait object. box_clone(&self) -> Box<dyn Rng>150 fn box_clone(&self) -> Box<dyn Rng>; 151 } 152 153 /// Trait methods for retrieving device specific information and for device specific functionality 154 pub trait Device { 155 /// Retrieve (or create) the ephemeral (with per-boot life time) 256-bits encryption key to be 156 /// used with AES-GCM get_or_create_per_boot_key( &self, aes: &dyn AesGcm, rng: &mut dyn Rng, ) -> Result<AesKey, Error>157 fn get_or_create_per_boot_key( 158 &self, 159 aes: &dyn AesGcm, 160 rng: &mut dyn Rng, 161 ) -> Result<AesKey, Error>; 162 /// Retrieve the already created ephemeral (with per-boot life time) 256-bits encryption key to 163 /// be used with AES-GCM. Return an error if it does not exist at the time of this method call. get_per_boot_key(&self) -> Result<AesKey, Error>164 fn get_per_boot_key(&self) -> Result<AesKey, Error>; 165 /// Retrieve the identity of the AuthGraph participant, along with the signing key get_identity(&self) -> Result<(Option<EcSignKey>, Identity), Error>166 fn get_identity(&self) -> Result<(Option<EcSignKey>, Identity), Error>; 167 /// Retrieve the Cose signing algorithm corresponding to the signing key pair of the party. get_cose_sign_algorithm(&self) -> Result<Algorithm, Error>168 fn get_cose_sign_algorithm(&self) -> Result<Algorithm, Error>; 169 /// Compute signature over the given data. This is an alternative to the `sign` method in the 170 /// `EcDsa` trait, when the private signing key is not readily available (i.e. `get_identity` in 171 /// the `Device` trait returns `None` for the first element of the returned tuple). If the 172 /// private signing key is returned from `get_identity` method, you can mark this as 173 /// unimplemented and instead implement `sign` method in the `EcDsa` trait. sign_data(&self, ecdsa: &dyn EcDsa, data: &[u8]) -> Result<Vec<u8>, Error>174 fn sign_data(&self, ecdsa: &dyn EcDsa, data: &[u8]) -> Result<Vec<u8>, Error>; 175 /// Validate the peer's identity (e.g. if `cert_chain` is present, validate the chain of 176 /// signatures and any other required information in the certificate chain) and return the 177 /// signature verification key. 178 /// The default implementation calls the helper functions that perform the minimum required 179 /// validation and extraction of the signature verification key. validate_peer_identity( &self, identity: &Identity, ecdsa: &dyn EcDsa, ) -> Result<EcVerifyKey, Error>180 fn validate_peer_identity( 181 &self, 182 identity: &Identity, 183 ecdsa: &dyn EcDsa, 184 ) -> Result<EcVerifyKey, Error> { 185 identity.validate(ecdsa) 186 } 187 /// Obtain the identity verification decision, by evaluating the latest identity against the 188 /// previous identity. 189 /// 1. If the `previous_identity` contains a `policy`, the `cert_chain` of the `latest_identity` 190 /// is evaluated against that policy to obtain `Match`/`Mismatch` decision. If the decision 191 /// is `Match`, then the `policy` in the `latest_identity` is compared against the `policy` 192 /// in the `previous_identity` to see if the final decision in `Updated`. 193 /// 2. If the `previous_identity` does not contain a `policy`, the `cert_chain` of the 194 /// `latest_identity` is compared byte to byte with the `cert_chain` in the 195 /// `previous_identity`. evaluate_identity( &self, latest_identity: &Identity, previous_identity: &Identity, ) -> Result<IdentityVerificationDecision, Error>196 fn evaluate_identity( 197 &self, 198 latest_identity: &Identity, 199 previous_identity: &Identity, 200 ) -> Result<IdentityVerificationDecision, Error>; 201 /// Retrieve the latest supported version of the key exchange protocol. get_version(&self) -> i32202 fn get_version(&self) -> i32 { 203 // There is only one version now 204 AG_KEY_EXCHANGE_PROTOCOL_VERSION_1 205 } 206 /// Given the latest supported version supported by the peer, select the matching version. This 207 /// is relevant only when there are multiple versions of the protocol and a given party supports 208 /// multiple versions. This is called on sink, as sink is the one who picks the matching version 209 /// based on source's version. get_negotiated_version(&self, _peer_version: i32) -> i32210 fn get_negotiated_version(&self, _peer_version: i32) -> i32 { 211 // There is only one version now 212 AG_KEY_EXCHANGE_PROTOCOL_VERSION_1 213 } 214 /// An internal entry point that hands over the finalized shared key arcs to the device specific 215 /// implementation so that the application protocol that uses the shared keys for secure 216 /// communication can store them in an appropriate way. record_shared_sessions( &mut self, peer_identity: &Identity, session_id: &[u8; SESSION_ID_LEN], shared_keys: &[Vec<u8>; 2], sha256: &dyn Sha256, ) -> Result<(), Error>217 fn record_shared_sessions( 218 &mut self, 219 peer_identity: &Identity, 220 session_id: &[u8; SESSION_ID_LEN], 221 shared_keys: &[Vec<u8>; 2], 222 sha256: &dyn Sha256, 223 ) -> Result<(), Error>; 224 /// The counterpart of `record_shared_sessions` above. Given arc(s) containing the shared keys, 225 /// validate them against the records created at the end of each key exchange protocol instance. 226 /// The validation logic depends on the application protocol which uses the shared keys. validate_shared_sessions( &self, peer_identity: &Identity, session_id: &[u8; SESSION_ID_LEN], shared_keys: &[Vec<u8>], sha256: &dyn Sha256, ) -> Result<(), Error>227 fn validate_shared_sessions( 228 &self, 229 peer_identity: &Identity, 230 session_id: &[u8; SESSION_ID_LEN], 231 shared_keys: &[Vec<u8>], 232 sha256: &dyn Sha256, 233 ) -> Result<(), Error>; 234 } 235 236 /// Trait method to get current time w.r.t a monotonic clock since an epoch that is common between 237 /// the source and sink. 238 pub trait MonotonicClock: Send { 239 /// Get the current time now(&self) -> MillisecondsSinceEpoch240 fn now(&self) -> MillisecondsSinceEpoch; 241 } 242