1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2023, The Android Open Source Project
2*e1997b9aSAndroid Build Coastguard Worker //
3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e1997b9aSAndroid Build Coastguard Worker //
7*e1997b9aSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*e1997b9aSAndroid Build Coastguard Worker //
9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License.
14*e1997b9aSAndroid Build Coastguard Worker
15*e1997b9aSAndroid Build Coastguard Worker //! Code for parsing software-backed keyblobs, as emitted by the C++ reference implementation of
16*e1997b9aSAndroid Build Coastguard Worker //! KeyMint.
17*e1997b9aSAndroid Build Coastguard Worker
18*e1997b9aSAndroid Build Coastguard Worker use crate::error::Error;
19*e1997b9aSAndroid Build Coastguard Worker use crate::ks_err;
20*e1997b9aSAndroid Build Coastguard Worker use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
21*e1997b9aSAndroid Build Coastguard Worker Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
22*e1997b9aSAndroid Build Coastguard Worker ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
23*e1997b9aSAndroid Build Coastguard Worker KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
24*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
25*e1997b9aSAndroid Build Coastguard Worker Tag::Tag, TagType::TagType,
26*e1997b9aSAndroid Build Coastguard Worker };
27*e1997b9aSAndroid Build Coastguard Worker use anyhow::Result;
28*e1997b9aSAndroid Build Coastguard Worker use keystore2_crypto::hmac_sha256;
29*e1997b9aSAndroid Build Coastguard Worker use std::mem::size_of;
30*e1997b9aSAndroid Build Coastguard Worker
31*e1997b9aSAndroid Build Coastguard Worker #[cfg(test)]
32*e1997b9aSAndroid Build Coastguard Worker mod tests;
33*e1997b9aSAndroid Build Coastguard Worker
34*e1997b9aSAndroid Build Coastguard Worker /// Root of trust value.
35*e1997b9aSAndroid Build Coastguard Worker const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW";
36*e1997b9aSAndroid Build Coastguard Worker
37*e1997b9aSAndroid Build Coastguard Worker /// Error macro.
38*e1997b9aSAndroid Build Coastguard Worker macro_rules! bloberr {
39*e1997b9aSAndroid Build Coastguard Worker { $($arg:tt)+ } => {
40*e1997b9aSAndroid Build Coastguard Worker anyhow::Error::new(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!($($arg)+))
41*e1997b9aSAndroid Build Coastguard Worker };
42*e1997b9aSAndroid Build Coastguard Worker }
43*e1997b9aSAndroid Build Coastguard Worker
44*e1997b9aSAndroid Build Coastguard Worker /// Get the `KeyParameterValue` associated with a tag from a collection of `KeyParameter`s.
get_tag_value(params: &[KeyParameter], tag: Tag) -> Option<&KeyParameterValue>45*e1997b9aSAndroid Build Coastguard Worker fn get_tag_value(params: &[KeyParameter], tag: Tag) -> Option<&KeyParameterValue> {
46*e1997b9aSAndroid Build Coastguard Worker params.iter().find_map(|kp| if kp.tag == tag { Some(&kp.value) } else { None })
47*e1997b9aSAndroid Build Coastguard Worker }
48*e1997b9aSAndroid Build Coastguard Worker
49*e1997b9aSAndroid Build Coastguard Worker /// Get the [`TagType`] for a [`Tag`].
tag_type(tag: &Tag) -> TagType50*e1997b9aSAndroid Build Coastguard Worker fn tag_type(tag: &Tag) -> TagType {
51*e1997b9aSAndroid Build Coastguard Worker TagType((tag.0 as u32 & 0xf0000000) as i32)
52*e1997b9aSAndroid Build Coastguard Worker }
53*e1997b9aSAndroid Build Coastguard Worker
54*e1997b9aSAndroid Build Coastguard Worker /// Extract key material and combined key characteristics from a legacy authenticated keyblob.
export_key( data: &[u8], params: &[KeyParameter], ) -> Result<(KeyFormat, Vec<u8>, Vec<KeyParameter>)>55*e1997b9aSAndroid Build Coastguard Worker pub fn export_key(
56*e1997b9aSAndroid Build Coastguard Worker data: &[u8],
57*e1997b9aSAndroid Build Coastguard Worker params: &[KeyParameter],
58*e1997b9aSAndroid Build Coastguard Worker ) -> Result<(KeyFormat, Vec<u8>, Vec<KeyParameter>)> {
59*e1997b9aSAndroid Build Coastguard Worker let hidden = hidden_params(params, &[SOFTWARE_ROOT_OF_TRUST]);
60*e1997b9aSAndroid Build Coastguard Worker let KeyBlob { key_material, hw_enforced, sw_enforced } =
61*e1997b9aSAndroid Build Coastguard Worker KeyBlob::new_from_serialized(data, &hidden)?;
62*e1997b9aSAndroid Build Coastguard Worker
63*e1997b9aSAndroid Build Coastguard Worker let mut combined = hw_enforced;
64*e1997b9aSAndroid Build Coastguard Worker combined.extend_from_slice(&sw_enforced);
65*e1997b9aSAndroid Build Coastguard Worker
66*e1997b9aSAndroid Build Coastguard Worker let algo_val =
67*e1997b9aSAndroid Build Coastguard Worker get_tag_value(&combined, Tag::ALGORITHM).ok_or_else(|| bloberr!("No algorithm found!"))?;
68*e1997b9aSAndroid Build Coastguard Worker
69*e1997b9aSAndroid Build Coastguard Worker let format = match algo_val {
70*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Algorithm(Algorithm::AES)
71*e1997b9aSAndroid Build Coastguard Worker | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES)
72*e1997b9aSAndroid Build Coastguard Worker | KeyParameterValue::Algorithm(Algorithm::HMAC) => KeyFormat::RAW,
73*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Algorithm(Algorithm::RSA)
74*e1997b9aSAndroid Build Coastguard Worker | KeyParameterValue::Algorithm(Algorithm::EC) => KeyFormat::PKCS8,
75*e1997b9aSAndroid Build Coastguard Worker _ => return Err(bloberr!("Unexpected algorithm {:?}", algo_val)),
76*e1997b9aSAndroid Build Coastguard Worker };
77*e1997b9aSAndroid Build Coastguard Worker
78*e1997b9aSAndroid Build Coastguard Worker let key_material = match (format, algo_val) {
79*e1997b9aSAndroid Build Coastguard Worker (KeyFormat::PKCS8, KeyParameterValue::Algorithm(Algorithm::EC)) => {
80*e1997b9aSAndroid Build Coastguard Worker // Key material format depends on the curve.
81*e1997b9aSAndroid Build Coastguard Worker let curve = get_tag_value(&combined, Tag::EC_CURVE)
82*e1997b9aSAndroid Build Coastguard Worker .ok_or_else(|| bloberr!("Failed to determine curve for EC key!"))?;
83*e1997b9aSAndroid Build Coastguard Worker match curve {
84*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::EcCurve(EcCurve::CURVE_25519) => key_material,
85*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::EcCurve(EcCurve::P_224) => {
86*e1997b9aSAndroid Build Coastguard Worker pkcs8_wrap_nist_key(&key_material, EcCurve::P_224)?
87*e1997b9aSAndroid Build Coastguard Worker }
88*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::EcCurve(EcCurve::P_256) => {
89*e1997b9aSAndroid Build Coastguard Worker pkcs8_wrap_nist_key(&key_material, EcCurve::P_256)?
90*e1997b9aSAndroid Build Coastguard Worker }
91*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::EcCurve(EcCurve::P_384) => {
92*e1997b9aSAndroid Build Coastguard Worker pkcs8_wrap_nist_key(&key_material, EcCurve::P_384)?
93*e1997b9aSAndroid Build Coastguard Worker }
94*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::EcCurve(EcCurve::P_521) => {
95*e1997b9aSAndroid Build Coastguard Worker pkcs8_wrap_nist_key(&key_material, EcCurve::P_521)?
96*e1997b9aSAndroid Build Coastguard Worker }
97*e1997b9aSAndroid Build Coastguard Worker _ => {
98*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("Unexpected EC curve {curve:?}"));
99*e1997b9aSAndroid Build Coastguard Worker }
100*e1997b9aSAndroid Build Coastguard Worker }
101*e1997b9aSAndroid Build Coastguard Worker }
102*e1997b9aSAndroid Build Coastguard Worker (KeyFormat::RAW, _) => key_material,
103*e1997b9aSAndroid Build Coastguard Worker (format, algo) => {
104*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!(
105*e1997b9aSAndroid Build Coastguard Worker "Unsupported combination of {format:?} format for {algo:?} algorithm"
106*e1997b9aSAndroid Build Coastguard Worker ));
107*e1997b9aSAndroid Build Coastguard Worker }
108*e1997b9aSAndroid Build Coastguard Worker };
109*e1997b9aSAndroid Build Coastguard Worker Ok((format, key_material, combined))
110*e1997b9aSAndroid Build Coastguard Worker }
111*e1997b9aSAndroid Build Coastguard Worker
112*e1997b9aSAndroid Build Coastguard Worker /// DER-encoded `AlgorithmIdentifier` for a P-224 key.
113*e1997b9aSAndroid Build Coastguard Worker const DER_ALGORITHM_ID_P224: &[u8] = &[
114*e1997b9aSAndroid Build Coastguard Worker 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
115*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
116*e1997b9aSAndroid Build Coastguard Worker 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
117*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x05, // OBJECT IDENTIFIER (param)
118*e1997b9aSAndroid Build Coastguard Worker 0x2b, 0x81, 0x04, 0x00, 0x21, // 1.3.132.0.33 (secp224r1) }
119*e1997b9aSAndroid Build Coastguard Worker ];
120*e1997b9aSAndroid Build Coastguard Worker
121*e1997b9aSAndroid Build Coastguard Worker /// DER-encoded `AlgorithmIdentifier` for a P-256 key.
122*e1997b9aSAndroid Build Coastguard Worker const DER_ALGORITHM_ID_P256: &[u8] = &[
123*e1997b9aSAndroid Build Coastguard Worker 0x30, 0x13, // SEQUENCE (AlgorithmIdentifier) {
124*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
125*e1997b9aSAndroid Build Coastguard Worker 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
126*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x08, // OBJECT IDENTIFIER (param)
127*e1997b9aSAndroid Build Coastguard Worker 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, // 1.2.840.10045.3.1.7 (secp256r1) }
128*e1997b9aSAndroid Build Coastguard Worker ];
129*e1997b9aSAndroid Build Coastguard Worker
130*e1997b9aSAndroid Build Coastguard Worker /// DER-encoded `AlgorithmIdentifier` for a P-384 key.
131*e1997b9aSAndroid Build Coastguard Worker const DER_ALGORITHM_ID_P384: &[u8] = &[
132*e1997b9aSAndroid Build Coastguard Worker 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
133*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
134*e1997b9aSAndroid Build Coastguard Worker 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
135*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x05, // OBJECT IDENTIFIER (param)
136*e1997b9aSAndroid Build Coastguard Worker 0x2b, 0x81, 0x04, 0x00, 0x22, // 1.3.132.0.34 (secp384r1) }
137*e1997b9aSAndroid Build Coastguard Worker ];
138*e1997b9aSAndroid Build Coastguard Worker
139*e1997b9aSAndroid Build Coastguard Worker /// DER-encoded `AlgorithmIdentifier` for a P-384 key.
140*e1997b9aSAndroid Build Coastguard Worker const DER_ALGORITHM_ID_P521: &[u8] = &[
141*e1997b9aSAndroid Build Coastguard Worker 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
142*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
143*e1997b9aSAndroid Build Coastguard Worker 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
144*e1997b9aSAndroid Build Coastguard Worker 0x06, 0x05, // OBJECT IDENTIFIER (param)
145*e1997b9aSAndroid Build Coastguard Worker 0x2b, 0x81, 0x04, 0x00, 0x23, // 1.3.132.0.35 (secp521r1) }
146*e1997b9aSAndroid Build Coastguard Worker ];
147*e1997b9aSAndroid Build Coastguard Worker
148*e1997b9aSAndroid Build Coastguard Worker /// DER-encoded integer value zero.
149*e1997b9aSAndroid Build Coastguard Worker const DER_VERSION_0: &[u8] = &[
150*e1997b9aSAndroid Build Coastguard Worker 0x02, // INTEGER
151*e1997b9aSAndroid Build Coastguard Worker 0x01, // len
152*e1997b9aSAndroid Build Coastguard Worker 0x00, // value 0
153*e1997b9aSAndroid Build Coastguard Worker ];
154*e1997b9aSAndroid Build Coastguard Worker
155*e1997b9aSAndroid Build Coastguard Worker /// Given a NIST curve EC key in the form of a DER-encoded `ECPrivateKey`
156*e1997b9aSAndroid Build Coastguard Worker /// (RFC 5915 s3), wrap it in a DER-encoded PKCS#8 format (RFC 5208 s5).
pkcs8_wrap_nist_key(nist_key: &[u8], curve: EcCurve) -> Result<Vec<u8>>157*e1997b9aSAndroid Build Coastguard Worker fn pkcs8_wrap_nist_key(nist_key: &[u8], curve: EcCurve) -> Result<Vec<u8>> {
158*e1997b9aSAndroid Build Coastguard Worker let der_alg_id = match curve {
159*e1997b9aSAndroid Build Coastguard Worker EcCurve::P_224 => DER_ALGORITHM_ID_P224,
160*e1997b9aSAndroid Build Coastguard Worker EcCurve::P_256 => DER_ALGORITHM_ID_P256,
161*e1997b9aSAndroid Build Coastguard Worker EcCurve::P_384 => DER_ALGORITHM_ID_P384,
162*e1997b9aSAndroid Build Coastguard Worker EcCurve::P_521 => DER_ALGORITHM_ID_P521,
163*e1997b9aSAndroid Build Coastguard Worker _ => return Err(bloberr!("unknown curve {curve:?}")),
164*e1997b9aSAndroid Build Coastguard Worker };
165*e1997b9aSAndroid Build Coastguard Worker
166*e1997b9aSAndroid Build Coastguard Worker // Output format is:
167*e1997b9aSAndroid Build Coastguard Worker //
168*e1997b9aSAndroid Build Coastguard Worker // PrivateKeyInfo ::= SEQUENCE {
169*e1997b9aSAndroid Build Coastguard Worker // version INTEGER,
170*e1997b9aSAndroid Build Coastguard Worker // privateKeyAlgorithm AlgorithmIdentifier,
171*e1997b9aSAndroid Build Coastguard Worker // privateKey OCTET STRING,
172*e1997b9aSAndroid Build Coastguard Worker // }
173*e1997b9aSAndroid Build Coastguard Worker //
174*e1997b9aSAndroid Build Coastguard Worker // Start by building the OCTET STRING so we know its length.
175*e1997b9aSAndroid Build Coastguard Worker let mut nist_key_octet_string = Vec::new();
176*e1997b9aSAndroid Build Coastguard Worker nist_key_octet_string.push(0x04); // OCTET STRING
177*e1997b9aSAndroid Build Coastguard Worker add_der_len(&mut nist_key_octet_string, nist_key.len())?;
178*e1997b9aSAndroid Build Coastguard Worker nist_key_octet_string.extend_from_slice(nist_key);
179*e1997b9aSAndroid Build Coastguard Worker
180*e1997b9aSAndroid Build Coastguard Worker let mut buf = Vec::new();
181*e1997b9aSAndroid Build Coastguard Worker buf.push(0x30); // SEQUENCE
182*e1997b9aSAndroid Build Coastguard Worker add_der_len(&mut buf, DER_VERSION_0.len() + der_alg_id.len() + nist_key_octet_string.len())?;
183*e1997b9aSAndroid Build Coastguard Worker buf.extend_from_slice(DER_VERSION_0);
184*e1997b9aSAndroid Build Coastguard Worker buf.extend_from_slice(der_alg_id);
185*e1997b9aSAndroid Build Coastguard Worker buf.extend_from_slice(&nist_key_octet_string);
186*e1997b9aSAndroid Build Coastguard Worker Ok(buf)
187*e1997b9aSAndroid Build Coastguard Worker }
188*e1997b9aSAndroid Build Coastguard Worker
189*e1997b9aSAndroid Build Coastguard Worker /// Append a DER-encoded length value to the given buffer.
add_der_len(buf: &mut Vec<u8>, len: usize) -> Result<()>190*e1997b9aSAndroid Build Coastguard Worker fn add_der_len(buf: &mut Vec<u8>, len: usize) -> Result<()> {
191*e1997b9aSAndroid Build Coastguard Worker if len <= 0x7f {
192*e1997b9aSAndroid Build Coastguard Worker buf.push(len as u8)
193*e1997b9aSAndroid Build Coastguard Worker } else if len <= 0xff {
194*e1997b9aSAndroid Build Coastguard Worker buf.push(0x81); // One length octet to come
195*e1997b9aSAndroid Build Coastguard Worker buf.push(len as u8);
196*e1997b9aSAndroid Build Coastguard Worker } else if len <= 0xffff {
197*e1997b9aSAndroid Build Coastguard Worker buf.push(0x82); // Two length octets to come
198*e1997b9aSAndroid Build Coastguard Worker buf.push((len >> 8) as u8);
199*e1997b9aSAndroid Build Coastguard Worker buf.push((len & 0xff) as u8);
200*e1997b9aSAndroid Build Coastguard Worker } else {
201*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("Unsupported DER length {len}"));
202*e1997b9aSAndroid Build Coastguard Worker }
203*e1997b9aSAndroid Build Coastguard Worker Ok(())
204*e1997b9aSAndroid Build Coastguard Worker }
205*e1997b9aSAndroid Build Coastguard Worker
206*e1997b9aSAndroid Build Coastguard Worker /// Plaintext key blob, with key characteristics.
207*e1997b9aSAndroid Build Coastguard Worker #[derive(PartialEq, Eq)]
208*e1997b9aSAndroid Build Coastguard Worker struct KeyBlob {
209*e1997b9aSAndroid Build Coastguard Worker /// Raw key material.
210*e1997b9aSAndroid Build Coastguard Worker key_material: Vec<u8>,
211*e1997b9aSAndroid Build Coastguard Worker /// Hardware-enforced key characteristics.
212*e1997b9aSAndroid Build Coastguard Worker hw_enforced: Vec<KeyParameter>,
213*e1997b9aSAndroid Build Coastguard Worker /// Software-enforced key characteristics.
214*e1997b9aSAndroid Build Coastguard Worker sw_enforced: Vec<KeyParameter>,
215*e1997b9aSAndroid Build Coastguard Worker }
216*e1997b9aSAndroid Build Coastguard Worker
217*e1997b9aSAndroid Build Coastguard Worker impl KeyBlob {
218*e1997b9aSAndroid Build Coastguard Worker /// Key blob version.
219*e1997b9aSAndroid Build Coastguard Worker const KEY_BLOB_VERSION: u8 = 0;
220*e1997b9aSAndroid Build Coastguard Worker
221*e1997b9aSAndroid Build Coastguard Worker /// Hard-coded HMAC key used for keyblob authentication.
222*e1997b9aSAndroid Build Coastguard Worker const LEGACY_HMAC_KEY: &'static [u8] = b"IntegrityAssuredBlob0\0";
223*e1997b9aSAndroid Build Coastguard Worker
224*e1997b9aSAndroid Build Coastguard Worker /// Size (in bytes) of appended MAC.
225*e1997b9aSAndroid Build Coastguard Worker const MAC_LEN: usize = 8;
226*e1997b9aSAndroid Build Coastguard Worker
227*e1997b9aSAndroid Build Coastguard Worker /// Parse a serialized [`KeyBlob`].
new_from_serialized(mut data: &[u8], hidden: &[KeyParameter]) -> Result<Self>228*e1997b9aSAndroid Build Coastguard Worker fn new_from_serialized(mut data: &[u8], hidden: &[KeyParameter]) -> Result<Self> {
229*e1997b9aSAndroid Build Coastguard Worker // Keyblob needs to be at least long enough for:
230*e1997b9aSAndroid Build Coastguard Worker // - version byte,
231*e1997b9aSAndroid Build Coastguard Worker // - 4-byte len for key material
232*e1997b9aSAndroid Build Coastguard Worker // - 4-byte len for hw_enforced params
233*e1997b9aSAndroid Build Coastguard Worker // - 4-byte len for sw_enforced params
234*e1997b9aSAndroid Build Coastguard Worker // - MAC tag.
235*e1997b9aSAndroid Build Coastguard Worker if data.len() < (1 + 3 * size_of::<u32>() + Self::MAC_LEN) {
236*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("blob not long enough (len = {})", data.len()));
237*e1997b9aSAndroid Build Coastguard Worker }
238*e1997b9aSAndroid Build Coastguard Worker
239*e1997b9aSAndroid Build Coastguard Worker // Check the HMAC in the last 8 bytes before doing anything else.
240*e1997b9aSAndroid Build Coastguard Worker let mac = &data[data.len() - Self::MAC_LEN..];
241*e1997b9aSAndroid Build Coastguard Worker let computed_mac = Self::compute_hmac(&data[..data.len() - Self::MAC_LEN], hidden)?;
242*e1997b9aSAndroid Build Coastguard Worker if mac != computed_mac {
243*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("invalid key blob"));
244*e1997b9aSAndroid Build Coastguard Worker }
245*e1997b9aSAndroid Build Coastguard Worker
246*e1997b9aSAndroid Build Coastguard Worker let version = consume_u8(&mut data)?;
247*e1997b9aSAndroid Build Coastguard Worker if version != Self::KEY_BLOB_VERSION {
248*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("unexpected blob version {}", version));
249*e1997b9aSAndroid Build Coastguard Worker }
250*e1997b9aSAndroid Build Coastguard Worker let key_material = consume_vec(&mut data)?;
251*e1997b9aSAndroid Build Coastguard Worker let hw_enforced = deserialize_params(&mut data)?;
252*e1997b9aSAndroid Build Coastguard Worker let sw_enforced = deserialize_params(&mut data)?;
253*e1997b9aSAndroid Build Coastguard Worker
254*e1997b9aSAndroid Build Coastguard Worker // Should just be the (already-checked) MAC left.
255*e1997b9aSAndroid Build Coastguard Worker let rest = &data[Self::MAC_LEN..];
256*e1997b9aSAndroid Build Coastguard Worker if !rest.is_empty() {
257*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("extra data (len {})", rest.len()));
258*e1997b9aSAndroid Build Coastguard Worker }
259*e1997b9aSAndroid Build Coastguard Worker Ok(KeyBlob { key_material, hw_enforced, sw_enforced })
260*e1997b9aSAndroid Build Coastguard Worker }
261*e1997b9aSAndroid Build Coastguard Worker
262*e1997b9aSAndroid Build Coastguard Worker /// Compute the authentication HMAC for a KeyBlob. This is built as:
263*e1997b9aSAndroid Build Coastguard Worker /// HMAC-SHA256(HK, data || serialize(hidden))
264*e1997b9aSAndroid Build Coastguard Worker /// with HK = b"IntegrityAssuredBlob0\0".
compute_hmac(data: &[u8], hidden: &[KeyParameter]) -> Result<Vec<u8>>265*e1997b9aSAndroid Build Coastguard Worker fn compute_hmac(data: &[u8], hidden: &[KeyParameter]) -> Result<Vec<u8>> {
266*e1997b9aSAndroid Build Coastguard Worker let hidden_data = serialize_params(hidden)?;
267*e1997b9aSAndroid Build Coastguard Worker let mut combined = data.to_vec();
268*e1997b9aSAndroid Build Coastguard Worker combined.extend_from_slice(&hidden_data);
269*e1997b9aSAndroid Build Coastguard Worker let mut tag = hmac_sha256(Self::LEGACY_HMAC_KEY, &combined)?;
270*e1997b9aSAndroid Build Coastguard Worker tag.truncate(Self::MAC_LEN);
271*e1997b9aSAndroid Build Coastguard Worker Ok(tag)
272*e1997b9aSAndroid Build Coastguard Worker }
273*e1997b9aSAndroid Build Coastguard Worker }
274*e1997b9aSAndroid Build Coastguard Worker
275*e1997b9aSAndroid Build Coastguard Worker /// Build the parameters that are used as the hidden input to HMAC calculations:
276*e1997b9aSAndroid Build Coastguard Worker /// - `ApplicationId(data)` if present
277*e1997b9aSAndroid Build Coastguard Worker /// - `ApplicationData(data)` if present
278*e1997b9aSAndroid Build Coastguard Worker /// - (repeated) `RootOfTrust(rot)` where `rot` is a hardcoded piece of root of trust information.
hidden_params(params: &[KeyParameter], rots: &[&[u8]]) -> Vec<KeyParameter>279*e1997b9aSAndroid Build Coastguard Worker fn hidden_params(params: &[KeyParameter], rots: &[&[u8]]) -> Vec<KeyParameter> {
280*e1997b9aSAndroid Build Coastguard Worker let mut results = Vec::new();
281*e1997b9aSAndroid Build Coastguard Worker if let Some(app_id) = get_tag_value(params, Tag::APPLICATION_ID) {
282*e1997b9aSAndroid Build Coastguard Worker results.push(KeyParameter { tag: Tag::APPLICATION_ID, value: app_id.clone() });
283*e1997b9aSAndroid Build Coastguard Worker }
284*e1997b9aSAndroid Build Coastguard Worker if let Some(app_data) = get_tag_value(params, Tag::APPLICATION_DATA) {
285*e1997b9aSAndroid Build Coastguard Worker results.push(KeyParameter { tag: Tag::APPLICATION_DATA, value: app_data.clone() });
286*e1997b9aSAndroid Build Coastguard Worker }
287*e1997b9aSAndroid Build Coastguard Worker for rot in rots {
288*e1997b9aSAndroid Build Coastguard Worker results.push(KeyParameter {
289*e1997b9aSAndroid Build Coastguard Worker tag: Tag::ROOT_OF_TRUST,
290*e1997b9aSAndroid Build Coastguard Worker value: KeyParameterValue::Blob(rot.to_vec()),
291*e1997b9aSAndroid Build Coastguard Worker });
292*e1997b9aSAndroid Build Coastguard Worker }
293*e1997b9aSAndroid Build Coastguard Worker results
294*e1997b9aSAndroid Build Coastguard Worker }
295*e1997b9aSAndroid Build Coastguard Worker
296*e1997b9aSAndroid Build Coastguard Worker /// Retrieve a `u8` from the start of the given slice, if possible.
consume_u8(data: &mut &[u8]) -> Result<u8>297*e1997b9aSAndroid Build Coastguard Worker fn consume_u8(data: &mut &[u8]) -> Result<u8> {
298*e1997b9aSAndroid Build Coastguard Worker match data.first() {
299*e1997b9aSAndroid Build Coastguard Worker Some(b) => {
300*e1997b9aSAndroid Build Coastguard Worker *data = &(*data)[1..];
301*e1997b9aSAndroid Build Coastguard Worker Ok(*b)
302*e1997b9aSAndroid Build Coastguard Worker }
303*e1997b9aSAndroid Build Coastguard Worker None => Err(bloberr!("failed to find 1 byte")),
304*e1997b9aSAndroid Build Coastguard Worker }
305*e1997b9aSAndroid Build Coastguard Worker }
306*e1997b9aSAndroid Build Coastguard Worker
307*e1997b9aSAndroid Build Coastguard Worker /// Move past a bool value from the start of the given slice, if possible.
308*e1997b9aSAndroid Build Coastguard Worker /// Bool values should only be included if `true`, so fail if the value
309*e1997b9aSAndroid Build Coastguard Worker /// is anything other than 1.
consume_bool(data: &mut &[u8]) -> Result<bool>310*e1997b9aSAndroid Build Coastguard Worker fn consume_bool(data: &mut &[u8]) -> Result<bool> {
311*e1997b9aSAndroid Build Coastguard Worker let b = consume_u8(data)?;
312*e1997b9aSAndroid Build Coastguard Worker if b == 0x01 {
313*e1997b9aSAndroid Build Coastguard Worker Ok(true)
314*e1997b9aSAndroid Build Coastguard Worker } else {
315*e1997b9aSAndroid Build Coastguard Worker Err(bloberr!("bool value other than 1 encountered"))
316*e1997b9aSAndroid Build Coastguard Worker }
317*e1997b9aSAndroid Build Coastguard Worker }
318*e1997b9aSAndroid Build Coastguard Worker
319*e1997b9aSAndroid Build Coastguard Worker /// Retrieve a (host-ordered) `u32` from the start of the given slice, if possible.
consume_u32(data: &mut &[u8]) -> Result<u32>320*e1997b9aSAndroid Build Coastguard Worker fn consume_u32(data: &mut &[u8]) -> Result<u32> {
321*e1997b9aSAndroid Build Coastguard Worker const LEN: usize = size_of::<u32>();
322*e1997b9aSAndroid Build Coastguard Worker if data.len() < LEN {
323*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("failed to find {LEN} bytes"));
324*e1997b9aSAndroid Build Coastguard Worker }
325*e1997b9aSAndroid Build Coastguard Worker let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked
326*e1997b9aSAndroid Build Coastguard Worker *data = &(*data)[LEN..];
327*e1997b9aSAndroid Build Coastguard Worker Ok(u32::from_ne_bytes(chunk))
328*e1997b9aSAndroid Build Coastguard Worker }
329*e1997b9aSAndroid Build Coastguard Worker
330*e1997b9aSAndroid Build Coastguard Worker /// Retrieve a (host-ordered) `i32` from the start of the given slice, if possible.
consume_i32(data: &mut &[u8]) -> Result<i32>331*e1997b9aSAndroid Build Coastguard Worker fn consume_i32(data: &mut &[u8]) -> Result<i32> {
332*e1997b9aSAndroid Build Coastguard Worker const LEN: usize = size_of::<i32>();
333*e1997b9aSAndroid Build Coastguard Worker if data.len() < LEN {
334*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("failed to find {LEN} bytes"));
335*e1997b9aSAndroid Build Coastguard Worker }
336*e1997b9aSAndroid Build Coastguard Worker let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked
337*e1997b9aSAndroid Build Coastguard Worker *data = &(*data)[4..];
338*e1997b9aSAndroid Build Coastguard Worker Ok(i32::from_ne_bytes(chunk))
339*e1997b9aSAndroid Build Coastguard Worker }
340*e1997b9aSAndroid Build Coastguard Worker
341*e1997b9aSAndroid Build Coastguard Worker /// Retrieve a (host-ordered) `i64` from the start of the given slice, if possible.
consume_i64(data: &mut &[u8]) -> Result<i64>342*e1997b9aSAndroid Build Coastguard Worker fn consume_i64(data: &mut &[u8]) -> Result<i64> {
343*e1997b9aSAndroid Build Coastguard Worker const LEN: usize = size_of::<i64>();
344*e1997b9aSAndroid Build Coastguard Worker if data.len() < LEN {
345*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("failed to find {LEN} bytes"));
346*e1997b9aSAndroid Build Coastguard Worker }
347*e1997b9aSAndroid Build Coastguard Worker let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked
348*e1997b9aSAndroid Build Coastguard Worker *data = &(*data)[LEN..];
349*e1997b9aSAndroid Build Coastguard Worker Ok(i64::from_ne_bytes(chunk))
350*e1997b9aSAndroid Build Coastguard Worker }
351*e1997b9aSAndroid Build Coastguard Worker
352*e1997b9aSAndroid Build Coastguard Worker /// Retrieve a vector of bytes from the start of the given slice, if possible,
353*e1997b9aSAndroid Build Coastguard Worker /// with the length of the data expected to appear as a host-ordered `u32` prefix.
consume_vec(data: &mut &[u8]) -> Result<Vec<u8>>354*e1997b9aSAndroid Build Coastguard Worker fn consume_vec(data: &mut &[u8]) -> Result<Vec<u8>> {
355*e1997b9aSAndroid Build Coastguard Worker let len = consume_u32(data)? as usize;
356*e1997b9aSAndroid Build Coastguard Worker if len > data.len() {
357*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("failed to find {} bytes", len));
358*e1997b9aSAndroid Build Coastguard Worker }
359*e1997b9aSAndroid Build Coastguard Worker let result = data[..len].to_vec();
360*e1997b9aSAndroid Build Coastguard Worker *data = &(*data)[len..];
361*e1997b9aSAndroid Build Coastguard Worker Ok(result)
362*e1997b9aSAndroid Build Coastguard Worker }
363*e1997b9aSAndroid Build Coastguard Worker
364*e1997b9aSAndroid Build Coastguard Worker /// Retrieve the contents of a tag of `TagType::Bytes`. The `data` parameter holds
365*e1997b9aSAndroid Build Coastguard Worker /// the as-yet unparsed data, and a length and offset are read from this (and consumed).
366*e1997b9aSAndroid Build Coastguard Worker /// This length and offset refer to a location in the combined `blob_data`; however,
367*e1997b9aSAndroid Build Coastguard Worker /// the offset is expected to be the next unconsumed chunk of `blob_data`, as indicated
368*e1997b9aSAndroid Build Coastguard Worker /// by `next_blob_offset` (which itself is updated as a result of consuming the data).
consume_blob( data: &mut &[u8], next_blob_offset: &mut usize, blob_data: &[u8], ) -> Result<Vec<u8>>369*e1997b9aSAndroid Build Coastguard Worker fn consume_blob(
370*e1997b9aSAndroid Build Coastguard Worker data: &mut &[u8],
371*e1997b9aSAndroid Build Coastguard Worker next_blob_offset: &mut usize,
372*e1997b9aSAndroid Build Coastguard Worker blob_data: &[u8],
373*e1997b9aSAndroid Build Coastguard Worker ) -> Result<Vec<u8>> {
374*e1997b9aSAndroid Build Coastguard Worker let data_len = consume_u32(data)? as usize;
375*e1997b9aSAndroid Build Coastguard Worker let data_offset = consume_u32(data)? as usize;
376*e1997b9aSAndroid Build Coastguard Worker // Expect the blob data to come from the next offset in the initial blob chunk.
377*e1997b9aSAndroid Build Coastguard Worker if data_offset != *next_blob_offset {
378*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("got blob offset {} instead of {}", data_offset, next_blob_offset));
379*e1997b9aSAndroid Build Coastguard Worker }
380*e1997b9aSAndroid Build Coastguard Worker if (data_offset + data_len) > blob_data.len() {
381*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!(
382*e1997b9aSAndroid Build Coastguard Worker "blob at offset [{}..{}+{}] goes beyond blob data size {}",
383*e1997b9aSAndroid Build Coastguard Worker data_offset,
384*e1997b9aSAndroid Build Coastguard Worker data_offset,
385*e1997b9aSAndroid Build Coastguard Worker data_len,
386*e1997b9aSAndroid Build Coastguard Worker blob_data.len(),
387*e1997b9aSAndroid Build Coastguard Worker ));
388*e1997b9aSAndroid Build Coastguard Worker }
389*e1997b9aSAndroid Build Coastguard Worker
390*e1997b9aSAndroid Build Coastguard Worker let slice = &blob_data[data_offset..data_offset + data_len];
391*e1997b9aSAndroid Build Coastguard Worker *next_blob_offset += data_len;
392*e1997b9aSAndroid Build Coastguard Worker Ok(slice.to_vec())
393*e1997b9aSAndroid Build Coastguard Worker }
394*e1997b9aSAndroid Build Coastguard Worker
395*e1997b9aSAndroid Build Coastguard Worker /// Deserialize a collection of [`KeyParam`]s in legacy serialized format. The provided slice is
396*e1997b9aSAndroid Build Coastguard Worker /// modified to contain the unconsumed part of the data.
deserialize_params(data: &mut &[u8]) -> Result<Vec<KeyParameter>>397*e1997b9aSAndroid Build Coastguard Worker fn deserialize_params(data: &mut &[u8]) -> Result<Vec<KeyParameter>> {
398*e1997b9aSAndroid Build Coastguard Worker let blob_data_size = consume_u32(data)? as usize;
399*e1997b9aSAndroid Build Coastguard Worker if blob_data_size > data.len() {
400*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!(
401*e1997b9aSAndroid Build Coastguard Worker "blob data size {} bigger than data (len={})",
402*e1997b9aSAndroid Build Coastguard Worker blob_data_size,
403*e1997b9aSAndroid Build Coastguard Worker data.len()
404*e1997b9aSAndroid Build Coastguard Worker ));
405*e1997b9aSAndroid Build Coastguard Worker }
406*e1997b9aSAndroid Build Coastguard Worker
407*e1997b9aSAndroid Build Coastguard Worker let blob_data = &data[..blob_data_size];
408*e1997b9aSAndroid Build Coastguard Worker let mut next_blob_offset = 0;
409*e1997b9aSAndroid Build Coastguard Worker
410*e1997b9aSAndroid Build Coastguard Worker // Move past the blob data.
411*e1997b9aSAndroid Build Coastguard Worker *data = &data[blob_data_size..];
412*e1997b9aSAndroid Build Coastguard Worker
413*e1997b9aSAndroid Build Coastguard Worker let param_count = consume_u32(data)? as usize;
414*e1997b9aSAndroid Build Coastguard Worker let param_size = consume_u32(data)? as usize;
415*e1997b9aSAndroid Build Coastguard Worker if param_size > data.len() {
416*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!(
417*e1997b9aSAndroid Build Coastguard Worker "size mismatch 4+{}+4+4+{} > {}",
418*e1997b9aSAndroid Build Coastguard Worker blob_data_size,
419*e1997b9aSAndroid Build Coastguard Worker param_size,
420*e1997b9aSAndroid Build Coastguard Worker data.len()
421*e1997b9aSAndroid Build Coastguard Worker ));
422*e1997b9aSAndroid Build Coastguard Worker }
423*e1997b9aSAndroid Build Coastguard Worker
424*e1997b9aSAndroid Build Coastguard Worker let mut results = Vec::new();
425*e1997b9aSAndroid Build Coastguard Worker for _i in 0..param_count {
426*e1997b9aSAndroid Build Coastguard Worker let tag_num = consume_u32(data)? as i32;
427*e1997b9aSAndroid Build Coastguard Worker let tag = Tag(tag_num);
428*e1997b9aSAndroid Build Coastguard Worker let value = match tag_type(&tag) {
429*e1997b9aSAndroid Build Coastguard Worker TagType::INVALID => return Err(bloberr!("invalid tag {:?} encountered", tag)),
430*e1997b9aSAndroid Build Coastguard Worker TagType::ENUM | TagType::ENUM_REP => {
431*e1997b9aSAndroid Build Coastguard Worker let val = consume_i32(data)?;
432*e1997b9aSAndroid Build Coastguard Worker match tag {
433*e1997b9aSAndroid Build Coastguard Worker Tag::ALGORITHM => KeyParameterValue::Algorithm(Algorithm(val)),
434*e1997b9aSAndroid Build Coastguard Worker Tag::BLOCK_MODE => KeyParameterValue::BlockMode(BlockMode(val)),
435*e1997b9aSAndroid Build Coastguard Worker Tag::PADDING => KeyParameterValue::PaddingMode(PaddingMode(val)),
436*e1997b9aSAndroid Build Coastguard Worker Tag::DIGEST | Tag::RSA_OAEP_MGF_DIGEST => {
437*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Digest(Digest(val))
438*e1997b9aSAndroid Build Coastguard Worker }
439*e1997b9aSAndroid Build Coastguard Worker Tag::EC_CURVE => KeyParameterValue::EcCurve(EcCurve(val)),
440*e1997b9aSAndroid Build Coastguard Worker Tag::ORIGIN => KeyParameterValue::Origin(KeyOrigin(val)),
441*e1997b9aSAndroid Build Coastguard Worker Tag::PURPOSE => KeyParameterValue::KeyPurpose(KeyPurpose(val)),
442*e1997b9aSAndroid Build Coastguard Worker Tag::USER_AUTH_TYPE => {
443*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(val))
444*e1997b9aSAndroid Build Coastguard Worker }
445*e1997b9aSAndroid Build Coastguard Worker _ => KeyParameterValue::Integer(val),
446*e1997b9aSAndroid Build Coastguard Worker }
447*e1997b9aSAndroid Build Coastguard Worker }
448*e1997b9aSAndroid Build Coastguard Worker TagType::UINT | TagType::UINT_REP => KeyParameterValue::Integer(consume_i32(data)?),
449*e1997b9aSAndroid Build Coastguard Worker TagType::ULONG | TagType::ULONG_REP => {
450*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::LongInteger(consume_i64(data)?)
451*e1997b9aSAndroid Build Coastguard Worker }
452*e1997b9aSAndroid Build Coastguard Worker TagType::DATE => KeyParameterValue::DateTime(consume_i64(data)?),
453*e1997b9aSAndroid Build Coastguard Worker TagType::BOOL => KeyParameterValue::BoolValue(consume_bool(data)?),
454*e1997b9aSAndroid Build Coastguard Worker TagType::BIGNUM | TagType::BYTES => {
455*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Blob(consume_blob(data, &mut next_blob_offset, blob_data)?)
456*e1997b9aSAndroid Build Coastguard Worker }
457*e1997b9aSAndroid Build Coastguard Worker _ => return Err(bloberr!("unexpected tag type for {:?}", tag)),
458*e1997b9aSAndroid Build Coastguard Worker };
459*e1997b9aSAndroid Build Coastguard Worker results.push(KeyParameter { tag, value });
460*e1997b9aSAndroid Build Coastguard Worker }
461*e1997b9aSAndroid Build Coastguard Worker
462*e1997b9aSAndroid Build Coastguard Worker Ok(results)
463*e1997b9aSAndroid Build Coastguard Worker }
464*e1997b9aSAndroid Build Coastguard Worker
465*e1997b9aSAndroid Build Coastguard Worker /// Serialize a collection of [`KeyParameter`]s into a format that is compatible with previous
466*e1997b9aSAndroid Build Coastguard Worker /// implementations:
467*e1997b9aSAndroid Build Coastguard Worker ///
468*e1997b9aSAndroid Build Coastguard Worker /// ```text
469*e1997b9aSAndroid Build Coastguard Worker /// [0..4] Size B of `TagType::Bytes` data, in host order.
470*e1997b9aSAndroid Build Coastguard Worker /// [4..4+B] (*) Concatenated contents of each `TagType::Bytes` tag.
471*e1997b9aSAndroid Build Coastguard Worker /// [4+B..4+B+4] Count N of the number of parameters, in host order.
472*e1997b9aSAndroid Build Coastguard Worker /// [8+B..8+B+4] Size Z of encoded parameters.
473*e1997b9aSAndroid Build Coastguard Worker /// [12+B..12+B+Z] Serialized parameters one after another.
474*e1997b9aSAndroid Build Coastguard Worker /// ```
475*e1997b9aSAndroid Build Coastguard Worker ///
476*e1997b9aSAndroid Build Coastguard Worker /// Individual parameters are serialized in the last chunk as:
477*e1997b9aSAndroid Build Coastguard Worker ///
478*e1997b9aSAndroid Build Coastguard Worker /// ```text
479*e1997b9aSAndroid Build Coastguard Worker /// [0..4] Tag number, in host order.
480*e1997b9aSAndroid Build Coastguard Worker /// Followed by one of the following depending on the tag's `TagType`; all integers in host order:
481*e1997b9aSAndroid Build Coastguard Worker /// [4..5] Bool value (`TagType::Bool`)
482*e1997b9aSAndroid Build Coastguard Worker /// [4..8] i32 values (`TagType::Uint[Rep]`, `TagType::Enum[Rep]`)
483*e1997b9aSAndroid Build Coastguard Worker /// [4..12] i64 values, in host order (`TagType::UlongRep`, `TagType::Date`)
484*e1997b9aSAndroid Build Coastguard Worker /// [4..8] + [8..12] Size + offset of data in (*) above (`TagType::Bytes`, `TagType::Bignum`)
485*e1997b9aSAndroid Build Coastguard Worker /// ```
serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>>486*e1997b9aSAndroid Build Coastguard Worker fn serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>> {
487*e1997b9aSAndroid Build Coastguard Worker // First 4 bytes are the length of the combined [`TagType::Bytes`] data; come back to set that
488*e1997b9aSAndroid Build Coastguard Worker // in a moment.
489*e1997b9aSAndroid Build Coastguard Worker let mut result = vec![0; 4];
490*e1997b9aSAndroid Build Coastguard Worker
491*e1997b9aSAndroid Build Coastguard Worker // Next append the contents of all of the [`TagType::Bytes`] data.
492*e1997b9aSAndroid Build Coastguard Worker let mut blob_size = 0u32;
493*e1997b9aSAndroid Build Coastguard Worker for param in params {
494*e1997b9aSAndroid Build Coastguard Worker let tag_type = tag_type(¶m.tag);
495*e1997b9aSAndroid Build Coastguard Worker if let KeyParameterValue::Blob(v) = ¶m.value {
496*e1997b9aSAndroid Build Coastguard Worker if tag_type != TagType::BIGNUM && tag_type != TagType::BYTES {
497*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("unexpected tag type for tag {:?} with blob", param.tag));
498*e1997b9aSAndroid Build Coastguard Worker }
499*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(v);
500*e1997b9aSAndroid Build Coastguard Worker blob_size += v.len() as u32;
501*e1997b9aSAndroid Build Coastguard Worker }
502*e1997b9aSAndroid Build Coastguard Worker }
503*e1997b9aSAndroid Build Coastguard Worker // Go back and fill in the combined blob length in native order at the start.
504*e1997b9aSAndroid Build Coastguard Worker result[..4].clone_from_slice(&blob_size.to_ne_bytes());
505*e1997b9aSAndroid Build Coastguard Worker
506*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(params.len() as u32).to_ne_bytes());
507*e1997b9aSAndroid Build Coastguard Worker
508*e1997b9aSAndroid Build Coastguard Worker let params_size_offset = result.len();
509*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&[0u8; 4]); // placeholder for size of elements
510*e1997b9aSAndroid Build Coastguard Worker let first_param_offset = result.len();
511*e1997b9aSAndroid Build Coastguard Worker let mut blob_offset = 0u32;
512*e1997b9aSAndroid Build Coastguard Worker for param in params {
513*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(param.tag.0 as u32).to_ne_bytes());
514*e1997b9aSAndroid Build Coastguard Worker match ¶m.value {
515*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Invalid(_v) => {
516*e1997b9aSAndroid Build Coastguard Worker return Err(bloberr!("invalid tag found in {:?}", param))
517*e1997b9aSAndroid Build Coastguard Worker }
518*e1997b9aSAndroid Build Coastguard Worker
519*e1997b9aSAndroid Build Coastguard Worker // Enum-holding variants.
520*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Algorithm(v) => {
521*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
522*e1997b9aSAndroid Build Coastguard Worker }
523*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::BlockMode(v) => {
524*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
525*e1997b9aSAndroid Build Coastguard Worker }
526*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::PaddingMode(v) => {
527*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
528*e1997b9aSAndroid Build Coastguard Worker }
529*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Digest(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()),
530*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::EcCurve(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()),
531*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Origin(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()),
532*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::KeyPurpose(v) => {
533*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
534*e1997b9aSAndroid Build Coastguard Worker }
535*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::HardwareAuthenticatorType(v) => {
536*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
537*e1997b9aSAndroid Build Coastguard Worker }
538*e1997b9aSAndroid Build Coastguard Worker
539*e1997b9aSAndroid Build Coastguard Worker // Value-holding variants.
540*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Integer(v) => result.extend_from_slice(&(*v as u32).to_ne_bytes()),
541*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::BoolValue(_v) => result.push(0x01u8),
542*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::LongInteger(v) | KeyParameterValue::DateTime(v) => {
543*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&(*v as u64).to_ne_bytes())
544*e1997b9aSAndroid Build Coastguard Worker }
545*e1997b9aSAndroid Build Coastguard Worker KeyParameterValue::Blob(v) => {
546*e1997b9aSAndroid Build Coastguard Worker let blob_len = v.len() as u32;
547*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&blob_len.to_ne_bytes());
548*e1997b9aSAndroid Build Coastguard Worker result.extend_from_slice(&blob_offset.to_ne_bytes());
549*e1997b9aSAndroid Build Coastguard Worker blob_offset += blob_len;
550*e1997b9aSAndroid Build Coastguard Worker }
551*e1997b9aSAndroid Build Coastguard Worker
552*e1997b9aSAndroid Build Coastguard Worker _ => return Err(bloberr!("unknown value found in {:?}", param)),
553*e1997b9aSAndroid Build Coastguard Worker }
554*e1997b9aSAndroid Build Coastguard Worker }
555*e1997b9aSAndroid Build Coastguard Worker let serialized_size = (result.len() - first_param_offset) as u32;
556*e1997b9aSAndroid Build Coastguard Worker
557*e1997b9aSAndroid Build Coastguard Worker // Go back and fill in the total serialized size.
558*e1997b9aSAndroid Build Coastguard Worker result[params_size_offset..params_size_offset + 4]
559*e1997b9aSAndroid Build Coastguard Worker .clone_from_slice(&serialized_size.to_ne_bytes());
560*e1997b9aSAndroid Build Coastguard Worker Ok(result)
561*e1997b9aSAndroid Build Coastguard Worker }
562