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