Skip to main content

fe11_verify_engine/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Optional Fe11 verification engine. The C ABI exposes whole synchronous requests;
3//! no OpenSSL object, Rust collection or allocator contract escapes this image.
4//!
5//! The loader must pin the image before using the returned table. OpenSSL is
6//! private to this component; its exports and local bindings must be inspected
7//! in the final distribution. Source-level context isolation alone is insufficient.
8#![deny(unsafe_op_in_unsafe_fn)]
9
10mod algorithms;
11mod boundary;
12mod context;
13mod digest;
14mod native;
15mod verification;
16
17use context::Context;
18use fe11_verify_engine_api as api;
19use std::{ffi::c_void, mem::size_of, panic::catch_unwind};
20
21#[derive(Clone, Copy)]
22enum Failure {
23    InvalidRequest,
24    Unavailable,
25    Engine,
26    Allocation,
27}
28
29type Result<T> = std::result::Result<T, Failure>;
30
31impl Failure {
32    fn status(self) -> u32 {
33        match self {
34            Self::InvalidRequest => api::status::INVALID_REQUEST,
35            Self::Unavailable => api::status::UNAVAILABLE,
36            Self::Engine => api::status::ENGINE_FAILURE,
37            Self::Allocation => api::status::ALLOCATION_FAILURE,
38        }
39    }
40}
41
42/// Keep unwinding inside this Rust runtime. Allocation aborts and native faults
43/// are not recoverable status codes; no promise of process isolation is made.
44fn dispatch(action: impl FnOnce() -> Result<()> + std::panic::UnwindSafe) -> u32 {
45    match catch_unwind(action) {
46        Ok(Ok(())) => api::status::OK,
47        Ok(Err(failure)) => failure.status(),
48        Err(_) => api::status::ENGINE_FAILURE,
49    }
50}
51
52unsafe extern "C" fn information(output: *mut api::Information, size: usize) -> u32 {
53    if size != size_of::<api::Information>() || !boundary::output_is_aligned(output) {
54        return api::status::INVALID_REQUEST;
55    }
56    // SAFETY: caller provides disjoint writable storage of this checked shape.
57    unsafe { output.write(std::mem::zeroed()) };
58    dispatch(|| {
59        let _context = Context::new()?;
60        let value = api::Information {
61            abi_version: api::ABI_VERSION,
62            structure_size: size_of::<api::Information>() as u32,
63            // Cargo supplies the inherited package version. Check the ABI's
64            // u32 components instead of truncating or duplicating the manifest.
65            implementation_version: [
66                env!("CARGO_PKG_VERSION_MAJOR")
67                    .parse()
68                    .map_err(|_| Failure::Engine)?,
69                env!("CARGO_PKG_VERSION_MINOR")
70                    .parse()
71                    .map_err(|_| Failure::Engine)?,
72                env!("CARGO_PKG_VERSION_PATCH")
73                    .parse()
74                    .map_err(|_| Failure::Engine)?,
75            ],
76            // SAFETY: the private library was initialized by Context::new.
77            openssl_version: unsafe { native::OpenSSL_version_num() } as u64,
78        };
79        unsafe { output.write(value) };
80        Ok(())
81    })
82}
83
84unsafe extern "C" fn verification_algorithms(
85    request: *const api::VerificationAlgorithms,
86    size: usize,
87) -> u32 {
88    dispatch(|| {
89        // SAFETY: the caller owns readable descriptors for this synchronous call.
90        let request = unsafe { boundary::request(request, size) }?;
91        unsafe { algorithms::check(request) }
92    })
93}
94
95unsafe extern "C" fn probe(request: *const api::VerificationRequest, size: usize) -> u32 {
96    dispatch(|| {
97        // SAFETY: validated ABI shape, caller's synchronous readable allocation.
98        let request = unsafe { boundary::request(request, size) }?;
99        unsafe { verification::run(request, None) }?;
100        Ok(())
101    })
102}
103
104unsafe extern "C" fn verify(
105    request: *const api::VerificationRequest,
106    size: usize,
107    message: api::Bytes,
108    signature: api::Bytes,
109    output: *mut u32,
110) -> u32 {
111    if !boundary::output_is_aligned(output) {
112        return api::status::INVALID_REQUEST;
113    }
114    // SAFETY: caller provides a writable aligned slot disjoint from all inputs.
115    unsafe { output.write(0) };
116    dispatch(|| {
117        let (request, message, signature) = unsafe {
118            (
119                boundary::request(request, size)?,
120                boundary::bytes(message)?,
121                boundary::bytes(signature)?,
122            )
123        };
124        let verified = unsafe { verification::run(request, Some((message, signature))) }?;
125        unsafe { output.write(u32::from(verified)) };
126        Ok(())
127    })
128}
129
130unsafe extern "C" fn digest(
131    request: *const api::DigestRequest,
132    size: usize,
133    output: *mut api::OwnedBytes,
134) -> u32 {
135    if !boundary::output_is_aligned(output) {
136        return api::status::INVALID_REQUEST;
137    }
138    unsafe { output.write(api::OwnedBytes::EMPTY) };
139    dispatch(|| {
140        let request = unsafe { boundary::request(request, size) }?;
141        let bytes = unsafe { digest::calculate(request) }?;
142        // Publish only after every fallible native operation has completed.
143        unsafe { output.write(bytes.publish()) };
144        Ok(())
145    })
146}
147
148unsafe extern "C" fn release(owner: *mut c_void) {
149    // SAFETY: exact owner returned by this image, surrendered once by the caller.
150    // Buffer destruction only wipes/frees its allocation and cannot unwind.
151    unsafe { boundary::release(owner) };
152}
153
154/// Obtain this component's exact ABI table without initializing OpenSSL.
155///
156/// # Safety
157/// `output` must denote aligned, writable storage for `Interface`. The caller
158/// pins this image before invoking the table and obeys every function's pointer
159/// and ownership contract in fe11-verify-engine-api. Unknown ABI versions and
160/// sizes are rejected without accessing the proposed output storage.
161#[export_name = "Fe11_VerifyEngine_GetInterface"]
162pub unsafe extern "C" fn get_interface(
163    version: u32,
164    output: *mut api::Interface,
165    size: usize,
166) -> u32 {
167    if version != api::ABI_VERSION
168        || size != size_of::<api::Interface>()
169        || !boundary::output_is_aligned(output)
170    {
171        return api::status::INVALID_REQUEST;
172    }
173    let interface = api::Interface {
174        structure_size: size_of::<api::Interface>() as u32,
175        abi_version: api::ABI_VERSION,
176        information: Some(information),
177        probe: Some(probe),
178        verification_algorithms: Some(verification_algorithms),
179        verify: Some(verify),
180        digest: Some(digest),
181        release: Some(release),
182    };
183    // SAFETY: checked shape; the caller guarantees the writable allocation.
184    unsafe { output.write(interface) };
185    api::status::OK
186}