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