• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021, The Android Open Source Project
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 //! Implement ECDH-based encryption.
16 
17 use crate::ks_err;
18 use anyhow::{Context, Result};
19 use keystore2_crypto::{
20     aes_gcm_decrypt, aes_gcm_encrypt, ec_key_generate_key, ec_key_get0_public_key,
21     ec_key_marshal_private_key, ec_key_parse_private_key, ec_point_oct_to_point,
22     ec_point_point_to_oct, ecdh_compute_key, generate_salt, hkdf_expand, hkdf_extract, ECKey, ZVec,
23     AES_256_KEY_LENGTH,
24 };
25 
26 /// Private key for ECDH encryption.
27 pub struct ECDHPrivateKey(ECKey);
28 
29 impl ECDHPrivateKey {
30     /// Randomly generate a fresh keypair.
generate() -> Result<ECDHPrivateKey>31     pub fn generate() -> Result<ECDHPrivateKey> {
32         ec_key_generate_key().map(ECDHPrivateKey).context(ks_err!("generation failed"))
33     }
34 
35     /// Deserialize bytes into an ECDH keypair
from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey>36     pub fn from_private_key(buf: &[u8]) -> Result<ECDHPrivateKey> {
37         ec_key_parse_private_key(buf).map(ECDHPrivateKey).context(ks_err!("parsing failed"))
38     }
39 
40     /// Serialize the ECDH key into bytes
private_key(&self) -> Result<ZVec>41     pub fn private_key(&self) -> Result<ZVec> {
42         ec_key_marshal_private_key(&self.0).context(ks_err!("marshalling failed"))
43     }
44 
45     /// Generate the serialization of the corresponding public key
public_key(&self) -> Result<Vec<u8>>46     pub fn public_key(&self) -> Result<Vec<u8>> {
47         let point = ec_key_get0_public_key(&self.0);
48         ec_point_point_to_oct(point.get_point()).context(ks_err!("marshalling failed"))
49     }
50 
51     /// Use ECDH to agree an AES key with another party whose public key we have.
52     /// Sender and recipient public keys are passed separately because they are
53     /// 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     fn agree_key(
55         &self,
56         salt: &[u8],
57         other_public_key: &[u8],
58         sender_public_key: &[u8],
59         recipient_public_key: &[u8],
60     ) -> Result<ZVec> {
61         let hkdf = hkdf_extract(sender_public_key, salt)
62             .context(ks_err!("hkdf_extract on sender_public_key failed"))?;
63         let hkdf = hkdf_extract(recipient_public_key, &hkdf)
64             .context(ks_err!("hkdf_extract on recipient_public_key failed"))?;
65         let other_public_key = ec_point_oct_to_point(other_public_key)
66             .context(ks_err!("ec_point_oct_to_point failed"))?;
67         let secret = ecdh_compute_key(other_public_key.get_point(), &self.0)
68             .context(ks_err!("ecdh_compute_key failed"))?;
69         let prk = hkdf_extract(&secret, &hkdf).context(ks_err!("hkdf_extract on secret failed"))?;
70 
71         let aes_key = hkdf_expand(AES_256_KEY_LENGTH, &prk, b"AES-256-GCM key")
72             .context(ks_err!("hkdf_expand failed"))?;
73         Ok(aes_key)
74     }
75 
76     /// 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     pub fn encrypt_message(
78         recipient_public_key: &[u8],
79         message: &[u8],
80     ) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>)> {
81         let sender_key = Self::generate().context(ks_err!("generate failed"))?;
82         let sender_public_key = sender_key.public_key().context(ks_err!("public_key failed"))?;
83         let salt = generate_salt().context(ks_err!("generate_salt failed"))?;
84         let aes_key = sender_key
85             .agree_key(&salt, recipient_public_key, &sender_public_key, recipient_public_key)
86             .context(ks_err!("agree_key failed"))?;
87         let (ciphertext, iv, tag) =
88             aes_gcm_encrypt(message, &aes_key).context(ks_err!("aes_gcm_encrypt failed"))?;
89         Ok((sender_public_key, salt, iv, ciphertext, tag))
90     }
91 
92     /// Decrypt a message sent to us
decrypt_message( &self, sender_public_key: &[u8], salt: &[u8], iv: &[u8], ciphertext: &[u8], tag: &[u8], ) -> Result<ZVec>93     pub fn decrypt_message(
94         &self,
95         sender_public_key: &[u8],
96         salt: &[u8],
97         iv: &[u8],
98         ciphertext: &[u8],
99         tag: &[u8],
100     ) -> Result<ZVec> {
101         let recipient_public_key = self.public_key()?;
102         let aes_key = self
103             .agree_key(salt, sender_public_key, sender_public_key, &recipient_public_key)
104             .context(ks_err!("agree_key failed"))?;
105         aes_gcm_decrypt(ciphertext, iv, tag, &aes_key).context(ks_err!("aes_gcm_decrypt failed"))
106     }
107 }
108 
109 #[cfg(test)]
110 mod test {
111     use super::*;
112 
113     #[test]
test_crypto_roundtrip() -> Result<()>114     fn test_crypto_roundtrip() -> Result<()> {
115         let message = b"Hello world";
116         let recipient = ECDHPrivateKey::generate()?;
117         let (sender_public_key, salt, iv, ciphertext, tag) =
118             ECDHPrivateKey::encrypt_message(&recipient.public_key()?, message)?;
119         let recipient = ECDHPrivateKey::from_private_key(&recipient.private_key()?)?;
120         let decrypted =
121             recipient.decrypt_message(&sender_public_key, &salt, &iv, &ciphertext, &tag)?;
122         let dc: &[u8] = &decrypted;
123         assert_eq!(message, dc);
124         Ok(())
125     }
126 }
127