xref: /aosp_15_r20/system/security/keystore2/src/ec_crypto.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2021, The Android Open Source Project
2*e1997b9aSAndroid Build Coastguard Worker //
3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e1997b9aSAndroid Build Coastguard Worker //
7*e1997b9aSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*e1997b9aSAndroid Build Coastguard Worker //
9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License.
14*e1997b9aSAndroid Build Coastguard Worker 
15*e1997b9aSAndroid Build Coastguard Worker //! Implement ECDH-based encryption.
16*e1997b9aSAndroid Build Coastguard Worker 
17*e1997b9aSAndroid Build Coastguard Worker use crate::ks_err;
18*e1997b9aSAndroid Build Coastguard Worker use anyhow::{Context, Result};
19*e1997b9aSAndroid Build Coastguard Worker use keystore2_crypto::{
20*e1997b9aSAndroid Build Coastguard Worker     aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
21*e1997b9aSAndroid Build Coastguard Worker     ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
22*e1997b9aSAndroid Build Coastguard Worker     ec_point_point_to_oct, ecdh_compute_key, generate_salt, hkdf_expand, hkdf_extract, ECKey, ZVec,
23*e1997b9aSAndroid Build Coastguard Worker     AES_256_KEY_LENGTH,
24*e1997b9aSAndroid Build Coastguard Worker };
25*e1997b9aSAndroid Build Coastguard Worker 
26*e1997b9aSAndroid Build Coastguard Worker /// Private key for ECDH encryption.
27*e1997b9aSAndroid Build Coastguard Worker pub struct ECDHPrivateKey(ECKey);
28*e1997b9aSAndroid Build Coastguard Worker 
29*e1997b9aSAndroid Build Coastguard Worker impl ECDHPrivateKey {
30*e1997b9aSAndroid Build Coastguard Worker     /// Randomly generate a fresh keypair.
generate() -> Result<ECDHPrivateKey>31*e1997b9aSAndroid Build Coastguard Worker     pub fn generate() -> Result<ECDHPrivateKey> {
32*e1997b9aSAndroid Build Coastguard Worker         ec_key_generate_key().map(ECDHPrivateKey).context(ks_err!("generation failed"))
33*e1997b9aSAndroid Build Coastguard Worker     }
34*e1997b9aSAndroid Build Coastguard Worker 
35*e1997b9aSAndroid Build Coastguard Worker     /// Deserialize bytes into an ECDH keypair
from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey>36*e1997b9aSAndroid Build Coastguard Worker     pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
37*e1997b9aSAndroid Build Coastguard Worker         ec_key_parse_private_key(buf).map(ECDHPrivateKey).context(ks_err!("parsing failed"))
38*e1997b9aSAndroid Build Coastguard Worker     }
39*e1997b9aSAndroid Build Coastguard Worker 
40*e1997b9aSAndroid Build Coastguard Worker     /// Serialize the ECDH key into bytes
private_key(&self) -> Result<ZVec>41*e1997b9aSAndroid Build Coastguard Worker     pub fn private_key(&self) -> Result<ZVec> {
42*e1997b9aSAndroid Build Coastguard Worker         ec_key_marshal_private_key(&self.0).context(ks_err!("marshalling failed"))
43*e1997b9aSAndroid Build Coastguard Worker     }
44*e1997b9aSAndroid Build Coastguard Worker 
45*e1997b9aSAndroid Build Coastguard Worker     /// Generate the serialization of the corresponding public key
public_key(&self) -> Result<Vec<u8>>46*e1997b9aSAndroid Build Coastguard Worker     pub fn public_key(&self) -> Result<Vec<u8>> {
47*e1997b9aSAndroid Build Coastguard Worker         let point = ec_key_get0_public_key(&self.0);
48*e1997b9aSAndroid Build Coastguard Worker         ec_point_point_to_oct(point.get_point()).context(ks_err!("marshalling failed"))
49*e1997b9aSAndroid Build Coastguard Worker     }
50*e1997b9aSAndroid Build Coastguard Worker 
51*e1997b9aSAndroid Build Coastguard Worker     /// Use ECDH to agree an AES key with another party whose public key we have.
52*e1997b9aSAndroid Build Coastguard Worker     /// Sender and recipient public keys are passed separately because they are
53*e1997b9aSAndroid Build Coastguard Worker     /// switched in encryption vs decryption.
agree_key( &self, salt: &[u8], other_public_key: &[u8], sender_public_key: &[u8], recipient_public_key: &[u8], ) -> Result<ZVec>54*e1997b9aSAndroid Build Coastguard Worker     fn agree_key(
55*e1997b9aSAndroid Build Coastguard Worker         &self,
56*e1997b9aSAndroid Build Coastguard Worker         salt: &[u8],
57*e1997b9aSAndroid Build Coastguard Worker         other_public_key: &[u8],
58*e1997b9aSAndroid Build Coastguard Worker         sender_public_key: &[u8],
59*e1997b9aSAndroid Build Coastguard Worker         recipient_public_key: &[u8],
60*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<ZVec> {
61*e1997b9aSAndroid Build Coastguard Worker         let hkdf = hkdf_extract(sender_public_key, salt)
62*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("hkdf_extract on sender_public_key failed"))?;
63*e1997b9aSAndroid Build Coastguard Worker         let hkdf = hkdf_extract(recipient_public_key, &hkdf)
64*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("hkdf_extract on recipient_public_key failed"))?;
65*e1997b9aSAndroid Build Coastguard Worker         let other_public_key = ec_point_oct_to_point(other_public_key)
66*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("ec_point_oct_to_point failed"))?;
67*e1997b9aSAndroid Build Coastguard Worker         let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
68*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("ecdh_compute_key failed"))?;
69*e1997b9aSAndroid Build Coastguard Worker         let prk = hkdf_extract(&secret, &hkdf).context(ks_err!("hkdf_extract on secret failed"))?;
70*e1997b9aSAndroid Build Coastguard Worker 
71*e1997b9aSAndroid Build Coastguard Worker         let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
72*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("hkdf_expand failed"))?;
73*e1997b9aSAndroid Build Coastguard Worker         Ok(aes_key)
74*e1997b9aSAndroid Build Coastguard Worker     }
75*e1997b9aSAndroid Build Coastguard Worker 
76*e1997b9aSAndroid Build Coastguard Worker     /// Encrypt a message to the party with the given public key
encrypt_message( recipient_public_key: &[u8], message: &[u8], ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)>77*e1997b9aSAndroid Build Coastguard Worker     pub fn encrypt_message(
78*e1997b9aSAndroid Build Coastguard Worker         recipient_public_key: &[u8],
79*e1997b9aSAndroid Build Coastguard Worker         message: &[u8],
80*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
81*e1997b9aSAndroid Build Coastguard Worker         let sender_key = Self::generate().context(ks_err!("generate failed"))?;
82*e1997b9aSAndroid Build Coastguard Worker         let sender_public_key = sender_key.public_key().context(ks_err!("public_key failed"))?;
83*e1997b9aSAndroid Build Coastguard Worker         let salt = generate_salt().context(ks_err!("generate_salt failed"))?;
84*e1997b9aSAndroid Build Coastguard Worker         let aes_key = sender_key
85*e1997b9aSAndroid Build Coastguard Worker             .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
86*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("agree_key failed"))?;
87*e1997b9aSAndroid Build Coastguard Worker         let (ciphertext, iv, tag) =
88*e1997b9aSAndroid Build Coastguard Worker             aes_gcm_encrypt(message, &aes_key).context(ks_err!("aes_gcm_encrypt failed"))?;
89*e1997b9aSAndroid Build Coastguard Worker         Ok((sender_public_key, salt, iv, ciphertext, tag))
90*e1997b9aSAndroid Build Coastguard Worker     }
91*e1997b9aSAndroid Build Coastguard Worker 
92*e1997b9aSAndroid Build Coastguard Worker     /// Decrypt a message sent to us
decrypt_message( &self, sender_public_key: &[u8], salt: &[u8], iv: &[u8], ciphertext: &[u8], tag: &[u8], ) -> Result<ZVec>93*e1997b9aSAndroid Build Coastguard Worker     pub fn decrypt_message(
94*e1997b9aSAndroid Build Coastguard Worker         &self,
95*e1997b9aSAndroid Build Coastguard Worker         sender_public_key: &[u8],
96*e1997b9aSAndroid Build Coastguard Worker         salt: &[u8],
97*e1997b9aSAndroid Build Coastguard Worker         iv: &[u8],
98*e1997b9aSAndroid Build Coastguard Worker         ciphertext: &[u8],
99*e1997b9aSAndroid Build Coastguard Worker         tag: &[u8],
100*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<ZVec> {
101*e1997b9aSAndroid Build Coastguard Worker         let recipient_public_key = self.public_key()?;
102*e1997b9aSAndroid Build Coastguard Worker         let aes_key = self
103*e1997b9aSAndroid Build Coastguard Worker             .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
104*e1997b9aSAndroid Build Coastguard Worker             .context(ks_err!("agree_key failed"))?;
105*e1997b9aSAndroid Build Coastguard Worker         aes_gcm_decrypt(ciphertext, iv, tag, &aes_key).context(ks_err!("aes_gcm_decrypt failed"))
106*e1997b9aSAndroid Build Coastguard Worker     }
107*e1997b9aSAndroid Build Coastguard Worker }
108*e1997b9aSAndroid Build Coastguard Worker 
109*e1997b9aSAndroid Build Coastguard Worker #[cfg(test)]
110*e1997b9aSAndroid Build Coastguard Worker mod test {
111*e1997b9aSAndroid Build Coastguard Worker     use super::*;
112*e1997b9aSAndroid Build Coastguard Worker 
113*e1997b9aSAndroid Build Coastguard Worker     #[test]
test_crypto_roundtrip() -> Result<()>114*e1997b9aSAndroid Build Coastguard Worker     fn test_crypto_roundtrip() -> Result<()> {
115*e1997b9aSAndroid Build Coastguard Worker         let message = b"Hello world";
116*e1997b9aSAndroid Build Coastguard Worker         let recipient = ECDHPrivateKey::generate()?;
117*e1997b9aSAndroid Build Coastguard Worker         let (sender_public_key, salt, iv, ciphertext, tag) =
118*e1997b9aSAndroid Build Coastguard Worker             ECDHPrivateKey::encrypt_message(&recipient.public_key()?, message)?;
119*e1997b9aSAndroid Build Coastguard Worker         let recipient = ECDHPrivateKey::from_private_key(&recipient.private_key()?)?;
120*e1997b9aSAndroid Build Coastguard Worker         let decrypted =
121*e1997b9aSAndroid Build Coastguard Worker             recipient.decrypt_message(&sender_public_key, &salt, &iv, &ciphertext, &tag)?;
122*e1997b9aSAndroid Build Coastguard Worker         let dc: &[u8] = &decrypted;
123*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(message, dc);
124*e1997b9aSAndroid Build Coastguard Worker         Ok(())
125*e1997b9aSAndroid Build Coastguard Worker     }
126*e1997b9aSAndroid Build Coastguard Worker }
127