blob: 1fb7a6788ea9b0a6fe2b0fcf6535d4ac2b12bfd6 [file] [log] [blame]
Alice Wang8b8e6e62023-10-02 09:10:13 +00001// 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 Wang8b8e6e62023-10-02 09:10:13 +000017use alloc::vec;
18use alloc::vec::Vec;
Alice Wang78b35f82023-10-06 11:57:35 +000019use bssl_avf::{hkdf, rand_bytes, Aead, AeadContext, Digester, AES_GCM_NONCE_LENGTH};
Alice Wang8b8e6e62023-10-02 09:10:13 +000020use core::result;
21use serde::{Deserialize, Serialize};
22use service_vm_comm::RequestProcessingError;
Alice Wang8b8e6e62023-10-02 09:10:13 +000023use zeroize::Zeroizing;
24
25type Result<T> = result::Result<T, RequestProcessingError>;
26
27/// The KEK (Key Encryption Key) info is used as information to derive the KEK using HKDF.
28const 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.
34const 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.
38const PRIVATE_KEY_AD: &[u8] = &[];
39
40// Encrypted key blob.
41#[derive(Clone, Debug, Deserialize, Serialize)]
42pub(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)]
49pub(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
57impl 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 Wang8b8e6e62023-10-02 09:10:13 +000062 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 Wang8b8e6e62023-10-02 09:10:13 +000067}
68
69impl 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 Wang78b35f82023-10-06 11:57:35 +000076 let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
Alice Wang8b8e6e62023-10-02 09:10:13 +000077 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 Wang8b8e6e62023-10-02 09:10:13 +000083 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 Wang78b35f82023-10-06 11:57:35 +000087 let aead_ctx = AeadContext::new(Aead::aes_256_gcm(), kek.as_slice(), tag_len)?;
Alice Wang8b8e6e62023-10-02 09:10:13 +000088 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 Wang9aeb4062023-10-30 14:19:38 +000098pub(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 Wang8b8e6e62023-10-02 09:10:13 +0000107#[cfg(test)]
108mod 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 Wang5dddeea2023-10-13 12:56:22 +0000131 let encrypted_key_blob =
132 cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
Alice Wang9aeb4062023-10-30 14:19:38 +0000133 let decrypted_key = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET1)?;
Alice Wang8b8e6e62023-10-02 09:10:13 +0000134
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 Wang5dddeea2023-10-13 12:56:22 +0000141 let encrypted_key_blob =
142 cbor_util::serialize(&EncryptedKeyBlob::new(&TEST_KEY, &TEST_SECRET1)?)?;
Alice Wang9aeb4062023-10-30 14:19:38 +0000143 let err = decrypt_private_key(&encrypted_key_blob, &TEST_SECRET2).unwrap_err();
Alice Wang8b8e6e62023-10-02 09:10:13 +0000144
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}