blob: c25d9280adc55de5149b5b1913c280f498b06986 [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),
74}
75
76/// Keystore2 error mapping.
77pub fn map_ks_error<T>(r: BinderResult<T>) -> Result<T, Error> {
78 r.map_err(|s| {
79 match s.exception_code() {
80 ExceptionCode::SERVICE_SPECIFIC => {
81 match s.service_specific_error() {
82 se if se < 0 => {
83 // Negative service specific errors are KM error codes.
84 Error::Km(ErrorCode(se))
85 }
86 se => {
87 // Positive service specific errors are KS response codes.
88 Error::Rc(ResponseCode(se))
89 }
90 }
91 }
92 // We create `Error::Binder` to preserve the exception code
93 // for logging.
94 e_code => Error::Binder(e_code),
95 }
96 })
97}
98
99/// Generate EC Key using given security level and domain with below key parameters and
100/// optionally allow the generated key to be attested with factory provisioned attest key using
101/// given challenge and application id -
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000102/// Purposes: SIGN and VERIFY
103/// Digest: SHA_2_256
104/// Curve: P_256
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000105pub fn generate_ec_p256_signing_key(
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000106 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000107 domain: Domain,
108 nspace: i64,
109 alias: Option<String>,
110 att_challenge: Option<&[u8]>,
111 att_app_id: Option<&[u8]>,
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000112) -> binder::Result<KeyMetadata> {
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000113 let mut key_attest = false;
114 let mut gen_params = AuthSetBuilder::new()
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000115 .algorithm(Algorithm::EC)
116 .purpose(KeyPurpose::SIGN)
117 .purpose(KeyPurpose::VERIFY)
118 .digest(Digest::SHA_2_256)
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000119 .ec_curve(EcCurve::P_256);
120
121 if let Some(challenge) = att_challenge {
122 key_attest = true;
123 gen_params = gen_params.clone().attestation_challenge(challenge.to_vec());
124 }
125
126 if let Some(app_id) = att_app_id {
127 key_attest = true;
128 gen_params = gen_params.clone().attestation_app_id(app_id.to_vec());
129 }
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000130
131 match sec_level.generateKey(
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000132 &KeyDescriptor { domain, nspace, alias, blob: None },
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000133 None,
134 &gen_params,
135 0,
136 b"entropy",
137 ) {
138 Ok(key_metadata) => {
139 assert!(key_metadata.certificate.is_some());
Rajesh Nyamagoudb881d512021-12-10 00:33:15 +0000140 if key_attest {
141 assert!(key_metadata.certificateChain.is_some());
142 }
143 if domain == Domain::BLOB {
144 assert!(key_metadata.key.blob.is_some());
145 }
Rajesh Nyamagoud901386c2022-03-21 20:35:18 +0000146
147 Ok(key_metadata)
148 }
149 Err(e) => Err(e),
150 }
151}
Rajesh Nyamagouda7766452021-12-13 21:44:19 +0000152
153/// Generate EC signing key.
154pub fn generate_ec_key<S: IKeystoreSecurityLevel + ?Sized>(
155 sec_level: &S,
156 domain: Domain,
157 nspace: i64,
158 alias: Option<String>,
159 ec_curve: EcCurve,
160 digest: Digest,
161) -> binder::Result<KeyMetadata> {
162 let gen_params = AuthSetBuilder::new()
163 .no_auth_required()
164 .algorithm(Algorithm::EC)
165 .purpose(KeyPurpose::SIGN)
166 .purpose(KeyPurpose::VERIFY)
167 .digest(digest)
168 .ec_curve(ec_curve);
169
170 let key_metadata = sec_level.generateKey(
171 &KeyDescriptor { domain, nspace, alias, blob: None },
172 None,
173 &gen_params,
174 0,
175 b"entropy",
176 )?;
177
178 // Must have a public key.
179 assert!(key_metadata.certificate.is_some());
180
181 // Should not have an attestation record.
182 assert!(key_metadata.certificateChain.is_none());
183
184 if domain == Domain::BLOB {
185 assert!(key_metadata.key.blob.is_some());
186 } else {
187 assert!(key_metadata.key.blob.is_none());
188 }
189 Ok(key_metadata)
190}
Rajesh Nyamagoud11912ea2021-12-20 20:37:20 +0000191
192/// Generate a RSA key with the given key parameters, alias, domain and namespace.
193pub fn generate_rsa_key(
194 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
195 domain: Domain,
196 nspace: i64,
197 alias: Option<String>,
198 key_params: &KeyParams,
199 attest_key: Option<&KeyDescriptor>,
200) -> binder::Result<KeyMetadata> {
201 let mut gen_params = AuthSetBuilder::new()
202 .no_auth_required()
203 .algorithm(Algorithm::RSA)
204 .rsa_public_exponent(65537)
205 .key_size(key_params.key_size);
206
207 for purpose in &key_params.purpose {
208 gen_params = gen_params.purpose(*purpose);
209 }
210 if let Some(value) = key_params.digest {
211 gen_params = gen_params.digest(value)
212 }
213 if let Some(value) = key_params.padding {
214 gen_params = gen_params.padding_mode(value);
215 }
216 if let Some(value) = key_params.mgf_digest {
217 gen_params = gen_params.mgf_digest(value);
218 }
219 if let Some(value) = key_params.block_mode {
220 gen_params = gen_params.block_mode(value)
221 }
222 if let Some(value) = &key_params.att_challenge {
223 gen_params = gen_params.attestation_challenge(value.to_vec())
224 }
225 if let Some(value) = &key_params.att_app_id {
226 gen_params = gen_params.attestation_app_id(value.to_vec())
227 }
228
229 let key_metadata = sec_level.generateKey(
230 &KeyDescriptor { domain, nspace, alias, blob: None },
231 attest_key,
232 &gen_params,
233 0,
234 b"entropy",
235 )?;
236
237 // Must have a public key.
238 assert!(key_metadata.certificate.is_some());
239
240 if attest_key.is_none() && key_params.att_challenge.is_some() && key_params.att_app_id.is_some()
241 {
242 // Should have an attestation record.
243 assert!(key_metadata.certificateChain.is_some());
244 } else {
245 // Should not have an attestation record.
246 assert!(key_metadata.certificateChain.is_none());
247 }
248
249 assert!(
250 (domain == Domain::BLOB && key_metadata.key.blob.is_some())
251 || key_metadata.key.blob.is_none()
252 );
253
254 Ok(key_metadata)
255}
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000256
Rajesh Nyamagoudc3523ba2022-08-05 17:38:25 +0000257/// Generate AES/3DES key.
258pub fn generate_sym_key(
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000259 sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
Rajesh Nyamagoudc3523ba2022-08-05 17:38:25 +0000260 algorithm: Algorithm,
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000261 size: i32,
262 alias: &str,
263 padding_mode: &PaddingMode,
264 block_mode: &BlockMode,
265 min_mac_len: Option<i32>,
266) -> binder::Result<KeyMetadata> {
267 let mut gen_params = AuthSetBuilder::new()
268 .no_auth_required()
Rajesh Nyamagoudc3523ba2022-08-05 17:38:25 +0000269 .algorithm(algorithm)
Rajesh Nyamagoud47409932022-01-08 00:37:13 +0000270 .purpose(KeyPurpose::ENCRYPT)
271 .purpose(KeyPurpose::DECRYPT)
272 .key_size(size)
273 .padding_mode(*padding_mode)
274 .block_mode(*block_mode);
275
276 if let Some(val) = min_mac_len {
277 gen_params = gen_params.min_mac_length(val);
278 }
279
280 let key_metadata = sec_level.generateKey(
281 &KeyDescriptor {
282 domain: Domain::APP,
283 nspace: -1,
284 alias: Some(alias.to_string()),
285 blob: None,
286 },
287 None,
288 &gen_params,
289 0,
290 b"entropy",
291 )?;
292
293 // Should not have public certificate.
294 assert!(key_metadata.certificate.is_none());
295
296 // Should not have an attestation record.
297 assert!(key_metadata.certificateChain.is_none());
298 Ok(key_metadata)
299}