#![no_std]
extern crate liboscore_cryptobackend;
extern crate liboscore_msgbackend;
use core::mem::MaybeUninit;
mod platform;
pub mod raw;
mod impl_message;
pub use impl_message::ProtectedMessage;
mod oscore_option;
pub use oscore_option::OscoreOption;
mod algorithms;
pub use algorithms::{AeadAlg, AlgorithmNotSupported, HkdfAlg};
mod primitive;
pub use primitive::{DeriveError, PrimitiveContext, PrimitiveImmutables};
#[track_caller]
#[inline]
fn poll_once_expect_immediate<R>(future: impl core::future::Future<Output = R>) -> R {
let future = core::pin::pin!(future);
let mut context = core::task::Context::from_waker(core::task::Waker::noop());
match future.poll(&mut context) {
core::task::Poll::Ready(r) => r,
core::task::Poll::Pending => unreachable!("The function's only await point is in the callback, and the provided callback has none."),
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum PrepareError {
SecurityContextUnavailable,
}
impl PrepareError {
fn new(input: raw::oscore_prepare_result) -> Result<(), Self> {
match input {
raw::oscore_prepare_result_OSCORE_PREPARE_OK => Ok(()),
raw::oscore_prepare_result_OSCORE_PREPARE_SECCTX_UNAVAILABLE => {
Err(PrepareError::SecurityContextUnavailable)
}
_ => unreachable!(),
}
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum FinishError {
Size,
Crypto,
}
impl FinishError {
fn new(input: raw::oscore_finish_result) -> Result<(), Self> {
match input {
raw::oscore_finish_result_OSCORE_FINISH_OK => Ok(()),
raw::oscore_finish_result_OSCORE_FINISH_ERROR_SIZE => Err(FinishError::Size),
raw::oscore_finish_result_OSCORE_FINISH_ERROR_CRYPTO => Err(FinishError::Crypto),
_ => unreachable!(),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum ProtectError {
Prepare(PrepareError),
Finish(FinishError),
}
impl From<PrepareError> for ProtectError {
fn from(e: PrepareError) -> Self {
ProtectError::Prepare(e)
}
}
impl From<FinishError> for ProtectError {
fn from(e: FinishError) -> Self {
ProtectError::Finish(e)
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum UnprotectRequestError {
Duplicate,
Invalid,
}
impl UnprotectRequestError {
fn new(input: raw::oscore_unprotect_request_result) -> Result<(), Self> {
match input {
raw::oscore_unprotect_request_result_OSCORE_UNPROTECT_REQUEST_OK => Ok(()),
raw::oscore_unprotect_request_result_OSCORE_UNPROTECT_REQUEST_DUPLICATE => {
Err(UnprotectRequestError::Duplicate)
}
raw::oscore_unprotect_request_result_OSCORE_UNPROTECT_REQUEST_INVALID => {
Err(UnprotectRequestError::Invalid)
}
_ => unreachable!(),
}
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub enum UnprotectResponseError {
Invalid,
}
impl UnprotectResponseError {
fn new(input: raw::oscore_unprotect_response_result) -> Result<(), Self> {
match input {
raw::oscore_unprotect_response_result_OSCORE_UNPROTECT_RESPONSE_OK => Ok(()),
raw::oscore_unprotect_response_result_OSCORE_UNPROTECT_RESPONSE_INVALID => {
Err(UnprotectResponseError::Invalid)
}
_ => unreachable!(),
}
}
}
pub async fn async_protect_request<R>(
request: impl liboscore_msgbackend::WithMsgNative,
ctx: &mut PrimitiveContext,
writer: impl AsyncFnOnce(&mut ProtectedMessage) -> R,
) -> Result<(raw::oscore_requestid_t, R), ProtectError> {
request
.async_with_msg_native(async |msg| {
let mut plaintext = MaybeUninit::uninit();
let mut request_data = MaybeUninit::uninit();
let prepare_ok = unsafe {
raw::oscore_prepare_request(
msg,
plaintext.as_mut_ptr(),
ctx.as_mut(),
request_data.as_mut_ptr(),
)
};
PrepareError::new(prepare_ok)?;
let plaintext = unsafe { plaintext.assume_init() };
let request_data = unsafe { request_data.assume_init() };
let mut plaintext = crate::ProtectedMessage::new(plaintext);
let user_carry = writer(&mut plaintext).await;
plaintext.flush();
let mut plaintext = plaintext.into_inner();
let mut returned_msg = MaybeUninit::uninit();
let finish_ok =
unsafe { raw::oscore_encrypt_message(&mut plaintext, returned_msg.as_mut_ptr()) };
FinishError::new(finish_ok)?;
Ok((request_data, user_carry))
})
.await
}
pub fn protect_request<R>(
request: impl liboscore_msgbackend::WithMsgNative,
ctx: &mut PrimitiveContext,
writer: impl FnOnce(&mut ProtectedMessage) -> R,
) -> Result<(raw::oscore_requestid_t, R), ProtectError> {
poll_once_expect_immediate(async_protect_request(request, ctx, async |m| writer(m)))
}
pub async fn async_unprotect_request<R>(
request: impl liboscore_msgbackend::WithMsgNative,
oscoreoption: OscoreOption<'_>, ctx: &mut PrimitiveContext,
reader: impl AsyncFnOnce(&ProtectedMessage) -> R,
) -> Result<(raw::oscore_requestid_t, R), UnprotectRequestError> {
request
.async_with_msg_native(async |nativemsg| {
let mut plaintext = MaybeUninit::uninit();
let mut request_data = MaybeUninit::uninit();
let decrypt_ok = unsafe {
raw::oscore_unprotect_request(
nativemsg,
plaintext.as_mut_ptr(),
&oscoreoption.into_inner(),
ctx.as_mut(),
request_data.as_mut_ptr(),
)
};
UnprotectRequestError::new(decrypt_ok)?;
let plaintext = unsafe { plaintext.assume_init() };
let request_data = unsafe { request_data.assume_init() };
let plaintext = ProtectedMessage::new(plaintext);
let user_data = reader(&plaintext).await;
unsafe { raw::oscore_release_unprotected(&mut plaintext.into_inner()) };
Ok((request_data, user_data))
})
.await
}
pub fn unprotect_request<R>(
request: impl liboscore_msgbackend::WithMsgNative,
oscoreoption: OscoreOption<'_>,
ctx: &mut PrimitiveContext,
reader: impl FnOnce(&ProtectedMessage) -> R,
) -> Result<(raw::oscore_requestid_t, R), UnprotectRequestError> {
poll_once_expect_immediate(async_unprotect_request(
request,
oscoreoption,
ctx,
async |m| reader(m),
))
}
pub async fn async_protect_response<R>(
response: impl liboscore_msgbackend::WithMsgNative,
ctx: &mut PrimitiveContext,
correlation: &mut raw::oscore_requestid_t,
writer: impl AsyncFnOnce(&mut ProtectedMessage) -> R,
) -> Result<R, ProtectError> {
response
.async_with_msg_native(async |nativemsg| {
let mut plaintext = MaybeUninit::uninit();
let prepare_ok = unsafe {
raw::oscore_prepare_response(
nativemsg,
plaintext.as_mut_ptr(),
ctx.as_mut(),
correlation,
)
};
PrepareError::new(prepare_ok)?;
let plaintext = unsafe { plaintext.assume_init() };
let mut plaintext = crate::ProtectedMessage::new(plaintext);
let user_data = writer(&mut plaintext).await;
plaintext.flush();
let mut plaintext = plaintext.into_inner();
let mut returned_msg = MaybeUninit::uninit();
let finish_ok =
unsafe { raw::oscore_encrypt_message(&mut plaintext, returned_msg.as_mut_ptr()) };
FinishError::new(finish_ok)?;
Ok(user_data)
})
.await
}
pub fn protect_response<R>(
response: impl liboscore_msgbackend::WithMsgNative,
ctx: &mut PrimitiveContext,
correlation: &mut raw::oscore_requestid_t,
writer: impl FnOnce(&mut ProtectedMessage) -> R,
) -> Result<R, ProtectError> {
poll_once_expect_immediate(async_protect_response(
response,
ctx,
correlation,
async |m| writer(m),
))
}
pub async fn async_unprotect_response<R>(
response: impl liboscore_msgbackend::WithMsgNative,
ctx: &mut PrimitiveContext,
oscoreoption: OscoreOption<'_>,
correlation: &mut raw::oscore_requestid_t,
reader: impl AsyncFnOnce(&ProtectedMessage) -> R,
) -> Result<R, UnprotectResponseError> {
response
.async_with_msg_native(async |nativemsg| {
let mut plaintext = MaybeUninit::uninit();
let decrypt_ok = unsafe {
raw::oscore_unprotect_response(
nativemsg,
plaintext.as_mut_ptr(),
&oscoreoption.into_inner(),
ctx.as_mut(),
correlation,
)
};
UnprotectResponseError::new(decrypt_ok)?;
let plaintext = unsafe { plaintext.assume_init() };
let plaintext = ProtectedMessage::new(plaintext);
let user_data = reader(&plaintext).await;
unsafe { raw::oscore_release_unprotected(&mut plaintext.into_inner()) };
Ok(user_data)
})
.await
}
pub fn unprotect_response<R>(
response: impl liboscore_msgbackend::WithMsgNative,
ctx: &mut PrimitiveContext,
oscoreoption: OscoreOption<'_>,
correlation: &mut raw::oscore_requestid_t,
reader: impl FnOnce(&ProtectedMessage) -> R,
) -> Result<R, UnprotectResponseError> {
poll_once_expect_immediate(async_unprotect_response(
response,
ctx,
oscoreoption,
correlation,
async |m| reader(m),
))
}