• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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