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 /// Product intermediate key (PIK) rollback index location.
108 ///
109 /// If using libavb_cert, make sure no vbmetas use this location, it must be reserved for the PIK.
110 pub const CERT_PIK_VERSION_LOCATION: usize = avb_bindgen::AVB_CERT_PIK_VERSION_LOCATION as usize;
111
112 /// Product signing key (PSK) rollback index location.
113 ///
114 /// If using libavb_cert, make sure no vbmetas use this location, it must be reserved for the PSK.
115 pub const CERT_PSK_VERSION_LOCATION: usize = avb_bindgen::AVB_CERT_PSK_VERSION_LOCATION as usize;
116
117 /// libavb_cert extension callbacks.
118 pub trait CertOps {
119 /// Reads the device's permanent attributes.
120 ///
121 /// The full permanent attributes are not required to be securely stored; corruption of this
122 /// data will result in failing to verify the images (denial-of-service), but will not change
123 /// the signing keys or allow improperly-signed images to verify.
124 ///
125 /// # Arguments
126 /// * `attributes`: permanent attributes to update; passed as an output parameter rather than a
127 /// return value due to the size (>1KiB).
128 ///
129 /// # Returns
130 /// Unit on success, error on failure.
read_permanent_attributes( &mut self, attributes: &mut CertPermanentAttributes, ) -> IoResult<()>131 fn read_permanent_attributes(
132 &mut self,
133 attributes: &mut CertPermanentAttributes,
134 ) -> IoResult<()>;
135
136 /// Reads the SHA256 hash of the device's permanent attributes.
137 ///
138 /// This hash must be sourced from secure storage whenever the device is locked; corruption
139 /// of this data could result in changing the signing keys and allowing improperly-signed images
140 /// to pass verification.
141 ///
142 /// This may be calculated at runtime from `read_permanent_attributes()` only if the entire
143 /// permanent attributes are sourced from secure storage, but secure storage space is often
144 /// limited so it can be useful to only store the hash securely.
145 ///
146 /// # Returns
147 /// The 32-byte SHA256 digest on success, error on failure.
read_permanent_attributes_hash(&mut self) -> IoResult<[u8; SHA256_DIGEST_SIZE]>148 fn read_permanent_attributes_hash(&mut self) -> IoResult<[u8; SHA256_DIGEST_SIZE]>;
149
150 /// Provides the key version for the rotateable keys.
151 ///
152 /// libavb_cert stores signing key versions as rollback indices; when this function is called it
153 /// indicates that the key at the given index location is using the given version.
154 ///
155 /// The exact steps to take when receiving this callback depend on device policy, but generally
156 /// these values should only be cached in this callback, and written to the rollback storage
157 /// only after the images are known to be successful.
158 ///
159 /// For example, a device using A/B boot slots should not update the key version rollbacks
160 /// until it knows for sure the new image works, otherwise an OTA could break the A/B fallback
161 /// behavior by updating the key version too soon and preventing falling back to the previous
162 /// slot.
163 ///
164 /// # Arguments
165 /// * `rollback_index_location`: rollback location to store this key version
166 /// * `key_version`: value to store in the rollback location
167 ///
168 /// # Returns
169 /// `None`; since the rollback should be cached rather than written immediately, this function
170 /// cannot fail.
set_key_version(&mut self, rollback_index_location: usize, key_version: u64)171 fn set_key_version(&mut self, rollback_index_location: usize, key_version: u64);
172
173 /// Generates random bytes.
174 ///
175 /// This is only used for authenticated unlock. If authenticated unlock is not needed, this can
176 /// just return `IoError::NotImplemented`.
177 ///
178 /// # Arguments
179 /// * `bytes`: buffer to completely fill with random bytes.
180 ///
181 /// # Returns
182 /// Unit on success, error on failure.
get_random(&mut self, bytes: &mut [u8]) -> IoResult<()>183 fn get_random(&mut self, bytes: &mut [u8]) -> IoResult<()>;
184 }
185
186 /// Certificate-based vbmeta key validation.
187 ///
188 /// This can be called from `validate_vbmeta_public_key()` or `validate_public_key_for_partition()`
189 /// to provide the correct behavior using the libavb_cert keys, such as:
190 ///
191 /// ```
192 /// impl avb::Ops for MyOps {
193 /// fn validate_vbmeta_public_key(
194 /// &mut self,
195 /// public_key: &[u8],
196 /// public_key_metadata: Option<&[u8]>,
197 /// ) -> IoResult<bool> {
198 /// cert_validate_vbmeta_public_key(self, public_key, public_key_metadata)
199 /// }
200 /// }
201 /// ```
202 ///
203 /// We don't automatically call this from the validation functions because it's up to the device
204 /// when to use certificate authentication e.g. a device may want to use libavb_cert only for
205 /// specific partitions.
206 ///
207 /// # Arguments
208 /// * `ops`: the `Ops` callback implementations, which must provide a `cert_ops()` implementation.
209 /// * `public_key`: the public key.
210 /// * `public_key_metadata`: public key metadata.
211 ///
212 /// # Returns
213 /// * `Ok(true)` if the given key is valid according to the permanent attributes.
214 /// * `Ok(false)` if the given key is invalid.
215 /// * `Err(IoError::NotImplemented)` if `ops` does not provide the required `cert_ops()`.
216 /// * `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>217 pub fn cert_validate_vbmeta_public_key(
218 ops: &mut dyn Ops,
219 public_key: &[u8],
220 public_key_metadata: Option<&[u8]>,
221 ) -> IoResult<bool> {
222 // This API requires both AVB and cert ops.
223 if ops.cert_ops().is_none() {
224 return Err(IoError::NotImplemented);
225 }
226
227 let ops_bridge = pin!(ops::OpsBridge::new(ops));
228 let public_key_metadata = public_key_metadata.unwrap_or(&[]);
229 let mut trusted = false;
230 io_enum_to_result(
231 // SAFETY:
232 // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps` with cert.
233 // * `public_key` args are C-compatible pointer + size byte buffers.
234 // * `trusted` is a C-compatible bool.
235 // * this function does not retain references to any of these arguments.
236 unsafe {
237 avb_cert_validate_vbmeta_public_key(
238 ops_bridge.init_and_get_c_ops(),
239 public_key.as_ptr(),
240 public_key.len(),
241 public_key_metadata.as_ptr(),
242 public_key_metadata.len(),
243 &mut trusted,
244 )
245 },
246 )?;
247 Ok(trusted)
248 }
249
250 /// Generates a challenge for authenticated unlock.
251 ///
252 /// Used to create a challenge token to be signed with the PUK.
253 ///
254 /// The user can sign the resulting token via `avbtool make_cert_unlock_credential`.
255 ///
256 /// # Arguments
257 /// * `cert_ops`: the `CertOps` callback implementations; base `Ops` are not required here.
258 ///
259 /// # Returns
260 /// The challenge to sign with the PUK, or `IoError` on `cert_ops` failure.
cert_generate_unlock_challenge(cert_ops: &mut dyn CertOps) -> IoResult<CertUnlockChallenge>261 pub fn cert_generate_unlock_challenge(cert_ops: &mut dyn CertOps) -> IoResult<CertUnlockChallenge> {
262 // `OpsBridge` requires a full `Ops` object, so we wrap `cert_ops` in a do-nothing `Ops`
263 // implementation. This is simpler than teaching `OpsBridge` to handle the cert-only case.
264 let mut ops = CertOnlyOps { cert_ops };
265 let ops_bridge = pin!(ops::OpsBridge::new(&mut ops));
266 let mut challenge = CertUnlockChallenge::default();
267 io_enum_to_result(
268 // SAFETY:
269 // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps` with cert.
270 // * `challenge` is a valid C-compatible `CertUnlockChallenge`.
271 // * this function does not retain references to any of these arguments.
272 unsafe {
273 avb_cert_generate_unlock_challenge(
274 ops_bridge.init_and_get_c_ops().cert_ops,
275 &mut challenge,
276 )
277 },
278 )?;
279 Ok(challenge)
280 }
281
282 /// Validates a signed credential for authenticated unlock.
283 ///
284 /// Used to check that an unlock credential was properly signed with the PUK according to the
285 /// device's permanent attributes.
286 ///
287 /// # Arguments
288 /// * `ops`: the `Ops` callback implementations, which must provide a `cert_ops()` implementation.
289 /// * `credential`: the signed unlock credential to verify.
290 ///
291 /// # Returns
292 /// * `Ok(true)` if the credential validated
293 /// * `Ok(false)` if it failed validation
294 /// * `Err(IoError::NotImplemented)` if `ops` does not provide the required `cert_ops()`.
295 /// * `Err(IoError)` on `ops` failure
cert_validate_unlock_credential( ops: &mut dyn Ops, credential: &CertUnlockCredential, ) -> IoResult<bool>296 pub fn cert_validate_unlock_credential(
297 // Note: in the libavb C API this function takes an `AvbCertOps` rather than `AvbOps`, but
298 // the implementation requires both, so we need an `Ops` here. This is also more consistent
299 // with `validate_vbmeta_public_key()` which similarly requires both but takes `AvbOps`.
300 ops: &mut dyn Ops,
301 credential: &CertUnlockCredential,
302 ) -> IoResult<bool> {
303 // This API requires both AVB and cert ops.
304 if ops.cert_ops().is_none() {
305 return Err(IoError::NotImplemented);
306 }
307
308 let ops_bridge = pin!(ops::OpsBridge::new(ops));
309 let mut trusted = false;
310 io_enum_to_result(
311 // SAFETY:
312 // * `ops_bridge.init_and_get_c_ops()` gives us a valid `AvbOps` with cert.
313 // * `credential` is a valid C-compatible `CertUnlockCredential`.
314 // * `trusted` is a C-compatible bool.
315 // * this function does not retain references to any of these arguments.
316 unsafe {
317 avb_cert_validate_unlock_credential(
318 ops_bridge.init_and_get_c_ops().cert_ops,
319 credential,
320 &mut trusted,
321 )
322 },
323 )?;
324 Ok(trusted)
325 }
326
327 /// An `Ops` implementation that only provides the `cert_ops()` callback.
328 struct CertOnlyOps<'a> {
329 cert_ops: &'a mut dyn CertOps,
330 }
331
332 impl<'a> Ops<'static> for CertOnlyOps<'a> {
read_from_partition( &mut self, _partition: &CStr, _offset: i64, _buffer: &mut [u8], ) -> IoResult<usize>333 fn read_from_partition(
334 &mut self,
335 _partition: &CStr,
336 _offset: i64,
337 _buffer: &mut [u8],
338 ) -> IoResult<usize> {
339 Err(IoError::NotImplemented)
340 }
341
validate_vbmeta_public_key( &mut self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> IoResult<bool>342 fn validate_vbmeta_public_key(
343 &mut self,
344 _public_key: &[u8],
345 _public_key_metadata: Option<&[u8]>,
346 ) -> IoResult<bool> {
347 Err(IoError::NotImplemented)
348 }
349
read_rollback_index(&mut self, _rollback_index_location: usize) -> IoResult<u64>350 fn read_rollback_index(&mut self, _rollback_index_location: usize) -> IoResult<u64> {
351 Err(IoError::NotImplemented)
352 }
353
write_rollback_index( &mut self, _rollback_index_location: usize, _index: u64, ) -> IoResult<()>354 fn write_rollback_index(
355 &mut self,
356 _rollback_index_location: usize,
357 _index: u64,
358 ) -> IoResult<()> {
359 Err(IoError::NotImplemented)
360 }
361
read_is_device_unlocked(&mut self) -> IoResult<bool>362 fn read_is_device_unlocked(&mut self) -> IoResult<bool> {
363 Err(IoError::NotImplemented)
364 }
365
366 #[cfg(feature = "uuid")]
get_unique_guid_for_partition(&mut self, _partition: &CStr) -> IoResult<Uuid>367 fn get_unique_guid_for_partition(&mut self, _partition: &CStr) -> IoResult<Uuid> {
368 Err(IoError::NotImplemented)
369 }
370
get_size_of_partition(&mut self, _partition: &CStr) -> IoResult<u64>371 fn get_size_of_partition(&mut self, _partition: &CStr) -> IoResult<u64> {
372 Err(IoError::NotImplemented)
373 }
374
read_persistent_value(&mut self, _name: &CStr, _value: &mut [u8]) -> IoResult<usize>375 fn read_persistent_value(&mut self, _name: &CStr, _value: &mut [u8]) -> IoResult<usize> {
376 Err(IoError::NotImplemented)
377 }
378
write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> IoResult<()>379 fn write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> IoResult<()> {
380 Err(IoError::NotImplemented)
381 }
382
erase_persistent_value(&mut self, _name: &CStr) -> IoResult<()>383 fn erase_persistent_value(&mut self, _name: &CStr) -> IoResult<()> {
384 Err(IoError::NotImplemented)
385 }
386
validate_public_key_for_partition( &mut self, _partition: &CStr, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> IoResult<PublicKeyForPartitionInfo>387 fn validate_public_key_for_partition(
388 &mut self,
389 _partition: &CStr,
390 _public_key: &[u8],
391 _public_key_metadata: Option<&[u8]>,
392 ) -> IoResult<PublicKeyForPartitionInfo> {
393 Err(IoError::NotImplemented)
394 }
395
cert_ops(&mut self) -> Option<&mut dyn CertOps>396 fn cert_ops(&mut self) -> Option<&mut dyn CertOps> {
397 Some(self.cert_ops)
398 }
399 }
400