1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
//! Friendly bindings to liboqs
//!
//! See the [`kem::Kem`](crate::kem::Kem) and [`sig::Sig`](crate::sig::Sig) structs for how to use this crate.
//!
//! # Example: Some signed KEX
//!
//! This protocol has no replay protection!
//! ```
//! use oqs::*;
//! # #[cfg(all(feature = "dilithium2", feature = "kyber"))]
//! fn main() -> Result<()> {
//!     oqs::init(); // Important: initialize liboqs
//!     let sigalg = sig::Sig::new(sig::Algorithm::Dilithium2)?;
//!     let kemalg = kem::Kem::new(kem::Algorithm::Kyber512)?;
//!     // A's long-term secrets
//!     let (a_sig_pk, a_sig_sk) = sigalg.keypair()?;
//!     // B's long-term secrets
//!     let (b_sig_pk, b_sig_sk) = sigalg.keypair()?;
//!
//!     // assumption: A has (a_sig_sk, a_sig_pk, b_sig_pk)
//!     // assumption: B has (b_sig_sk, b_sig_pk, a_sig_pk)
//!
//!     // A -> B: kem_pk, signature
//!     let (kem_pk, kem_sk) = kemalg.keypair()?;
//!     let signature = sigalg.sign(kem_pk.as_ref(), &a_sig_sk)?;
//!
//!     // B -> A: kem_ct, signature
//!     sigalg.verify(kem_pk.as_ref(), &signature, &a_sig_pk)?;
//!     let (kem_ct, b_kem_ss) = kemalg.encapsulate(&kem_pk)?;
//!     let signature = sigalg.sign(kem_ct.as_ref(), &b_sig_sk)?;
//!
//!     // A verifies, decapsulates, now both have kem_ss
//!     sigalg.verify(kem_ct.as_ref(), &signature, &b_sig_pk)?;
//!     let a_kem_ss = kemalg.decapsulate(&kem_sk, &kem_ct)?;
//!     assert_eq!(a_kem_ss, b_kem_ss);
//!
//!     Ok(())
//! }
//! # #[cfg(not(all(feature = "dilithium2", feature = "kyber")))]
//! # fn main() {}
//! ```
// needs to be imported to be made available
extern crate alloc;

use ffi::common::OQS_STATUS;

/// Access the OQS ffi through this crate.
pub use oqs_sys as ffi;

mod macros;

/// Initialize liboqs
///
/// Make sure to call this before you use any of the functions.
///
/// When the ``std`` feature is enabled, this method is thread-safe
/// and can be called more than once.
#[cfg(feature = "std")]
pub fn init() {
    use std::sync::Once;
    static INIT: Once = Once::new();
    INIT.call_once(|| {
        unsafe { ffi::common::OQS_init() };
    });
}

/// Initialize liboqs
///
/// Needs to be called before you use any of the functions.
///
/// This ``no_std`` variant is not thread-safe.
#[cfg(not(feature = "std"))]
pub fn init() {
    unsafe { ffi::common::OQS_init() };
}

#[derive(Debug)]
#[non_exhaustive]
/// Possible errors
pub enum Error {
    /// Indicates an algorithm has been disabled
    AlgorithmDisabled,
    /// Generic error
    Error,
    /// Error occurred in OpenSSL functions external to liboqs
    #[allow(clippy::upper_case_acronyms)]
    ErrorExternalOpenSSL,
    /// Invalid length of a public object
    InvalidLength,
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}

/// Result type for operations that may fail
pub type Result<T> = core::result::Result<T, Error>;

impl core::fmt::Display for Error {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Error::AlgorithmDisabled => write!(f, "OQS Error: Algorithm has been disabled"),
            Error::ErrorExternalOpenSSL => write!(f, "OQS error: OpenSSL call failed"),
            _ => write!(f, "OQS Error!"),
        }
    }
}

/// Convert an OQS_STATUS to the Result type.
fn status_to_result(status: OQS_STATUS) -> Result<()> {
    match status {
        OQS_STATUS::OQS_SUCCESS => Ok(()),
        OQS_STATUS::OQS_ERROR => Err(Error::Error),
        OQS_STATUS::OQS_EXTERNAL_LIB_ERROR_OPENSSL => Err(Error::ErrorExternalOpenSSL),
    }
}

pub mod kem;
pub mod sig;