xref: /aosp_15_r20/external/avb/rust/src/cert.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
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