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