blob: 21139b4466cb4ab3653f56b347624038c14e7cdc [file]
// Licensed under the Apache-2.0 license
//! Crypto Server Application
//!
//! A userspace crypto service that handles IPC requests from clients.
//! Uses trait-based backend abstraction for pluggable crypto implementations.
#![no_main]
#![no_std]
use crypto_api::{
CryptoError, CryptoOp, CryptoRequestHeader, CryptoResponseHeader,
};
use crypto_api::backend::{
OneShot, Streaming,
Sha256 as Sha256Marker, Sha384 as Sha384Marker, Sha512 as Sha512Marker,
HmacSha256 as HmacSha256Marker, HmacSha384 as HmacSha384Marker, HmacSha512 as HmacSha512Marker,
Aes256GcmEncrypt, Aes256GcmDecrypt,
GetRandomBytes,
};
use crypto_backend_rustcrypto::{
RustCryptoBackend,
Sha256Session, Sha384Session, Sha512Session,
};
use pw_status::Result;
use userspace::entry;
use userspace::syscall::{self, Signals};
use userspace::time::Instant;
use app_crypto_server::handle;
const MAX_REQUEST_SIZE: usize = 1024;
const MAX_RESPONSE_SIZE: usize = 1024;
/// Per-channel streaming hash session state.
enum HashSession {
None,
Sha256(Sha256Session),
Sha384(Sha384Session),
Sha512(Sha512Session),
}
impl HashSession {
fn is_none(&self) -> bool {
matches!(self, HashSession::None)
}
}
/// All channel handles served by this crypto server instance.
/// Each entry: (channel_handle, user_data for WG)
const CHANNELS: [(u32, usize); 6] = [
(handle::CRYPTO_REQ, 0),
(handle::CRYPTO_REQ_M1, 1),
(handle::CRYPTO_REQ_L1, 2),
(handle::CRYPTO_RESP, 3),
(handle::CRYPTO_RESP_M1, 4),
(handle::CRYPTO_RESP_L1, 5),
];
fn crypto_server_loop() -> Result<()> {
pw_log::info!("Crypto server starting");
let mut request_buf = [0u8; MAX_REQUEST_SIZE];
let mut response_buf = [0u8; MAX_RESPONSE_SIZE];
// Independent session state per channel
let mut sessions: [HashSession; 6] = [
HashSession::None,
HashSession::None,
HashSession::None,
HashSession::None,
HashSession::None,
HashSession::None,
];
let mut backend = RustCryptoBackend::new();
// Register all channels in the WaitGroup
for (chan, ud) in CHANNELS {
syscall::wait_group_add(handle::WG, chan, Signals::READABLE, ud)?;
}
loop {
let ev = syscall::object_wait(handle::WG, Signals::READABLE, Instant::MAX)?;
let idx = ev.user_data;
let chan = CHANNELS[idx].0;
let len = syscall::channel_read(chan, 0, &mut request_buf)?;
if len < CryptoRequestHeader::SIZE {
let header = CryptoResponseHeader::error(CryptoError::InvalidDataLength);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response_buf[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
syscall::channel_respond(chan, &response_buf[..CryptoResponseHeader::SIZE])?;
continue;
}
let response_len = dispatch_crypto_op(
&request_buf[..len],
&mut response_buf,
&mut sessions[idx],
&mut backend,
);
syscall::channel_respond(chan, &response_buf[..response_len])?;
}
}
fn dispatch_crypto_op(
request: &[u8],
response: &mut [u8],
session: &mut HashSession,
backend: &mut RustCryptoBackend,
) -> usize {
// Parse header
let header_bytes = &request[..CryptoRequestHeader::SIZE];
let Some(header) = zerocopy::Ref::<_, CryptoRequestHeader>::from_bytes(header_bytes).ok() else {
return encode_error(response, CryptoError::InvalidDataLength);
};
let header: &CryptoRequestHeader = &*header;
let op = match header.operation() {
Ok(op) => op,
Err(e) => return encode_error(response, e),
};
// Extract key, nonce, and data from payload
let payload = &request[CryptoRequestHeader::SIZE..];
let key_len = header.key_length();
let nonce_len = header.nonce_length();
let data_len = header.data_length();
if payload.len() < key_len + nonce_len + data_len {
return encode_error(response, CryptoError::InvalidDataLength);
}
let key = &payload[..key_len];
let nonce = &payload[key_len..key_len + nonce_len];
let data = &payload[key_len + nonce_len..key_len + nonce_len + data_len];
match op {
// One-shot hash via backend traits
CryptoOp::Sha256Hash => do_oneshot::<Sha256Marker>(backend, op, key, nonce, data, response),
CryptoOp::Sha384Hash => do_oneshot::<Sha384Marker>(backend, op, key, nonce, data, response),
CryptoOp::Sha512Hash => do_oneshot::<Sha512Marker>(backend, op, key, nonce, data, response),
// Streaming hash - SHA-256 via backend Streaming trait
CryptoOp::Sha256Begin => do_sha256_begin(backend, session, response),
CryptoOp::Sha256Update => do_sha256_update(backend, session, data, response),
CryptoOp::Sha256Finish => do_sha256_finish(backend, session, response),
// Streaming hash - SHA-384 via backend Streaming trait
CryptoOp::Sha384Begin => do_sha384_begin(backend, session, response),
CryptoOp::Sha384Update => do_sha384_update(backend, session, data, response),
CryptoOp::Sha384Finish => do_sha384_finish(backend, session, response),
// Streaming hash - SHA-512 via backend Streaming trait
CryptoOp::Sha512Begin => do_sha512_begin(backend, session, response),
CryptoOp::Sha512Update => do_sha512_update(backend, session, data, response),
CryptoOp::Sha512Finish => do_sha512_finish(backend, session, response),
// HMAC via backend traits
CryptoOp::HmacSha256 => do_oneshot::<HmacSha256Marker>(backend, op, key, nonce, data, response),
CryptoOp::HmacSha384 => do_oneshot::<HmacSha384Marker>(backend, op, key, nonce, data, response),
CryptoOp::HmacSha512 => do_oneshot::<HmacSha512Marker>(backend, op, key, nonce, data, response),
// AES-GCM via backend traits
CryptoOp::Aes256GcmEncrypt => do_oneshot::<Aes256GcmEncrypt>(backend, op, key, nonce, data, response),
CryptoOp::Aes256GcmDecrypt => do_oneshot::<Aes256GcmDecrypt>(backend, op, key, nonce, data, response),
// RNG via backend traits
CryptoOp::GetRandomBytes => do_oneshot::<GetRandomBytes>(backend, op, key, nonce, data, response),
// ECDSA not yet migrated to backend traits
CryptoOp::EcdsaP256Sign | CryptoOp::EcdsaP256Verify |
CryptoOp::EcdsaP384Sign | CryptoOp::EcdsaP384Verify => {
encode_error(response, CryptoError::InvalidOperation)
}
}
}
/// Generic one-shot operation via backend trait.
fn do_oneshot<A>(
backend: &RustCryptoBackend,
op: CryptoOp,
key: &[u8],
nonce: &[u8],
data: &[u8],
response: &mut [u8],
) -> usize
where
A: crypto_api::backend::Algorithm,
RustCryptoBackend: OneShot<A>,
{
use crypto_api::backend::CryptoInput;
let input = CryptoInput::from_wire(op, key, nonce, data);
let output = &mut response[CryptoResponseHeader::SIZE..];
match backend.compute(&input, output) {
Ok(len) => {
let header = CryptoResponseHeader::success(len as u16);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
CryptoResponseHeader::SIZE + len
}
Err(e) => encode_error(response, e.into()),
}
}
fn encode_error(response: &mut [u8], err: CryptoError) -> usize {
let header = CryptoResponseHeader::error(err);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
CryptoResponseHeader::SIZE
}
fn encode_success(response: &mut [u8], result: &[u8]) -> usize {
let header = CryptoResponseHeader::success(result.len() as u16);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
response[CryptoResponseHeader::SIZE..CryptoResponseHeader::SIZE + result.len()]
.copy_from_slice(result);
CryptoResponseHeader::SIZE + result.len()
}
// ---------------------------------------------------------------------------
// Streaming hash operations via backend Streaming<A> trait
// ---------------------------------------------------------------------------
// SHA-256 streaming
fn do_sha256_begin(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
response: &mut [u8],
) -> usize {
if !session.is_none() {
return encode_error(response, CryptoError::SessionBusy);
}
match <RustCryptoBackend as Streaming<Sha256Marker>>::begin(backend) {
Ok(s) => {
*session = HashSession::Sha256(s);
encode_success(response, &[])
}
Err(e) => encode_error(response, e.into()),
}
}
fn do_sha256_update(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
data: &[u8],
response: &mut [u8],
) -> usize {
match session {
HashSession::Sha256(s) => {
match <RustCryptoBackend as Streaming<Sha256Marker>>::feed(backend, s, data) {
Ok(()) => encode_success(response, &[]),
Err(e) => encode_error(response, e.into()),
}
}
_ => encode_error(response, CryptoError::SessionNotFound),
}
}
fn do_sha256_finish(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
response: &mut [u8],
) -> usize {
let HashSession::Sha256(s) = core::mem::replace(session, HashSession::None) else {
return encode_error(response, CryptoError::SessionNotFound);
};
let output = &mut response[CryptoResponseHeader::SIZE..];
match <RustCryptoBackend as Streaming<Sha256Marker>>::finish(backend, s, output) {
Ok(len) => {
let header = CryptoResponseHeader::success(len as u16);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
CryptoResponseHeader::SIZE + len
}
Err(e) => encode_error(response, e.into()),
}
}
// SHA-384 streaming
fn do_sha384_begin(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
response: &mut [u8],
) -> usize {
if !session.is_none() {
return encode_error(response, CryptoError::SessionBusy);
}
match <RustCryptoBackend as Streaming<Sha384Marker>>::begin(backend) {
Ok(s) => {
*session = HashSession::Sha384(s);
encode_success(response, &[])
}
Err(e) => encode_error(response, e.into()),
}
}
fn do_sha384_update(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
data: &[u8],
response: &mut [u8],
) -> usize {
match session {
HashSession::Sha384(s) => {
match <RustCryptoBackend as Streaming<Sha384Marker>>::feed(backend, s, data) {
Ok(()) => encode_success(response, &[]),
Err(e) => encode_error(response, e.into()),
}
}
_ => encode_error(response, CryptoError::SessionNotFound),
}
}
fn do_sha384_finish(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
response: &mut [u8],
) -> usize {
let HashSession::Sha384(s) = core::mem::replace(session, HashSession::None) else {
return encode_error(response, CryptoError::SessionNotFound);
};
let output = &mut response[CryptoResponseHeader::SIZE..];
match <RustCryptoBackend as Streaming<Sha384Marker>>::finish(backend, s, output) {
Ok(len) => {
let header = CryptoResponseHeader::success(len as u16);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
CryptoResponseHeader::SIZE + len
}
Err(e) => encode_error(response, e.into()),
}
}
// SHA-512 streaming
fn do_sha512_begin(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
response: &mut [u8],
) -> usize {
if !session.is_none() {
return encode_error(response, CryptoError::SessionBusy);
}
match <RustCryptoBackend as Streaming<Sha512Marker>>::begin(backend) {
Ok(s) => {
*session = HashSession::Sha512(s);
encode_success(response, &[])
}
Err(e) => encode_error(response, e.into()),
}
}
fn do_sha512_update(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
data: &[u8],
response: &mut [u8],
) -> usize {
match session {
HashSession::Sha512(s) => {
match <RustCryptoBackend as Streaming<Sha512Marker>>::feed(backend, s, data) {
Ok(()) => encode_success(response, &[]),
Err(e) => encode_error(response, e.into()),
}
}
_ => encode_error(response, CryptoError::SessionNotFound),
}
}
fn do_sha512_finish(
backend: &mut RustCryptoBackend,
session: &mut HashSession,
response: &mut [u8],
) -> usize {
let HashSession::Sha512(s) = core::mem::replace(session, HashSession::None) else {
return encode_error(response, CryptoError::SessionNotFound);
};
let output = &mut response[CryptoResponseHeader::SIZE..];
match <RustCryptoBackend as Streaming<Sha512Marker>>::finish(backend, s, output) {
Ok(len) => {
let header = CryptoResponseHeader::success(len as u16);
let header_bytes = zerocopy::IntoBytes::as_bytes(&header);
response[..CryptoResponseHeader::SIZE].copy_from_slice(header_bytes);
CryptoResponseHeader::SIZE + len
}
Err(e) => encode_error(response, e.into()),
}
}
#[entry]
fn entry() -> ! {
if let Err(e) = crypto_server_loop() {
pw_log::error!("Crypto server error: {}", e as u32);
let _ = syscall::debug_shutdown(Err(e));
}
loop {}
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}