blob: 07cb3f93fa522f0f425d8e3a5c9711b46053e53d [file] [log] [blame]
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +00001// Copyright 2022, 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//! This module implements test utils to generate various types of keys.
16
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +000017use anyhow::Result;
18
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +000019use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Rajesh Nyamagoud11912ea2021-12-20 20:37:20 +000020 Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
21 ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +000022};
23use android_system_keystore2::aidl::android::system::keystore2::{
24 Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +000025 KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +000026};
27
28use crate::authorizations::AuthSetBuilder;
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +000029use android_system_keystore2::binder::{ExceptionCode, Result as BinderResult};
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +000030
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +000031/// Shell namespace.
32pub const SELINUX_SHELL_NAMESPACE: i64 = 1;
Rajesh Nyamagouddc6fb232021-12-08 21:27:15 +000033/// Vold namespace.
34pub const SELINUX_VOLD_NAMESPACE: i64 = 100;
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +000035
Rajesh Nyamagoudfa7c0f12021-12-02 17:15:48 +000036/// SU context.
37pub const TARGET_SU_CTX: &str = "u:r:su:s0";
38
39/// Vold context
40pub const TARGET_VOLD_CTX: &str = "u:r:vold:s0";
41
Rajesh Nyamagoud11912ea2021-12-20 20:37:20 +000042/// Key parameters to generate a key.
43pub struct KeyParams {
44 /// Key Size.
45 pub key_size: i32,
46 /// Key Purposes.
47 pub purpose: Vec<KeyPurpose>,
48 /// Padding Mode.
49 pub padding: Option<PaddingMode>,
50 /// Digest.
51 pub digest: Option<Digest>,
52 /// MFG Digest.
53 pub mgf_digest: Option<Digest>,
54 /// Block Mode.
55 pub block_mode: Option<BlockMode>,
56 /// Attestation challenge.
57 pub att_challenge: Option<Vec<u8>>,
58 /// Attestation app id.
59 pub att_app_id: Option<Vec<u8>>,
60}
61
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +000062/// To map Keystore errors.
63#[derive(thiserror::Error, Debug, Eq, PartialEq)]
64pub enum Error {
65 /// Keystore2 error code
66 #[error("ResponseCode {0:?}")]
67 Rc(ResponseCode),
68 /// Keymint error code
69 #[error("ErrorCode {0:?}")]
70 Km(ErrorCode),
71 /// Exception
72 #[error("Binder exception {0:?}")]
73 Binder(ExceptionCode),
Rajesh Nyamagoud4d483372022-02-09 01:38:23 +000074 /// This is returned if the C implementation of extractSubjectFromCertificate failed.
75 #[error("Failed to validate certificate chain.")]
76 ValidateCertChainFailed,
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +000077}
78
79/// Keystore2 error mapping.
80pub fn map_ks_error<T>(r: BinderResult<T>) -> Result<T, Error> {
81 r.map_err(|s| {
82 match s.exception_code() {
83 ExceptionCode::SERVICE_SPECIFIC => {
84 match s.service_specific_error() {
85 se if se < 0 => {
86 // Negative service specific errors are KM error codes.
87 Error::Km(ErrorCode(se))
88 }
89 se => {
90 // Positive service specific errors are KS response codes.
91 Error::Rc(ResponseCode(se))
92 }
93 }
94 }
95 // We create `Error::Binder` to preserve the exception code
96 // for logging.
97 e_code => Error::Binder(e_code),
98 }
99 })
100}
101
102/// Generate EC Key using given security level and domain with below key parameters and
103/// optionally allow the generated key to be attested with factory provisioned attest key using
104/// given challenge and application id -
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000105/// Purposes: SIGN and VERIFY
106/// Digest: SHA_2_256
107/// Curve: P_256
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000108pub fn generate_ec_p256_signing_key(
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000109 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000110 domain: Domain,
111 nspace: i64,
112 alias: Option<String>,
113 att_challenge: Option<&[u8]>,
114 att_app_id: Option<&[u8]>,
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000115) -> binder::Result<KeyMetadata> {
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000116 let mut key_attest = false;
117 let mut gen_params = AuthSetBuilder::new()
Rajesh Nyamagoudc7d064d2022-08-20 01:45:17 +0000118 .no_auth_required()
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000119 .algorithm(Algorithm::EC)
120 .purpose(KeyPurpose::SIGN)
121 .purpose(KeyPurpose::VERIFY)
122 .digest(Digest::SHA_2_256)
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000123 .ec_curve(EcCurve::P_256);
124
125 if let Some(challenge) = att_challenge {
126 key_attest = true;
127 gen_params = gen_params.clone().attestation_challenge(challenge.to_vec());
128 }
129
130 if let Some(app_id) = att_app_id {
131 key_attest = true;
132 gen_params = gen_params.clone().attestation_app_id(app_id.to_vec());
133 }
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000134
135 match sec_level.generateKey(
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000136 &KeyDescriptor { domain, nspace, alias, blob: None },
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000137 None,
138 &gen_params,
139 0,
140 b"entropy",
141 ) {
142 Ok(key_metadata) => {
143 assert!(key_metadata.certificate.is_some());
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000144 if key_attest {
145 assert!(key_metadata.certificateChain.is_some());
146 }
147 if domain == Domain::BLOB {
148 assert!(key_metadata.key.blob.is_some());
149 }
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000150
151 Ok(key_metadata)
152 }
153 Err(e) => Err(e),
154 }
155}
Rajesh Nyamagouda7766452021-12-13 21:44:19 +0000156
157/// Generate EC signing key.
Rajesh Nyamagoudc7d064d2022-08-20 01:45:17 +0000158pub fn generate_ec_key(
159 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
Rajesh Nyamagouda7766452021-12-13 21:44:19 +0000160 domain: Domain,
161 nspace: i64,
162 alias: Option<String>,
163 ec_curve: EcCurve,
164 digest: Digest,
165) -> binder::Result<KeyMetadata> {
166 let gen_params = AuthSetBuilder::new()
167 .no_auth_required()
168 .algorithm(Algorithm::EC)
169 .purpose(KeyPurpose::SIGN)
170 .purpose(KeyPurpose::VERIFY)
171 .digest(digest)
172 .ec_curve(ec_curve);
173
174 let key_metadata = sec_level.generateKey(
175 &KeyDescriptor { domain, nspace, alias, blob: None },
176 None,
177 &gen_params,
178 0,
179 b"entropy",
180 )?;
181
182 // Must have a public key.
183 assert!(key_metadata.certificate.is_some());
184
185 // Should not have an attestation record.
186 assert!(key_metadata.certificateChain.is_none());
187
188 if domain == Domain::BLOB {
189 assert!(key_metadata.key.blob.is_some());
190 } else {
191 assert!(key_metadata.key.blob.is_none());
192 }
193 Ok(key_metadata)
194}
Rajesh Nyamagoud11912ea2021-12-20 20:37:20 +0000195
196/// Generate a RSA key with the given key parameters, alias, domain and namespace.
197pub fn generate_rsa_key(
198 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
199 domain: Domain,
200 nspace: i64,
201 alias: Option<String>,
202 key_params: &KeyParams,
203 attest_key: Option<&KeyDescriptor>,
204) -> binder::Result<KeyMetadata> {
205 let mut gen_params = AuthSetBuilder::new()
206 .no_auth_required()
207 .algorithm(Algorithm::RSA)
208 .rsa_public_exponent(65537)
209 .key_size(key_params.key_size);
210
211 for purpose in &key_params.purpose {
212 gen_params = gen_params.purpose(*purpose);
213 }
214 if let Some(value) = key_params.digest {
215 gen_params = gen_params.digest(value)
216 }
217 if let Some(value) = key_params.padding {
218 gen_params = gen_params.padding_mode(value);
219 }
220 if let Some(value) = key_params.mgf_digest {
221 gen_params = gen_params.mgf_digest(value);
222 }
223 if let Some(value) = key_params.block_mode {
224 gen_params = gen_params.block_mode(value)
225 }
226 if let Some(value) = &key_params.att_challenge {
227 gen_params = gen_params.attestation_challenge(value.to_vec())
228 }
229 if let Some(value) = &key_params.att_app_id {
230 gen_params = gen_params.attestation_app_id(value.to_vec())
231 }
232
233 let key_metadata = sec_level.generateKey(
234 &KeyDescriptor { domain, nspace, alias, blob: None },
235 attest_key,
236 &gen_params,
237 0,
238 b"entropy",
239 )?;
240
241 // Must have a public key.
242 assert!(key_metadata.certificate.is_some());
243
244 if attest_key.is_none() && key_params.att_challenge.is_some() && key_params.att_app_id.is_some()
245 {
246 // Should have an attestation record.
247 assert!(key_metadata.certificateChain.is_some());
248 } else {
249 // Should not have an attestation record.
250 assert!(key_metadata.certificateChain.is_none());
251 }
252
253 assert!(
254 (domain == Domain::BLOB && key_metadata.key.blob.is_some())
255 || key_metadata.key.blob.is_none()
256 );
257
258 Ok(key_metadata)
259}
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000260
Rajesh Nyamagoudc3523ba2022-08-05 17:38:25 +0000261/// Generate AES/3DES key.
262pub fn generate_sym_key(
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000263 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
Rajesh Nyamagoudc3523ba2022-08-05 17:38:25 +0000264 algorithm: Algorithm,
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000265 size: i32,
266 alias: &str,
267 padding_mode: &PaddingMode,
268 block_mode: &BlockMode,
269 min_mac_len: Option<i32>,
270) -> binder::Result<KeyMetadata> {
271 let mut gen_params = AuthSetBuilder::new()
272 .no_auth_required()
Rajesh Nyamagoudc3523ba2022-08-05 17:38:25 +0000273 .algorithm(algorithm)
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000274 .purpose(KeyPurpose::ENCRYPT)
275 .purpose(KeyPurpose::DECRYPT)
276 .key_size(size)
277 .padding_mode(*padding_mode)
278 .block_mode(*block_mode);
279
280 if let Some(val) = min_mac_len {
281 gen_params = gen_params.min_mac_length(val);
282 }
283
284 let key_metadata = sec_level.generateKey(
285 &KeyDescriptor {
286 domain: Domain::APP,
287 nspace: -1,
288 alias: Some(alias.to_string()),
289 blob: None,
290 },
291 None,
292 &gen_params,
293 0,
294 b"entropy",
295 )?;
296
297 // Should not have public certificate.
298 assert!(key_metadata.certificate.is_none());
299
300 // Should not have an attestation record.
301 assert!(key_metadata.certificateChain.is_none());
302 Ok(key_metadata)
303}
Rajesh Nyamagoud4c6193c2022-02-03 01:15:34 +0000304
305/// Generate HMAC key.
306pub fn generate_hmac_key(
307 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
308 alias: &str,
309 key_size: i32,
310 min_mac_len: i32,
311 digest: Digest,
312) -> binder::Result<KeyMetadata> {
313 let gen_params = AuthSetBuilder::new()
314 .no_auth_required()
315 .algorithm(Algorithm::HMAC)
316 .purpose(KeyPurpose::SIGN)
317 .purpose(KeyPurpose::VERIFY)
318 .key_size(key_size)
319 .min_mac_length(min_mac_len)
320 .digest(digest);
321
322 let key_metadata = sec_level.generateKey(
323 &KeyDescriptor {
324 domain: Domain::APP,
325 nspace: -1,
326 alias: Some(alias.to_string()),
327 blob: None,
328 },
329 None,
330 &gen_params,
331 0,
332 b"entropy",
333 )?;
334
335 // Should not have public certificate.
336 assert!(key_metadata.certificate.is_none());
337
338 // Should not have an attestation record.
339 assert!(key_metadata.certificateChain.is_none());
340
341 Ok(key_metadata)
342}
Rajesh Nyamagoud4d483372022-02-09 01:38:23 +0000343
344/// Generate RSA or EC attestation keys using below parameters -
345/// Purpose: ATTEST_KEY
346/// Digest: Digest::SHA_2_256
347/// Padding: PaddingMode::RSA_PKCS1_1_5_SIGN
348/// RSA-Key-Size: 2048
349/// EC-Curve: EcCurve::P_256
350pub fn generate_attestation_key(
351 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
352 algorithm: Algorithm,
353 att_challenge: &[u8],
354 att_app_id: &[u8],
355) -> binder::Result<KeyMetadata> {
356 assert!(algorithm == Algorithm::RSA || algorithm == Algorithm::EC);
357
358 if algorithm == Algorithm::RSA {
359 let alias = "ks_rsa_attest_test_key";
360 let metadata = generate_rsa_key(
361 sec_level,
362 Domain::APP,
363 -1,
364 Some(alias.to_string()),
365 &KeyParams {
366 key_size: 2048,
367 purpose: vec![KeyPurpose::ATTEST_KEY],
368 padding: Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
369 digest: Some(Digest::SHA_2_256),
370 mgf_digest: None,
371 block_mode: None,
372 att_challenge: Some(att_challenge.to_vec()),
373 att_app_id: Some(att_app_id.to_vec()),
374 },
375 None,
376 )
377 .unwrap();
378 Ok(metadata)
379 } else {
380 let metadata = generate_ec_attestation_key(
381 sec_level,
382 att_challenge,
383 att_app_id,
384 Digest::SHA_2_256,
385 EcCurve::P_256,
386 )
387 .unwrap();
388
389 Ok(metadata)
390 }
391}
392
393/// Generate EC attestation key with the given
394/// curve, attestation-challenge and attestation-app-id.
395pub fn generate_ec_attestation_key(
396 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
397 att_challenge: &[u8],
398 att_app_id: &[u8],
399 digest: Digest,
400 ec_curve: EcCurve,
401) -> binder::Result<KeyMetadata> {
402 let alias = "ks_attest_ec_test_key";
403 let gen_params = AuthSetBuilder::new()
404 .no_auth_required()
405 .algorithm(Algorithm::EC)
406 .purpose(KeyPurpose::ATTEST_KEY)
407 .ec_curve(ec_curve)
408 .digest(digest)
409 .attestation_challenge(att_challenge.to_vec())
410 .attestation_app_id(att_app_id.to_vec());
411
412 let attestation_key_metadata = sec_level.generateKey(
413 &KeyDescriptor {
414 domain: Domain::APP,
415 nspace: -1,
416 alias: Some(alias.to_string()),
417 blob: None,
418 },
419 None,
420 &gen_params,
421 0,
422 b"entropy",
423 )?;
424
425 // Should have public certificate.
426 assert!(attestation_key_metadata.certificate.is_some());
427 // Should have an attestation record.
428 assert!(attestation_key_metadata.certificateChain.is_some());
429
430 Ok(attestation_key_metadata)
431}
432
433/// Generate EC-P-256 key and attest it with given attestation key.
434pub fn generate_ec_256_attested_key(
435 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
436 alias: Option<String>,
437 att_challenge: &[u8],
438 att_app_id: &[u8],
439 attest_key: &KeyDescriptor,
440) -> binder::Result<KeyMetadata> {
441 let ec_gen_params = AuthSetBuilder::new()
442 .no_auth_required()
443 .algorithm(Algorithm::EC)
444 .purpose(KeyPurpose::SIGN)
445 .purpose(KeyPurpose::VERIFY)
446 .digest(Digest::SHA_2_256)
447 .ec_curve(EcCurve::P_256)
448 .attestation_challenge(att_challenge.to_vec())
449 .attestation_app_id(att_app_id.to_vec());
450
451 let ec_key_metadata = sec_level
452 .generateKey(
453 &KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: None },
454 Some(attest_key),
455 &ec_gen_params,
456 0,
457 b"entropy",
458 )
459 .unwrap();
460
461 // Should have public certificate.
462 assert!(ec_key_metadata.certificate.is_some());
463 // Shouldn't have an attestation record.
464 assert!(ec_key_metadata.certificateChain.is_none());
465
466 Ok(ec_key_metadata)
467}