mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 22:43:26 -08:00
Initial implementation of the Rosenpass tool, implemented by @koraa. Includes contributions and some lints from @wucke13. Co-authored-by: wucke13 <wucke13@gmail.com>
177 lines
7.6 KiB
Rust
177 lines
7.6 KiB
Rust
//! This module contains Traits and implementations for Key Encapsulation
|
|
//! Mechanisms (KEM). KEMs are the interface provided by almost all post-quantum
|
|
//! secure key exchange mechanisms.
|
|
//!
|
|
//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting
|
|
//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during
|
|
//!
|
|
//! encapsulation.
|
|
//! The [KEM] Trait describes the basic API offered by a Key Encapsulation
|
|
//! Mechanism. Two implementations for it are provided, [SKEM] and [EKEM].
|
|
|
|
use crate::{RosenpassError, RosenpassMaybeError};
|
|
|
|
/// Key Encapsulation Mechanism
|
|
///
|
|
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
|
/// decapsulation.
|
|
pub trait KEM {
|
|
/// Secrete Key length
|
|
const SK_LEN: usize;
|
|
/// Public Key length
|
|
const PK_LEN: usize;
|
|
/// Ciphertext length
|
|
const CT_LEN: usize;
|
|
/// Shared Secret length
|
|
const SHK_LEN: usize;
|
|
|
|
/// Generate a keypair consisting of secret key (`sk`) and public key (`pk`)
|
|
///
|
|
/// `keygen() -> sk, pk`
|
|
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError>;
|
|
|
|
/// From a public key (`pk`), generate a shared key (`shk`, for local use)
|
|
/// and a cipher text (`ct`, to be sent to the owner of the `pk`).
|
|
///
|
|
/// `encaps(pk) -> shk, ct`
|
|
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError>;
|
|
|
|
/// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key
|
|
/// (`shk`)
|
|
///
|
|
/// `decaps(sk, ct) -> shk`
|
|
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError>;
|
|
}
|
|
|
|
/// A KEM that is secure against Chosen Ciphertext Attacks (CCA).
|
|
/// In the context of rosenpass this is used for static keys.
|
|
/// Uses [Classic McEliece](https://classic.mceliece.org/) 460896 from liboqs.
|
|
///
|
|
/// Classic McEliece is chosen because of its high security margin and its small
|
|
/// ciphertexts. The public keys are humongous, but (being static keys) the are never transmitted over
|
|
/// the wire so this is not a big problem.
|
|
pub struct SKEM;
|
|
|
|
/// # Safety
|
|
///
|
|
/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte
|
|
/// slices only identified using raw pointers. It must be ensured that the raw
|
|
/// pointers point into byte slices of sufficient length, to avoid UB through
|
|
/// overwriting of arbitrary data. This is checked in the following code before
|
|
/// the unsafe calls, and an early return with an Err occurs if the byte slice
|
|
/// size does not match the required size.
|
|
///
|
|
/// __Note__: This requirement is stricter than necessary, it would suffice
|
|
/// to only check that the buffers are big enough, allowing them to be even
|
|
/// bigger. However, from a correctness point of view it does not make sense to
|
|
/// allow bigger buffers.
|
|
impl KEM for SKEM {
|
|
const SK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_secret_key as usize;
|
|
const PK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_public_key as usize;
|
|
const CT_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_ciphertext as usize;
|
|
const SHK_LEN: usize =
|
|
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_shared_secret as usize;
|
|
|
|
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> {
|
|
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
|
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
|
unsafe {
|
|
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_keypair(pk.as_mut_ptr(), sk.as_mut_ptr())
|
|
.to_rg_error()
|
|
}
|
|
}
|
|
|
|
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> {
|
|
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
|
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
|
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
|
unsafe {
|
|
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_encaps(
|
|
ct.as_mut_ptr(),
|
|
shk.as_mut_ptr(),
|
|
pk.as_ptr(),
|
|
)
|
|
.to_rg_error()
|
|
}
|
|
}
|
|
|
|
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> {
|
|
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
|
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
|
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
|
unsafe {
|
|
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_decaps(
|
|
shk.as_mut_ptr(),
|
|
ct.as_ptr(),
|
|
sk.as_ptr(),
|
|
)
|
|
.to_rg_error()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Implements a KEM that is secure against Chosen Plaintext Attacks (CPA).
|
|
/// In the context of rosenpass this is used for ephemeral keys.
|
|
/// Currently the implementation uses
|
|
/// [Kyber 512](https://openquantumsafe.org/liboqs/algorithms/kem/kyber) from liboqs.
|
|
///
|
|
/// This is being used for ephemeral keys; since these are use-once the first post quantum
|
|
/// wireguard paper claimed that CPA security would be sufficient. Nonetheless we choose kyber
|
|
/// which provides CCA security since there are no publicly vetted KEMs out there which provide
|
|
/// only CPA security.
|
|
pub struct EKEM;
|
|
|
|
/// # Safety
|
|
///
|
|
/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte
|
|
/// slices only identified using raw pointers. It must be ensured that the raw
|
|
/// pointers point into byte slices of sufficient length, to avoid UB through
|
|
/// overwriting of arbitrary data. This is checked in the following code before
|
|
/// the unsafe calls, and an early return with an Err occurs if the byte slice
|
|
/// size does not match the required size.
|
|
///
|
|
/// __Note__: This requirement is stricter than necessary, it would suffice
|
|
/// to only check that the buffers are big enough, allowing them to be even
|
|
/// bigger. However, from a correctness point of view it does not make sense to
|
|
/// allow bigger buffers.
|
|
impl KEM for EKEM {
|
|
const SK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_secret_key as usize;
|
|
const PK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_public_key as usize;
|
|
const CT_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_ciphertext as usize;
|
|
const SHK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_shared_secret as usize;
|
|
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> {
|
|
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
|
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
|
unsafe {
|
|
oqs_sys::kem::OQS_KEM_kyber_512_keypair(pk.as_mut_ptr(), sk.as_mut_ptr())
|
|
.to_rg_error()
|
|
}
|
|
}
|
|
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> {
|
|
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
|
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
|
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
|
unsafe {
|
|
oqs_sys::kem::OQS_KEM_kyber_512_encaps(
|
|
ct.as_mut_ptr(),
|
|
shk.as_mut_ptr(),
|
|
pk.as_ptr(),
|
|
)
|
|
.to_rg_error()
|
|
}
|
|
}
|
|
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> {
|
|
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
|
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
|
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
|
unsafe {
|
|
oqs_sys::kem::OQS_KEM_kyber_512_decaps(
|
|
shk.as_mut_ptr(),
|
|
ct.as_ptr(),
|
|
sk.as_ptr(),
|
|
)
|
|
.to_rg_error()
|
|
}
|
|
}
|
|
}
|