Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 1 | // Copyright 2023, 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 | //! Handles the encryption and decryption of the key blob. |
| 16 | |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 17 | use alloc::vec; |
| 18 | use alloc::vec::Vec; |
Alice Wang | 78b35f8 | 2023-10-06 11:57:35 +0000 | [diff] [blame] | 19 | use bssl_avf::{hkdf, rand_bytes, Aead, AeadContext, Digester, AES_GCM_NONCE_LENGTH}; |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 20 | use core::result; |
| 21 | use serde::{Deserialize, Serialize}; |
| 22 | use service_vm_comm::RequestProcessingError; |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 23 | use zeroize::Zeroizing; |
| 24 | |
| 25 | type Result<T> = result::Result<T, RequestProcessingError>; |
| 26 | |
| 27 | /// The KEK (Key Encryption Key) info is used as information to derive the KEK using HKDF. |
| 28 | const KEK_INFO: &[u8] = b"rialto keyblob kek"; |
| 29 | |
| 30 | /// An all-zero nonce is utilized to encrypt the private key. This is because each key |
| 31 | /// undergoes encryption using a distinct KEK, which is derived from a secret and a random |
| 32 | /// salt. Since the uniqueness of the IV/key combination is already guaranteed by the uniqueness |
| 33 | /// of the KEK, there is no need for an additional random nonce. |
| 34 | const PRIVATE_KEY_NONCE: &[u8; AES_GCM_NONCE_LENGTH] = &[0; AES_GCM_NONCE_LENGTH]; |
| 35 | |
| 36 | /// Since Rialto functions as both the sender and receiver of the message, no additional data is |
| 37 | /// needed. |
| 38 | const PRIVATE_KEY_AD: &[u8] = &[]; |
| 39 | |
| 40 | // Encrypted key blob. |
| 41 | #[derive(Clone, Debug, Deserialize, Serialize)] |
| 42 | pub(crate) enum EncryptedKeyBlob { |
| 43 | /// Version 1 key blob. |
| 44 | V1(EncryptedKeyBlobV1), |
| 45 | } |
| 46 | |
| 47 | /// Encrypted key blob version 1. |
| 48 | #[derive(Clone, Debug, Deserialize, Serialize)] |
| 49 | pub(crate) struct EncryptedKeyBlobV1 { |
| 50 | /// Salt used to derive the KEK. |
| 51 | kek_salt: [u8; 32], |
| 52 | |
| 53 | /// Private key encrypted with AES-256-GCM. |
| 54 | encrypted_private_key: Vec<u8>, |
| 55 | } |
| 56 | |
| 57 | impl EncryptedKeyBlob { |
| 58 | pub(crate) fn new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self> { |
| 59 | EncryptedKeyBlobV1::new(private_key, kek_secret).map(Self::V1) |
| 60 | } |
| 61 | |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 62 | pub(crate) fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> { |
| 63 | match self { |
| 64 | Self::V1(blob) => blob.decrypt_private_key(kek_secret), |
| 65 | } |
| 66 | } |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | impl EncryptedKeyBlobV1 { |
| 70 | fn new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self> { |
| 71 | let mut kek_salt = [0u8; 32]; |
| 72 | rand_bytes(&mut kek_salt)?; |
| 73 | let kek = hkdf::<32>(kek_secret, &kek_salt, KEK_INFO, Digester::sha512())?; |
| 74 | |
| 75 | let tag_len = None; |
Alice Wang | 78b35f8 | 2023-10-06 11:57:35 +0000 | [diff] [blame] | 76 | let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?; |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 77 | let mut out = vec![0u8; private_key.len() + aead_ctx.aead().max_overhead()]; |
| 78 | let ciphertext = aead_ctx.seal(private_key, PRIVATE_KEY_NONCE, PRIVATE_KEY_AD, &mut out)?; |
| 79 | |
| 80 | Ok(Self { kek_salt, encrypted_private_key: ciphertext.to_vec() }) |
| 81 | } |
| 82 | |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 83 | fn decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>> { |
| 84 | let kek = hkdf::<32>(kek_secret, &self.kek_salt, KEK_INFO, Digester::sha512())?; |
| 85 | let mut out = Zeroizing::new(vec![0u8; self.encrypted_private_key.len()]); |
| 86 | let tag_len = None; |
Alice Wang | 78b35f8 | 2023-10-06 11:57:35 +0000 | [diff] [blame] | 87 | let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?; |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 88 | let plaintext = aead_ctx.open( |
| 89 | &self.encrypted_private_key, |
| 90 | PRIVATE_KEY_NONCE, |
| 91 | PRIVATE_KEY_AD, |
| 92 | &mut out, |
| 93 | )?; |
| 94 | Ok(Zeroizing::new(plaintext.to_vec())) |
| 95 | } |
| 96 | } |
| 97 | |
Alice Wang | 9aeb406 | 2023-10-30 14:19:38 +0000 | [diff] [blame] | 98 | pub(crate) fn decrypt_private_key( |
| 99 | encrypted_key_blob: &[u8], |
| 100 | kek_secret: &[u8], |
| 101 | ) -> Result<Zeroizing<Vec<u8>>> { |
| 102 | let key_blob: EncryptedKeyBlob = cbor_util::deserialize(encrypted_key_blob)?; |
| 103 | let private_key = key_blob.decrypt_private_key(kek_secret)?; |
| 104 | Ok(private_key) |
| 105 | } |
| 106 | |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 107 | #[cfg(test)] |
| 108 | mod tests { |
| 109 | use super::*; |
| 110 | use bssl_avf::{ApiName, CipherError, Error}; |
| 111 | |
| 112 | /// The test data are generated randomly with /dev/urandom. |
| 113 | const TEST_KEY: [u8; 32] = [ |
| 114 | 0x76, 0xf7, 0xd5, 0x36, 0x1f, 0x78, 0x58, 0x2e, 0x55, 0x2f, 0x88, 0x9d, 0xa3, 0x3e, 0xba, |
| 115 | 0xfb, 0xc1, 0x2b, 0x17, 0x85, 0x24, 0xdc, 0x0e, 0xc4, 0xbf, 0x6d, 0x2e, 0xe8, 0xa8, 0x36, |
| 116 | 0x93, 0x62, |
| 117 | ]; |
| 118 | const TEST_SECRET1: [u8; 32] = [ |
| 119 | 0xac, 0xb1, 0x6b, 0xdf, 0x45, 0x30, 0x20, 0xa5, 0x60, 0x6d, 0x81, 0x07, 0x30, 0x68, 0x6e, |
| 120 | 0x01, 0x3d, 0x5e, 0x86, 0xd6, 0xc6, 0x17, 0xfa, 0xd6, 0xe0, 0xff, 0xd4, 0xf0, 0xb0, 0x7c, |
| 121 | 0x5c, 0x8f, |
| 122 | ]; |
| 123 | const TEST_SECRET2: [u8; 32] = [ |
| 124 | 0x04, 0x6e, 0xca, 0x30, 0x5e, 0x6c, 0x8f, 0xe5, 0x1a, 0x47, 0x12, 0xbc, 0x45, 0xd7, 0xa8, |
| 125 | 0x38, 0xfb, 0x06, 0xc6, 0x44, 0xa1, 0x21, 0x40, 0x0b, 0x48, 0x88, 0xe2, 0x31, 0x64, 0x42, |
| 126 | 0x9d, 0x1c, |
| 127 | ]; |
| 128 | |
| 129 | #[test] |
| 130 | fn decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()> { |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 131 | let encrypted_key_blob = |
| 132 | cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?; |
Alice Wang | 9aeb406 | 2023-10-30 14:19:38 +0000 | [diff] [blame] | 133 | let decrypted_key = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET1)?; |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 134 | |
| 135 | assert_eq!(TEST_KEY, decrypted_key.as_slice()); |
| 136 | Ok(()) |
| 137 | } |
| 138 | |
| 139 | #[test] |
| 140 | fn decrypting_keyblob_fails_with_a_different_kek() -> Result<()> { |
Alice Wang | 5dddeea | 2023-10-13 12:56:22 +0000 | [diff] [blame] | 141 | let encrypted_key_blob = |
| 142 | cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?; |
Alice Wang | 9aeb406 | 2023-10-30 14:19:38 +0000 | [diff] [blame] | 143 | let err = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET2).unwrap_err(); |
Alice Wang | 8b8e6e6 | 2023-10-02 09:10:13 +0000 | [diff] [blame] | 144 | |
| 145 | let expected_err: RequestProcessingError = |
| 146 | Error::CallFailed(ApiName::EVP_AEAD_CTX_open, CipherError::BadDecrypt.into()).into(); |
| 147 | assert_eq!(expected_err, err); |
| 148 | Ok(()) |
| 149 | } |
| 150 | } |