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