aboutsummaryrefslogtreecommitdiff
path: root/rust/src/ops.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/ops.rs')
-rw-r--r--rust/src/ops.rs188
1 files changed, 181 insertions, 7 deletions
diff --git a/rust/src/ops.rs b/rust/src/ops.rs
index 88e9d95..d8c1b16 100644
--- a/rust/src/ops.rs
+++ b/rust/src/ops.rs
@@ -19,8 +19,8 @@
extern crate alloc;
-use crate::{error::result_to_io_enum, CertOps, IoError, IoResult};
-use avb_bindgen::{AvbCertOps, AvbIOResult, AvbOps};
+use crate::{error::result_to_io_enum, CertOps, IoError, IoResult, SHA256_DIGEST_SIZE};
+use avb_bindgen::{AvbCertOps, AvbCertPermanentAttributes, AvbIOResult, AvbOps};
use core::{
cmp::min,
ffi::{c_char, c_void, CStr},
@@ -250,6 +250,10 @@ pub trait Ops<'a> {
/// Commonly when using certs the same struct will implement both `Ops` and `CertOps`, in which
/// case this can just return `Some(self)`.
///
+ /// Note: changing this return value in the middle of a libavb operation (e.g. from another
+ /// callback) is not recommended; it may cause runtime errors or a panic later on in the
+ /// operation. It's fine to change this return value outside of libavb operations.
+ ///
/// # Returns
/// The `CertOps` object, or `None` if not supported.
fn cert_ops(&mut self) -> Option<&mut dyn CertOps> {
@@ -323,11 +327,10 @@ impl<'o, 'p> OpsBridge<'o, 'p> {
},
cert_ops: AvbCertOps {
ops: ptr::null_mut(), // Set at the time of use.
- // TODO(b/320543206): implement the callbacks.
- read_permanent_attributes: None,
- read_permanent_attributes_hash: None,
- set_key_version: None,
- get_random: None,
+ read_permanent_attributes: Some(read_permanent_attributes),
+ read_permanent_attributes_hash: Some(read_permanent_attributes_hash),
+ set_key_version: Some(set_key_version),
+ get_random: Some(get_random),
},
rust_ops: ops,
_pin: PhantomPinned,
@@ -410,6 +413,23 @@ unsafe fn as_ops<'o, 'p>(avb_ops: *mut AvbOps) -> IoResult<&'o mut dyn Ops<'p>>
Ok(unsafe { bridge.as_mut() }.ok_or(IoError::Io)?.rust_ops)
}
+/// Similar to `as_ops()`, but for `CertOps`.
+///
+/// # Safety
+/// Same as `as_ops()`.
+unsafe fn as_cert_ops<'o>(cert_ops: *mut AvbCertOps) -> IoResult<&'o mut dyn CertOps> {
+ // SAFETY: we created this `CertOps` object and passed it to libavb so we know it meets all
+ // the criteria for `as_mut()`.
+ let cert_ops = unsafe { cert_ops.as_mut() }.ok_or(IoError::Io)?;
+
+ // SAFETY: caller must adhere to `as_ops()` safety requirements.
+ let ops = unsafe { as_ops(cert_ops.ops) }?;
+
+ // Return the `CertOps` implementation. If it doesn't exist here, it indicates an internal error
+ // in this library; somewhere we accepted a non-cert `Ops` into a function that requires cert.
+ ops.cert_ops().ok_or(IoError::NotImplemented)
+}
+
/// Converts a non-NULL `ptr` to `()`, NULL to `Err(IoError::Io)`.
fn check_nonnull<T>(ptr: *const T) -> IoResult<()> {
match ptr.is_null() {
@@ -1167,3 +1187,157 @@ unsafe fn try_validate_public_key_for_partition(
}
Ok(())
}
+
+/// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
+///
+/// See corresponding `try_*` function docs.
+unsafe extern "C" fn read_permanent_attributes(
+ cert_ops: *mut AvbCertOps,
+ attributes: *mut AvbCertPermanentAttributes,
+) -> AvbIOResult {
+ result_to_io_enum(
+ // SAFETY: see corresponding `try_*` function safety documentation.
+ unsafe { try_read_permanent_attributes(cert_ops, attributes) },
+ )
+}
+
+/// Bounces the C callback into the user-provided Rust implementation.
+///
+/// # Safety
+/// * `cert_ops` must have been created via `ScopedAvbOps`.
+/// * `attributes` must be a valid `AvbCertPermanentAttributes` that we have exclusive access to.
+unsafe fn try_read_permanent_attributes(
+ cert_ops: *mut AvbCertOps,
+ attributes: *mut AvbCertPermanentAttributes,
+) -> IoResult<()> {
+ // SAFETY: `attributes` is a valid object provided by libavb that we have exclusive access to.
+ let attributes = unsafe { attributes.as_mut() }.ok_or(IoError::Io)?;
+
+ // SAFETY:
+ // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
+ // * `cert_ops` is only extracted once and is dropped at the end of the callback.
+ let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
+ cert_ops.read_permanent_attributes(attributes)
+}
+
+/// Wraps a callback to convert the given `IoResult<>` to raw `AvbIOResult` for libavb.
+///
+/// See corresponding `try_*` function docs.
+unsafe extern "C" fn read_permanent_attributes_hash(
+ cert_ops: *mut AvbCertOps,
+ hash: *mut u8,
+) -> AvbIOResult {
+ result_to_io_enum(
+ // SAFETY: see corresponding `try_*` function safety documentation.
+ unsafe { try_read_permanent_attributes_hash(cert_ops, hash) },
+ )
+}
+
+/// Bounces the C callback into the user-provided Rust implementation.
+///
+/// # Safety
+/// * `cert_ops` must have been created via `ScopedAvbOps`.
+/// * `hash` must point to a valid buffer of size `SHA256_DIGEST_SIZE` that we have exclusive
+/// access to.
+unsafe fn try_read_permanent_attributes_hash(
+ cert_ops: *mut AvbCertOps,
+ hash: *mut u8,
+) -> IoResult<()> {
+ check_nonnull(hash)?;
+
+ // SAFETY:
+ // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
+ // * `cert_ops` is only extracted once and is dropped at the end of the callback.
+ let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
+ let provided_hash = cert_ops.read_permanent_attributes_hash()?;
+
+ // SAFETY:
+ // * `provided_hash` is a valid `[u8]` with size `SHA256_DIGEST_SIZE`.
+ // * libavb gives us a properly-allocated `hash` with size `SHA256_DIGEST_SIZE`.
+ // * the arrays are independent objects and cannot overlap.
+ unsafe { ptr::copy_nonoverlapping(provided_hash.as_ptr(), hash, SHA256_DIGEST_SIZE) };
+
+ Ok(())
+}
+
+/// Wraps a callback to convert the given `IoResult<>` to `None` for libavb.
+///
+/// See corresponding `try_*` function docs.
+unsafe extern "C" fn set_key_version(
+ cert_ops: *mut AvbCertOps,
+ rollback_index_location: usize,
+ key_version: u64,
+) {
+ // SAFETY: see corresponding `try_*` function safety documentation.
+ let result = unsafe { try_set_key_version(cert_ops, rollback_index_location, key_version) };
+
+ // `set_key_version()` is unique in that it has no return value, and therefore cannot fail.
+ // However, our internal C -> Rust logic does have some potential failure points when we unwrap
+ // the C pointers to extract our Rust objects.
+ //
+ // Ignoring the error could be a security risk, as it would silently prevent the device from
+ // updating key rollback versions, so instead we panic here.
+ if let Err(e) = result {
+ panic!("Fatal error in set_key_version(): {:?}", e);
+ }
+}
+
+/// Bounces the C callback into the user-provided Rust implementation.
+///
+/// # Safety
+/// `cert_ops` must have been created via `ScopedAvbOps`.
+unsafe fn try_set_key_version(
+ cert_ops: *mut AvbCertOps,
+ rollback_index_location: usize,
+ key_version: u64,
+) -> IoResult<()> {
+ // SAFETY:
+ // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
+ // * `cert_ops` is only extracted once and is dropped at the end of the callback.
+ let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
+ cert_ops.set_key_version(rollback_index_location, key_version);
+ Ok(())
+}
+
+/// Wraps a callback to convert the given `IoResult<>` to `None` for libavb.
+///
+/// See corresponding `try_*` function docs.
+unsafe extern "C" fn get_random(
+ cert_ops: *mut AvbCertOps,
+ num_bytes: usize,
+ output: *mut u8,
+) -> AvbIOResult {
+ result_to_io_enum(
+ // SAFETY: see corresponding `try_*` function safety documentation.
+ unsafe { try_get_random(cert_ops, num_bytes, output) },
+ )
+}
+
+/// Bounces the C callback into the user-provided Rust implementation.
+///
+/// # Safety
+/// * `cert_ops` must have been created via `ScopedAvbOps`.
+/// * `output` must point to a valid buffer of size `num_bytes` that we have exclusive access to.
+unsafe fn try_get_random(
+ cert_ops: *mut AvbCertOps,
+ num_bytes: usize,
+ output: *mut u8,
+) -> IoResult<()> {
+ check_nonnull(output)?;
+ if num_bytes == 0 {
+ return Ok(());
+ }
+
+ // SAFETY:
+ // * we've checked that the pointer is non-NULL.
+ // * libavb gives us a properly-allocated `output` with size `num_bytes`.
+ // * we only access the contents via the returned slice.
+ // * the returned slice is not held past the scope of this callback.
+ let output = unsafe { slice::from_raw_parts_mut(output, num_bytes) };
+
+ // SAFETY:
+ // * we only use `cert_ops` objects created via `ScopedAvbOps` as required.
+ // * `cert_ops` is only extracted once and is dropped at the end of the callback.
+ let cert_ops = unsafe { as_cert_ops(cert_ops) }?;
+ cert_ops.get_random(output)
+}