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