blob: 338bdb9bdc0e298bcfa6d8c8aeb51e2dff7b7026 [file] [log] [blame]
Joel Galensonca0efb12020-10-01 14:32:30 -07001// Copyright 2020, 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
Janis Danisevskis9d90b812020-11-25 21:02:11 -080015//! This module implements safe wrappers for some crypto operations required by
16//! Keystore 2.0.
17
18mod error;
19mod zvec;
20pub use error::Error;
21use keystore2_crypto_bindgen::{
22 generateKeyFromPassword, randomBytes, size_t, AES_gcm_decrypt, AES_gcm_encrypt,
23};
24pub use zvec::ZVec;
25
26/// Length of the expected initialization vector.
27pub const IV_LENGTH: usize = 16;
28/// Length of the expected AEAD TAG.
29pub const TAG_LENGTH: usize = 16;
30/// Length of an AES 256 key in bytes.
31pub const AES_256_KEY_LENGTH: usize = 32;
32/// Length of an AES 128 key in bytes.
33pub const AES_128_KEY_LENGTH: usize = 16;
34/// Length of the expected salt for key from password generation.
35pub const SALT_LENGTH: usize = 16;
36
37// This is the number of bytes of the GCM IV that is expected to be initialized
38// with random bytes.
39const GCM_IV_LENGTH: usize = 12;
40
41/// Generate an AES256 key, essentially 32 random bytes from the underlying
42/// boringssl library discretely stuffed into a ZVec.
43pub fn generate_aes256_key() -> Result<ZVec, Error> {
44 // Safety: key has the same length as the requested number of random bytes.
45 let mut key = ZVec::new(AES_256_KEY_LENGTH)?;
46 if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH as size_t) } {
47 Ok(key)
48 } else {
49 Err(Error::RandomNumberGenerationFailed)
50 }
51}
52
53/// Generate a salt.
54pub fn generate_salt() -> Result<Vec<u8>, Error> {
55 // Safety: salt has the same length as the requested number of random bytes.
56 let mut salt = vec![0; SALT_LENGTH];
57 if unsafe { randomBytes(salt.as_mut_ptr(), SALT_LENGTH as size_t) } {
58 Ok(salt)
59 } else {
60 Err(Error::RandomNumberGenerationFailed)
61 }
62}
63
64/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
65/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
66/// on the key length.
67/// This function returns the plaintext message in a ZVec because it is assumed that
68/// it contains sensitive information that should be zeroed from memory before its buffer is
69/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
70/// in a ZVec as well.
71pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
72 if iv.len() != IV_LENGTH {
73 return Err(Error::InvalidIvLength);
74 }
75
76 if tag.len() != TAG_LENGTH {
77 return Err(Error::InvalidAeadTagLength);
78 }
79
80 match key.len() {
81 AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
82 _ => return Err(Error::InvalidKeyLength),
83 }
84
85 let mut result = ZVec::new(data.len())?;
86
87 // Safety: The first two arguments must point to buffers with a size given by the third
88 // argument. The key must have a size of 16 or 32 bytes which we check above.
89 // The iv and tag arguments must be 16 bytes, which we also check above.
90 match unsafe {
91 AES_gcm_decrypt(
92 data.as_ptr(),
93 result.as_mut_ptr(),
94 data.len() as size_t,
95 key.as_ptr(),
96 key.len() as size_t,
97 iv.as_ptr(),
98 tag.as_ptr(),
99 )
100 } {
101 true => Ok(result),
102 false => Err(Error::DecryptionFailed),
103 }
104}
105
106/// Uses AES GCM to encrypt a message given a key.
107/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
108/// the key length. The function generates an initialization vector. The return value is a tuple
109/// of `(ciphertext, iv, tag)`.
110pub fn aes_gcm_encrypt(data: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
111 let mut iv = vec![0; IV_LENGTH];
112 // Safety: iv is longer than GCM_IV_LENGTH, which is 12 while IV_LENGTH is 16.
113 // The iv needs to be 16 bytes long, but the last 4 bytes remain zeroed.
114 if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH as size_t) } {
115 return Err(Error::RandomNumberGenerationFailed);
116 }
117
118 match key.len() {
119 AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
120 _ => return Err(Error::InvalidKeyLength),
121 }
122
123 let mut result: Vec<u8> = vec![0; data.len()];
124 let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
125 match unsafe {
126 AES_gcm_encrypt(
127 data.as_ptr(),
128 result.as_mut_ptr(),
129 data.len() as size_t,
130 key.as_ptr(),
131 key.len() as size_t,
132 iv.as_ptr(),
133 tag.as_mut_ptr(),
134 )
135 } {
136 true => Ok((result, iv, tag)),
137 false => Err(Error::EncryptionFailed),
138 }
139}
140
141/// Generates a key from the given password and salt.
142/// The salt must be exactly 16 bytes long.
143/// Two key sizes are accepted: 16 and 32 bytes.
144pub fn derive_key_from_password(
145 pw: &[u8],
146 salt: Option<&[u8]>,
147 key_length: usize,
148) -> Result<ZVec, Error> {
149 let salt: *const u8 = match salt {
150 Some(s) => {
151 if s.len() != SALT_LENGTH {
152 return Err(Error::InvalidSaltLength);
153 }
154 s.as_ptr()
155 }
156 None => std::ptr::null(),
157 };
158
159 match key_length {
160 AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
161 _ => return Err(Error::InvalidKeyLength),
162 }
163
164 let mut result = ZVec::new(key_length)?;
165
166 unsafe {
167 generateKeyFromPassword(
168 result.as_mut_ptr(),
169 result.len() as size_t,
170 pw.as_ptr() as *const std::os::raw::c_char,
171 pw.len() as size_t,
172 salt,
173 )
174 };
175
176 Ok(result)
177}
Joel Galenson46d6fd02020-11-19 17:58:33 -0800178
Joel Galensonca0efb12020-10-01 14:32:30 -0700179#[cfg(test)]
180mod tests {
181
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800182 use super::*;
Joel Galensonca0efb12020-10-01 14:32:30 -0700183 use keystore2_crypto_bindgen::{
184 generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
185 };
186
187 #[test]
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800188 fn test_wrapper_roundtrip() {
189 let key = generate_aes256_key().unwrap();
190 let message = b"totally awesome message";
191 let (cipher_text, iv, tag) = aes_gcm_encrypt(message, &key).unwrap();
192 let message2 = aes_gcm_decrypt(&cipher_text, &iv, &tag, &key).unwrap();
193 assert_eq!(message[..], message2[..])
194 }
195
196 #[test]
Joel Galensonca0efb12020-10-01 14:32:30 -0700197 fn test_encrypt_decrypt() {
198 let input = vec![0; 16];
199 let mut out = vec![0; 16];
200 let mut out2 = vec![0; 16];
201 let key = vec![0; 16];
202 let iv = vec![0; 12];
203 let mut tag = vec![0; 16];
204 unsafe {
205 let res = AES_gcm_encrypt(
206 input.as_ptr(),
207 out.as_mut_ptr(),
208 16,
209 key.as_ptr(),
210 16,
211 iv.as_ptr(),
212 tag.as_mut_ptr(),
213 );
214 assert!(res);
215 assert_ne!(out, input);
216 assert_ne!(tag, input);
217 let res = AES_gcm_decrypt(
218 out.as_ptr(),
219 out2.as_mut_ptr(),
220 16,
221 key.as_ptr(),
222 16,
223 iv.as_ptr(),
224 tag.as_ptr(),
225 );
226 assert!(res);
227 assert_eq!(out2, input);
228 }
229 }
230
231 #[test]
232 fn test_create_key_id() {
233 let blob = vec![0; 16];
234 let mut out: u64 = 0;
235 unsafe {
236 let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
237 assert!(res);
238 assert_ne!(out, 0);
239 }
240 }
241
242 #[test]
243 fn test_generate_key_from_password() {
244 let mut key = vec![0; 16];
245 let pw = vec![0; 16];
246 let mut salt = vec![0; 16];
247 unsafe {
248 generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
249 }
250 assert_ne!(key, vec![0; 16]);
251 }
252}