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
17 use alloc::vec;
18 use alloc::vec::Vec;
19 use bssl_avf::{hkdf, rand_bytes, Aead, AeadContext, Digester, AES_GCM_NONCE_LENGTH};
20 use core::result;
21 use serde::{Deserialize, Serialize};
22 use service_vm_comm::RequestProcessingError;
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 {
new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self>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
decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>>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 }
67 }
68
69 impl EncryptedKeyBlobV1 {
new(private_key: &[u8], kek_secret: &[u8]) -> Result<Self>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;
76 let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
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
decrypt_private_key(&self, kek_secret: &[u8]) -> Result<Zeroizing<Vec<u8>>>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;
87 let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
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
decrypt_private_key( encrypted_key_blob: &[u8], kek_secret: &[u8], ) -> Result<Zeroizing<Vec<u8>>>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
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]
decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()>130 fn decrypting_keyblob_succeeds_with_the_same_kek() -> Result<()> {
131 let encrypted_key_blob =
132 cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
133 let decrypted_key = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET1)?;
134
135 assert_eq!(TEST_KEY, decrypted_key.as_slice());
136 Ok(())
137 }
138
139 #[test]
decrypting_keyblob_fails_with_a_different_kek() -> Result<()>140 fn decrypting_keyblob_fails_with_a_different_kek() -> Result<()> {
141 let encrypted_key_blob =
142 cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
143 let err = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET2).unwrap_err();
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 }
151