1 // Copyright 2024, 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 //! libavb_cert support.
16 //!
17 //! libavb_cert is an optional extension on top of the standard libavb API. It provides two
18 //! additional features:
19 //!
20 //! 1. Key management
21 //! 2. Authenticated unlock
22 //!
23 //! # Key management
24 //! The standard avb `Ops` must provide callbacks to manually validate vbmeta signing keys. This can
25 //! become complicated when using best-practices such as key heirarchies and rotations, which often
26 //! results in implementations omitting these features and just using a single fixed key.
27 //!
28 //! libavb_cert enables these features more easily by internally managing a set of related keys:
29 //!
30 //! * Product root key (PRK): un-rotateable root key
31 //! * Product intermediate key (PIK): rotateable key signed by the PRK
32 //! * Product signing key (PSK): rotateable key signed by the PIK, used as the vbmeta key
33 //!
34 //! PIK and PSK rotations are supported by storing their versions as rollback indices, so that
35 //! once the keys have been rotated the rollback value updates and the older keys will no longer
36 //! be accepted.
37 //!
38 //! The device validates keys using a fixed blob of data called "permanent attributes", which can
39 //! authenticate via the PRK and never needs to change even when PIK/PSK are rotated.
40 //!
41 //! To use this functionality, implement the `CertOps` trait and forward
42 //! `validate_vbmeta_public_key()` and/or `validate_public_key_for_partition()` to the provided
43 //! `cert_validate_vbmeta_public_key()` implementation.
44 //!
45 //! # Authenticated unlock
46 //! Typically devices support fastboot commands such as `fastboot flashing unlock` to unlock the
47 //! bootloader. Authenticated unlock is an optional feature that additionally adds an authentication
48 //! requirement in order to unlock the bootloader.
49 //!
50 //! Authenticated unlock introduces one additional key, the product unlock key (PUK), which is
51 //! signed by the PIK. The PUK is in the same key heirarchy but a distinct key, so that access to
52 //! the PUK does not give the ability to sign images. When authenticated unlock is requested,
53 //! libavb_cert produces a randomized "challenge token" which the user must then properly sign with
54 //! the PUK in order to unlock.
55 //!
56 //! It's up to individual device policy how to use authenticated unlock. For example a device may
57 //! want to support standard un-authenticated unlock for most operations, but then additionally
58 //! use authenticated unlock to enable higher-privileged operations.
59 //!
60 //! An example unlock flow using fastboot might look like this:
61 //!
62 //! ```ignore
63 //! # 1. Generate an unlock challenge (the exact fastboot command is device-specific).
64 //! $ fastboot oem get-auth-unlock-challenge
65 //!
66 //! # Internally, the device calls `cert_generate_unlock_challenge()` to generate the token.
67 //!
68 //! # 2. Download the challenge token from the device.
69 //! $ fastboot get_staged /tmp/challenge.bin
70 //!
71 //! # 3. Sign the challenge with the PUK.
72 //! $ avbtool make_cert_unlock_credential \
73 //! --challenge /tmp/challenge.bin \
74 //! --output /tmp/signed.bin \
75 //! ... # see --help for full args
76 //!
77 //! # 4. Upload the signed credential back to the device.
78 //! $ fastboot stage /tmp/signed.bin
79 //!
80 //! # 5. Unlock the device (the exact fastboot command is device-specific).
81 //! $ fastboot oem auth-unlock
82 //!
83 //! # Internally, the device calls `cert_validate_unlock_credential()` to verify the credential.
84 //! ```
85
86 use crate::{error::io_enum_to_result, ops, IoError, IoResult, Ops, PublicKeyForPartitionInfo};
87 use avb_bindgen::{
88 avb_cert_generate_unlock_challenge, avb_cert_validate_unlock_credential,
89 avb_cert_validate_vbmeta_public_key,
90 };
91 use core::{ffi::CStr, pin::pin};
92 #[cfg(feature = "uuid")]
93 use uuid::Uuid;
94
95 /// libavb_cert permanent attributes.
96 pub use avb_bindgen::AvbCertPermanentAttributes as CertPermanentAttributes;
97
98 /// Authenticated unlock challenge.
99 pub use avb_bindgen::AvbCertUnlockChallenge as CertUnlockChallenge;
100
101 /// Signed authenticated unlock credential.
102 pub use avb_bindgen::AvbCertUnlockCredential as CertUnlockCredential;
103
104 /// Size in bytes of a SHA256 digest.
105 pub const SHA256_DIGEST_SIZE: usize = avb_bindgen::AVB_SHA256_DIGEST_SIZE as usize;
106
107 /// Size in bytes of a SHA512 digest.
108 pub const SHA512_DIGEST_SIZE: usize = avb_bindgen::AVB_SHA512_DIGEST_SIZE as usize;
109
110 /// Product intermediate key (PIK) rollback index location.
111 ///
112 /// If using libavb_cert, make sure no vbmetas use this location, it must be reserved for the PIK.
113 pub const CERT_PIK_VERSION_LOCATION: usize = avb_bindgen::AVB_CERT_PIK_VERSION_LOCATION as usize;
114
115 /// Product signing key (PSK) rollback index location.
116 ///
117 /// If using libavb_cert, make sure no vbmetas use this location, it must be reserved for the PSK.
118 pub const CERT_PSK_VERSION_LOCATION: usize = avb_bindgen::AVB_CERT_PSK_VERSION_LOCATION as usize;
119
120 /// libavb_cert extension callbacks.
121 pub trait CertOps {
122 /// Reads the device's permanent attributes.
123 ///
124 /// The full permanent attributes are not required to be securely stored; corruption of this
125 /// data will result in failing to verify the images (denial-of-service), but will not change
126 /// the signing keys or allow improperly-signed images to verify.
127 ///
128 /// # Arguments
129 /// * `attributes`: permanent attributes to update; passed as an output parameter rather than a
130 /// return value due to the size (>1KiB).
131 ///
132 /// # Returns
133 /// Unit on success, error on failure.
read_permanent_attributes( &mut self, attributes: &mut CertPermanentAttributes, ) -> IoResult<()>134 fn read_permanent_attributes(
135 &mut self,
136 attributes: &mut CertPermanentAttributes,
137 ) -> IoResult<()>;
138
139 /// Reads the SHA256 hash of the device's permanent attributes.
140 ///
141 /// This hash must be sourced from secure storage whenever the device is locked; corruption
142 /// of this data could result in changing the signing keys and allowing improperly-signed images
143 /// to pass verification.
144 ///
145 /// This may be calculated at runtime from `read_permanent_attributes()` only if the entire
146 /// permanent attributes are sourced from secure storage, but secure storage space is often
147 /// limited so it can be useful to only store the hash securely.
148 ///
149 /// # Returns
150 /// The 32-byte SHA256 digest on success, error on failure.
read_permanent_attributes_hash(&mut self) -> IoResult<[u8; SHA256_DIGEST_SIZE]>151 fn read_permanent_attributes_hash(&mut self) -> IoResult<[u8; SHA256_DIGEST_SIZE]>;
152
153 /// Provides the key version for the rotateable keys.
154 ///
155 /// libavb_cert stores signing key versions as rollback indices; when this function is called it
156 /// indicates that the key at the given index location is using the given version.
157 ///
158 /// The exact steps to take when receiving this callback depend on device policy, but generally
159 /// these values should only be cached in this callback, and written to the rollback storage
160 /// only after the images are known to be successful.
161 ///
162 /// For example, a device using A/B boot slots should not update the key version rollbacks
163 /// until it knows for sure the new image works, otherwise an OTA could break the A/B fallback
164 /// behavior by updating the key version too soon and preventing falling back to the previous
165 /// slot.
166 ///
167 /// # Arguments
168 /// * `rollback_index_location`: rollback location to store this key version
169 /// * `key_version`: value to store in the rollback location
170 ///
171 /// # Returns
172 /// `None`; since the rollback should be cached rather than written immediately, this function
173 /// cannot fail.
set_key_version(&mut self, rollback_index_location: usize, key_version: u64)174 fn set_key_version(&mut self, rollback_index_location: usize, key_version: u64);
175
176 /// Generates random bytes.
177 ///
178 /// This is only used for authenticated unlock. If authenticated unlock is not needed, this can
179 /// just return `IoError::NotImplemented`.
180 ///
181 /// # Arguments
182 /// * `bytes`: buffer to completely fill with random bytes.
183 ///
184 /// # Returns
185 /// Unit on success, error on failure.
get_random(&mut self, bytes: &mut [u8]) -> IoResult<()>186 fn get_random(&mut self, bytes: &mut [u8]) -> IoResult<()>;
187 }
188
189 /// Certificate-based vbmeta key validation.
190 ///
191 /// This can be called from `validate_vbmeta_public_key()` or `validate_public_key_for_partition()`
192 /// to provide the correct behavior using the libavb_cert keys, such as:
193 ///
194 /// ```
195 /// impl avb::Ops for MyOps {
196 /// fn validate_vbmeta_public_key(
197 /// &mut self,
198 /// public_key: &[u8],
199 /// public_key_metadata: Option<&[u8]>,
200 /// ) -> IoResult<bool> {
201 /// cert_validate_vbmeta_public_key(self, public_key, public_key_metadata)
202 /// }
203 /// }
204 /// ```
205 ///
206 /// We don't automatically call this from the validation functions because it's up to the device
207 /// when to use certificate authentication e.g. a device may want to use libavb_cert only for
208 /// specific partitions.
209 ///
210 /// # Arguments
211 /// * `ops`: the `Ops` callback implementations, which must provide a `cert_ops()` implementation.
212 /// * `public_key`: the public key.
213 /// * `public_key_metadata`: public key metadata.
214 ///
215 /// # Returns
216 /// * `Ok(true)` if the given key is valid according to the permanent attributes.
217 /// * `Ok(false)` if the given key is invalid.
218 /// * `Err(IoError::NotImplemented)` if `ops` does not provide the required `cert_ops()`.
219 /// * `Err(IoError)` on `ops` callback error.
cert_validate_vbmeta_public_key( ops: &mut dyn Ops, public_key: &[u8], public_key_metadata: Option<&[u8]>, ) -> IoResult<bool>220 pub fn cert_validate_vbmeta_public_key(
221 ops: &mut dyn Ops,
222 public_key: &[u8],
223 public_key_metadata: Option<&[u8]>,
224 ) -> IoResult<bool> {
225 // This API requires both AVB and cert ops.
226 if ops.cert_ops().is_none() {
227 return Err(IoError::NotImplemented);
228 }
229
230 let ops_bridge = pin!(ops::OpsBridge::new(ops));
231 let public_key_metadata = public_key_metadata.unwrap_or(&[]);
232 let mut trusted = false;
233 io_enum_to_result(
234 // SAFETY:
235 // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps` with cert.
236 // * `public_key` args are C-compatible pointer + size byte buffers.
237 // * `trusted` is a C-compatible bool.
238 // * this function does not retain references to any of these arguments.
239 unsafe {
240 avb_cert_validate_vbmeta_public_key(
241 ops_bridge.init_and_get_c_ops(),
242 public_key.as_ptr(),
243 public_key.len(),
244 public_key_metadata.as_ptr(),
245 public_key_metadata.len(),
246 &mut trusted,
247 )
248 },
249 )?;
250 Ok(trusted)
251 }
252
253 /// Generates a challenge for authenticated unlock.
254 ///
255 /// Used to create a challenge token to be signed with the PUK.
256 ///
257 /// The user can sign the resulting token via `avbtool make_cert_unlock_credential`.
258 ///
259 /// # Arguments
260 /// * `cert_ops`: the `CertOps` callback implementations; base `Ops` are not required here.
261 ///
262 /// # Returns
263 /// The challenge to sign with the PUK, or `IoError` on `cert_ops` failure.
cert_generate_unlock_challenge(cert_ops: &mut dyn CertOps) -> IoResult<CertUnlockChallenge>264 pub fn cert_generate_unlock_challenge(cert_ops: &mut dyn CertOps) -> IoResult<CertUnlockChallenge> {
265 // `OpsBridge` requires a full `Ops` object, so we wrap `cert_ops` in a do-nothing `Ops`
266 // implementation. This is simpler than teaching `OpsBridge` to handle the cert-only case.
267 let mut ops = CertOnlyOps { cert_ops };
268 let ops_bridge = pin!(ops::OpsBridge::new(&mut ops));
269 let mut challenge = CertUnlockChallenge::default();
270 io_enum_to_result(
271 // SAFETY:
272 // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps` with cert.
273 // * `challenge` is a valid C-compatible `CertUnlockChallenge`.
274 // * this function does not retain references to any of these arguments.
275 unsafe {
276 avb_cert_generate_unlock_challenge(
277 ops_bridge.init_and_get_c_ops().cert_ops,
278 &mut challenge,
279 )
280 },
281 )?;
282 Ok(challenge)
283 }
284
285 /// Validates a signed credential for authenticated unlock.
286 ///
287 /// Used to check that an unlock credential was properly signed with the PUK according to the
288 /// device's permanent attributes.
289 ///
290 /// # Arguments
291 /// * `ops`: the `Ops` callback implementations, which must provide a `cert_ops()` implementation.
292 /// * `credential`: the signed unlock credential to verify.
293 ///
294 /// # Returns
295 /// * `Ok(true)` if the credential validated
296 /// * `Ok(false)` if it failed validation
297 /// * `Err(IoError::NotImplemented)` if `ops` does not provide the required `cert_ops()`.
298 /// * `Err(IoError)` on `ops` failure
cert_validate_unlock_credential( ops: &mut dyn Ops, credential: &CertUnlockCredential, ) -> IoResult<bool>299 pub fn cert_validate_unlock_credential(
300 // Note: in the libavb C API this function takes an `AvbCertOps` rather than `AvbOps`, but
301 // the implementation requires both, so we need an `Ops` here. This is also more consistent
302 // with `validate_vbmeta_public_key()` which similarly requires both but takes `AvbOps`.
303 ops: &mut dyn Ops,
304 credential: &CertUnlockCredential,
305 ) -> IoResult<bool> {
306 // This API requires both AVB and cert ops.
307 if ops.cert_ops().is_none() {
308 return Err(IoError::NotImplemented);
309 }
310
311 let ops_bridge = pin!(ops::OpsBridge::new(ops));
312 let mut trusted = false;
313 io_enum_to_result(
314 // SAFETY:
315 // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps` with cert.
316 // * `credential` is a valid C-compatible `CertUnlockCredential`.
317 // * `trusted` is a C-compatible bool.
318 // * this function does not retain references to any of these arguments.
319 unsafe {
320 avb_cert_validate_unlock_credential(
321 ops_bridge.init_and_get_c_ops().cert_ops,
322 credential,
323 &mut trusted,
324 )
325 },
326 )?;
327 Ok(trusted)
328 }
329
330 /// An `Ops` implementation that only provides the `cert_ops()` callback.
331 struct CertOnlyOps<'a> {
332 cert_ops: &'a mut dyn CertOps,
333 }
334
335 impl Ops<'static> for CertOnlyOps<'_> {
read_from_partition( &mut self, _partition: &CStr, _offset: i64, _buffer: &mut [u8], ) -> IoResult<usize>336 fn read_from_partition(
337 &mut self,
338 _partition: &CStr,
339 _offset: i64,
340 _buffer: &mut [u8],
341 ) -> IoResult<usize> {
342 Err(IoError::NotImplemented)
343 }
344
validate_vbmeta_public_key( &mut self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> IoResult<bool>345 fn validate_vbmeta_public_key(
346 &mut self,
347 _public_key: &[u8],
348 _public_key_metadata: Option<&[u8]>,
349 ) -> IoResult<bool> {
350 Err(IoError::NotImplemented)
351 }
352
read_rollback_index(&mut self, _rollback_index_location: usize) -> IoResult<u64>353 fn read_rollback_index(&mut self, _rollback_index_location: usize) -> IoResult<u64> {
354 Err(IoError::NotImplemented)
355 }
356
write_rollback_index( &mut self, _rollback_index_location: usize, _index: u64, ) -> IoResult<()>357 fn write_rollback_index(
358 &mut self,
359 _rollback_index_location: usize,
360 _index: u64,
361 ) -> IoResult<()> {
362 Err(IoError::NotImplemented)
363 }
364
read_is_device_unlocked(&mut self) -> IoResult<bool>365 fn read_is_device_unlocked(&mut self) -> IoResult<bool> {
366 Err(IoError::NotImplemented)
367 }
368
369 #[cfg(feature = "uuid")]
get_unique_guid_for_partition(&mut self, _partition: &CStr) -> IoResult<Uuid>370 fn get_unique_guid_for_partition(&mut self, _partition: &CStr) -> IoResult<Uuid> {
371 Err(IoError::NotImplemented)
372 }
373
get_size_of_partition(&mut self, _partition: &CStr) -> IoResult<u64>374 fn get_size_of_partition(&mut self, _partition: &CStr) -> IoResult<u64> {
375 Err(IoError::NotImplemented)
376 }
377
read_persistent_value(&mut self, _name: &CStr, _value: &mut [u8]) -> IoResult<usize>378 fn read_persistent_value(&mut self, _name: &CStr, _value: &mut [u8]) -> IoResult<usize> {
379 Err(IoError::NotImplemented)
380 }
381
write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> IoResult<()>382 fn write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> IoResult<()> {
383 Err(IoError::NotImplemented)
384 }
385
erase_persistent_value(&mut self, _name: &CStr) -> IoResult<()>386 fn erase_persistent_value(&mut self, _name: &CStr) -> IoResult<()> {
387 Err(IoError::NotImplemented)
388 }
389
validate_public_key_for_partition( &mut self, _partition: &CStr, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> IoResult<PublicKeyForPartitionInfo>390 fn validate_public_key_for_partition(
391 &mut self,
392 _partition: &CStr,
393 _public_key: &[u8],
394 _public_key_metadata: Option<&[u8]>,
395 ) -> IoResult<PublicKeyForPartitionInfo> {
396 Err(IoError::NotImplemented)
397 }
398
cert_ops(&mut self) -> Option<&mut dyn CertOps>399 fn cert_ops(&mut self) -> Option<&mut dyn CertOps> {
400 Some(self.cert_ops)
401 }
402 }
403