fips203-ffi-0.2.1/.cargo_vcs_info.json0000644000000001410000000000100127600ustar { "git": { "sha1": "12c06611fc6a44d3f7cd23a0592ae6a6851dc343" }, "path_in_vcs": "ffi" }fips203-ffi-0.2.1/Cargo.toml0000644000000021200000000000100107550ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "fips203-ffi" version = "0.2.1" authors = ["Daniel Kahn Gillmor "] description = "C shared library exposing FIPS 203 (draft): Module-Lattice-Based Key-Encapsulation Mechanism" documentation = "https://docs.rs/fips203" readme = "README.md" keywords = [ "FIPS", "203", "lattice", "kem", "ml", ] categories = ["cryptography"] license = "MIT OR Apache-2.0" repository = "https://github.com/integritychain/fips203" [lib] name = "fips203" crate-type = [ "staticlib", "cdylib", ] bench = false [dependencies.fips203] version = "0.2.1" fips203-ffi-0.2.1/Cargo.toml.orig000064400000000000000000000011221046102023000144370ustar 00000000000000[package] name = "fips203-ffi" version = "0.2.1" edition = "2021" license = "MIT OR Apache-2.0" description = "C shared library exposing FIPS 203 (draft): Module-Lattice-Based Key-Encapsulation Mechanism" authors = ["Daniel Kahn Gillmor "] documentation = "https://docs.rs/fips203" categories = ["cryptography"] repository = "https://github.com/integritychain/fips203" keywords = ["FIPS", "203", "lattice", "kem", "ml"] rust-version = "1.70" [lib] crate-type = ["staticlib", "cdylib"] bench = false name = "fips203" [dependencies.fips203] path = ".." version = "0.2.1" fips203-ffi-0.2.1/README.md000064400000000000000000000024301046102023000130320ustar 00000000000000# C Shared Object for ML-KEM This crate provides a shared object (dynamically-linked library) using standard C FFI ABI that provides a functional implementation of ML-KEM. The goals of this implementation are: - simplicity - correctness - caller deals only with serialized objects - no library-specific memory management (caller manages all objects) - no internal state held by the library between calls - minimal symbol visibility - stable API/ABI security-related goals: - constant-time operations - clean library RAM (objects should be zeroed out of any library-allocated memory before function exit) non-goals are: - speed - efficiency - size # Outstanding work - better internal error handling - testing! - reduce symbol visibility in shared object # Paths considered but discarded - Autogenerate stable C headers (e.g. with cbindgen); manually-crafted headers are probably fine, given the simplicity of the API/ABI # Quick start ~~~ $ cd ffi # this directory $ cargo build $ (cd tests && make) $ python3 >>> from fips203 import ML_KEM_512 >>> >>> (encapsulation_key, decapsulation_key) = ML_KEM_512.keygen() >>> (ciphertext, shared_secret_1) = encapsulation_key.encaps() >>> shared_secret_2 = decapsulation_key.decaps(ciphertext) >>> assert(shared_secret_1 == shared_secret_2) ~~~ fips203-ffi-0.2.1/build.rs000064400000000000000000000002301046102023000132140ustar 00000000000000fn main() { println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libfips203.so.{}", std::env::var("CARGO_PKG_VERSION_MAJOR").unwrap()); } fips203-ffi-0.2.1/fips203.h000064400000000000000000000064611046102023000131220ustar 00000000000000#ifndef __FIPS203_H__ #define __FIPS203_H__ /* Minimalist ML-KEM C interface Author: Daniel Kahn Gillmor Memory allocation and tracking are entirely the job of the caller. The shared object backing this interface has no internal state between calls, and should be completely reentrant. These functions return true on success, false on error. */ #include typedef uint8_t ml_kem_err; const ml_kem_err ML_KEM_OK = 0; const ml_kem_err ML_KEM_NULL_PTR_ERROR = 1; const ml_kem_err ML_KEM_SERIALIZATION_ERROR = 2; const ml_kem_err ML_KEM_DESERIALIZATION_ERROR = 3; const ml_kem_err ML_KEM_KEYGEN_ERROR = 4; const ml_kem_err ML_KEM_ENCAPSULATION_ERROR = 5; const ml_kem_err ML_KEM_DECAPSULATION_ERROR = 6; typedef struct ml_kem_shared_secret { uint8_t data[32]; } ml_kem_shared_secret; typedef struct ml_kem_512_encaps_key { uint8_t data[800]; } ml_kem_512_encaps_key; typedef struct ml_kem_512_decaps_key { uint8_t data[1632]; } ml_kem_512_decaps_key; typedef struct ml_kem_512_ciphertext { uint8_t data[768]; } ml_kem_512_ciphertext; typedef struct ml_kem_768_encaps_key { uint8_t data[1184]; } ml_kem_768_encaps_key; typedef struct ml_kem_768_decaps_key { uint8_t data[2400]; } ml_kem_768_decaps_key; typedef struct ml_kem_768_ciphertext { uint8_t data[1088]; } ml_kem_768_ciphertext; typedef struct ml_kem_1024_encaps_key { uint8_t data[1568]; } ml_kem_1024_encaps_key; typedef struct ml_kem_1024_decaps_key { uint8_t data[3168]; } ml_kem_1024_decaps_key; typedef struct ml_kem_1024_ciphertext { uint8_t data[1568]; } ml_kem_1024_ciphertext; #ifdef __cplusplus extern "C" { #endif ml_kem_err ml_kem_512_keygen(ml_kem_512_encaps_key *encaps_out, ml_kem_512_decaps_key *decaps_out); ml_kem_err ml_kem_512_encaps(const ml_kem_512_encaps_key *encaps, ml_kem_512_ciphertext *ciphertext_out, ml_kem_shared_secret *shared_secret_out); ml_kem_err ml_kem_512_decaps(const ml_kem_512_decaps_key *decaps, const ml_kem_512_ciphertext *ciphertext, ml_kem_shared_secret *shared_secret_out); ml_kem_err ml_kem_768_keygen(ml_kem_768_encaps_key *encaps_out, ml_kem_768_decaps_key *decaps_out); ml_kem_err ml_kem_768_encaps(const ml_kem_768_encaps_key *encaps, ml_kem_768_ciphertext *ciphertext_out, ml_kem_shared_secret *shared_secret_out); ml_kem_err ml_kem_768_decaps(const ml_kem_768_decaps_key *decaps, const ml_kem_768_ciphertext *ciphertext, ml_kem_shared_secret *shared_secret_out); ml_kem_err ml_kem_1024_keygen(ml_kem_1024_encaps_key *encaps_out, ml_kem_1024_decaps_key *decaps_out); ml_kem_err ml_kem_1024_encaps(const ml_kem_1024_encaps_key *encaps, ml_kem_1024_ciphertext *ciphertext_out, ml_kem_shared_secret *shared_secret_out); ml_kem_err ml_kem_1024_decaps(const ml_kem_1024_decaps_key *decaps, const ml_kem_1024_ciphertext *ciphertext, ml_kem_shared_secret *shared_secret_out); #ifdef __cplusplus } // extern "C" #endif #endif // __FIPS203_H__ fips203-ffi-0.2.1/fips203.py000064400000000000000000000266441046102023000133300ustar 00000000000000'''FIPS 203 (ML-KEM) Asymmetric Post-Quantum Cryptography This Python module provides an implementation of FIPS 203, the Module-Lattice-based Key Encapsulation Mechanism Standard. ## Example The following example shows using the standard ML-KEM algorithm to produce identical 32-byte shared secrets: ``` from fips203 import ML_KEM_512 (encapsulation_key, decapsulation_key) = ML_KEM_512.keygen() (ciphertext, shared_secret_1) = encapsulation_key.encaps() shared_secret_2 = decapsulation_key.decaps(ciphertext) assert(shared_secret_1 == shared_secret_2) ``` Encapsulation keys, decapsulation keys, and ciphertexts can all be serialized by accessing them as `bytes`, and deserialized by initializing them with the appropriate size bytes object. A serialization example: ``` from fips203 import ML_KEM_768 (ek,dk) = ML_KEM_768.keygen() with open('encapskey.bin', 'wb') as f: f.write(bytes(ek)) with open('decapskey.bin', 'wb') as f: f.write(bytes(dk)) ``` A deserialization example, followed by use: ``` import fips203 with open('encapskey.bin', 'b') as f: ekdata = f.read() ek = fips203.EncapsulationKey(ekdata) (ct, ss) = ek.Encaps() ``` The expected sizes (in bytes) of the different objects in each parameter set can be accessed with `EK_SIZE`, `DK_SIZE`, `CT_SIZE`, and `SS_SIZE`: ``` from fips203 import ML_KEM_768 print(f"ML-KEM-768 Ciphertext size (in bytes) is {ML_KEM_768.CT_SIZE}") ``` ## Implementation Notes This is a wrapper around libfips203, built from the Rust fips203-ffi crate. If that library is not installed in the expected path for libraries on your system, any attempt to use this module will fail. ## See Also - https://doi.org/10.6028/NIST.FIPS.203.ipd - https://github.com/integritychain/fips203 ''' __version__ = '0.1' __author__ = 'Daniel Kahn Gillmor ' __all__ = [ 'ML_KEM_512', 'ML_KEM_768', 'ML_KEM_1024', 'Ciphertext', 'EncapsulationKey', 'DecapsulationKey', ] import ctypes import ctypes.util import enum from typing import Tuple, Dict, Any, Union from abc import ABC class _SharedSecret(ctypes.Structure): _fields_ = [('data', ctypes.c_uint8 * 32)] class Err(enum.IntEnum): OK = 0 NULL_PTR_ERROR = 1 SERIALIZATION_ERROR = 2 DESERIALIZATION_ERROR = 3 KEYGEN_ERROR = 4 ENCAPSULATION_ERROR = 5 DECAPSULATION_ERROR = 6 class Ciphertext(): '''ML-KEM Ciphertext Serialize this object by asking for it as `bytes`. You can convert it to a 32-byte shared secret by passing it to the Decaps() function of the appropriate Decapsulation Key. ''' def __init__(self, data: Union[bytes, int]) -> None: '''Create ML-KEM Ciphertext from bytes (or strength level).''' if isinstance(data, bytes): self._strength = _ML_KEM.strength_from_length('CT_SIZE', len(data)) elif isinstance(data, int): self._strength = data else: raise Exception("Initialize ML-KEM Ciphertext object with " f"bytes or a strength level, not {type(data)}") self._ffi = _ML_KEM.strength(self._strength) self._ct = self._ffi['Ciphertext']() if isinstance(data, bytes): self._set(data) def __repr__(self) -> str: return f'' def __bytes__(self) -> bytes: return bytes(self._ct.data) def _set(self, data: bytes) -> None: if len(data) != len(self._ct.data): raise ValueError(f"Expected {len(self._ct.data)} bytes, " f"got {len(data)}") for i in range(len(data)): self._ct.data[i] = data[i] class EncapsulationKey(): '''ML-KEM Encapsulation Key Serialize this object by asking for it as `bytes`. Produce a Ciphertext and a 32-byte shared secret by invoking Encaps() on it. ''' def __init__(self, data: Union[bytes, int]) -> None: '''Create ML-KEM Encapsulation Key from bytes (or strength level).''' if isinstance(data, bytes): self._strength = _ML_KEM.strength_from_length('EK_SIZE', len(data)) elif isinstance(data, int): self._strength = data else: raise Exception("Initialize ML-KEM Encapsulation Key with " f"bytes or a strength level, not {type(data)}") self._ffi = _ML_KEM.strength(self._strength) self._ek = self._ffi['EncapsKey']() if isinstance(data, bytes): self._set(data) def __repr__(self) -> str: return f'' def __bytes__(self) -> bytes: return bytes(self._ek.data) def _set(self, data: bytes) -> None: if len(data) != len(self._ek.data): raise ValueError(f"Expected {len(self._ek.data)} bytes, " f"got {len(data)}") for i in range(len(data)): self._ek.data[i] = data[i] def encaps(self) -> Tuple[Ciphertext, bytes]: '''Produce a new Ciphertext and corresponding 32-byte shared secret.''' ct = Ciphertext(self._strength) ss = _SharedSecret() ret = Err(self._ffi['encaps'](ctypes.byref(self._ek), ctypes.byref(ct._ct), ctypes.byref(ss))) if ret is not Err.OK: raise Exception(f"ml_kem_{self._strength}_encaps() " f"returned {ret} ({ret.name})") return (ct, bytes(ss.data)) class DecapsulationKey(): '''ML-KEM Decapsulation Key Serialize this object by asking for it as `bytes`. Produce a 32-byte shared secret from a Ciphertext by invoking Decaps() on it. ''' def __init__(self, data: Union[bytes, int]) -> None: '''Create ML-KEM Decapsulation Key from bytes (or strength level).''' if isinstance(data, bytes): self._strength = _ML_KEM.strength_from_length('DK_SIZE', len(data)) elif isinstance(data, int): self._strength = data else: raise Exception("Initialize ML-KEM Encapsulation Key with bytes " f"or a strength level, not {type(data)}") self._ffi = _ML_KEM.strength(self._strength) self._dk = self._ffi['DecapsKey']() if isinstance(data, bytes): self._set(data) def __repr__(self) -> str: return f'' def __bytes__(self) -> bytes: return bytes(self._dk.data) def _set(self, data: bytes) -> None: if len(data) != len(self._dk.data): raise ValueError(f"Expected {len(self._dk.data)} bytes, " f"got {len(data)}") for i in range(len(data)): self._dk.data[i] = data[i] def decaps(self, ct: Ciphertext) -> bytes: '''Get 32-byte shared secret corresponding to the given Ciphertext.''' if self._strength != ct._strength: raise Exception(f"Cannot decapsulate {ct} with {self}") ss = _SharedSecret() ret = Err(self._ffi['decaps'](ctypes.byref(self._dk), ctypes.byref(ct._ct), ctypes.byref(ss))) if ret is not Err.OK: raise Exception(f"ml_kem_{self._strength}_decaps() " f"returned {ret} ({ret.name})") return bytes(ss.data) class _ML_KEM(): params: Dict[int, Dict[str, int]] = { 512: { 'EK_SIZE': 800, 'DK_SIZE': 1632, 'CT_SIZE': 768, }, 768: { 'EK_SIZE': 1184, 'DK_SIZE': 2400, 'CT_SIZE': 1088, }, 1024: { 'EK_SIZE': 1568, 'DK_SIZE': 3168, 'CT_SIZE': 1568, }, } lib = ctypes.CDLL(ctypes.util.find_library('fips203')) if not hasattr(lib, 'ml_kem_512_keygen'): lib = ctypes.CDLL("../target/debug/libfips203.so") # use Any below because i don't know how to specify the type of the FuncPtr ffi: Dict[int, Dict[str, Any]] = {} @classmethod def strength(cls, level: int) -> Dict[str, Any]: if level not in cls.ffi: class _EncapsKey(ctypes.Structure): _fields_ = [('data', ctypes.c_uint8 * cls.params[level]['EK_SIZE'])] class _DecapsKey(ctypes.Structure): _fields_ = [('data', ctypes.c_uint8 * cls.params[level]['DK_SIZE'])] class _Ciphertext(ctypes.Structure): _fields_ = [('data', ctypes.c_uint8 * cls.params[level]['CT_SIZE'])] ffi: Dict[str, Any] = {} ffi['keygen'] = cls.lib[f'ml_kem_{level}_keygen'] ffi['keygen'].argtypes = [ctypes.POINTER(_EncapsKey), ctypes.POINTER(_DecapsKey)] ffi['keygen'].restype = ctypes.c_uint8 ffi['encaps'] = cls.lib[f'ml_kem_{level}_encaps'] ffi['encaps'].argtypes = [ctypes.POINTER(_EncapsKey), ctypes.POINTER(_Ciphertext), ctypes.POINTER(_SharedSecret)] ffi['encaps'].restype = ctypes.c_uint8 ffi['decaps'] = cls.lib[f'ml_kem_{level}_decaps'] ffi['decaps'].argtypes = [ctypes.POINTER(_DecapsKey), ctypes.POINTER(_Ciphertext), ctypes.POINTER(_SharedSecret)] ffi['decaps'].restype = ctypes.c_uint8 ffi['EncapsKey'] = _EncapsKey ffi['DecapsKey'] = _DecapsKey ffi['Ciphertext'] = _Ciphertext cls.ffi[level] = ffi return cls.ffi[level] @classmethod def strength_from_length(cls, object_type: str, object_len: int) -> int: for strength in cls.params: if cls.params[strength][object_type] == object_len: return strength raise Exception(f"No ML-KEM parameter set has {object_type} " f"of {object_len} bytes") @classmethod def _keygen(cls, strength: int) -> Tuple[EncapsulationKey, DecapsulationKey]: ek = EncapsulationKey(strength) dk = DecapsulationKey(strength) ret = Err(cls.strength(strength)['keygen'](ctypes.byref(ek._ek), ctypes.byref(dk._dk))) if ret is not Err.OK: raise Exception(f"ml_kem_{strength}_keygen() returned " f"{ret} ({ret.name})") return (ek, dk) class ML_KEM(ABC): '''Abstract base class for all ML-KEM (FIPS 203) parameter sets.''' _strength: int EK_SIZE: int DK_SIZE: int CT_SIZE: int SS_SIZE: int = 32 @classmethod def keygen(cls) -> Tuple[EncapsulationKey, DecapsulationKey]: '''Generate a pair of Encapsulation and Decapsulation Keys.''' return _ML_KEM._keygen(cls._strength) class ML_KEM_512(ML_KEM): '''ML-KEM-512 (FIPS 203) Implementation.''' _strength: int = 512 EK_SIZE: int = 800 DK_SIZE: int = 1632 CT_SIZE: int = 768 class ML_KEM_768(ML_KEM): '''ML-KEM-768 (FIPS 203) Implementation.''' _strength: int = 768 EK_SIZE: int = 1184 DK_SIZE: int = 2400 CT_SIZE: int = 1088 class ML_KEM_1024(ML_KEM): '''ML-KEM-1024 (FIPS 203) Implementation.''' _strength: int = 1024 EK_SIZE: int = 1568 DK_SIZE: int = 3168 CT_SIZE: int = 1568 fips203-ffi-0.2.1/src/.rustfmt.toml000064400000000000000000000000361046102023000150210ustar 00000000000000fn_params_layout = "Vertical" fips203-ffi-0.2.1/src/lib.rs000064400000000000000000000172121046102023000134620ustar 00000000000000//use fips203; #[repr(C)] pub struct ml_kem_shared_secret { data: [u8; fips203::SSK_LEN], } pub const ML_KEM_OK: u8 = 0; pub const ML_KEM_NULL_PTR_ERROR: u8 = 1; pub const ML_KEM_SERIALIZATION_ERROR: u8 = 2; pub const ML_KEM_DESERIALIZATION_ERROR: u8 = 3; pub const ML_KEM_KEYGEN_ERROR: u8 = 4; pub const ML_KEM_ENCAPSULATION_ERROR: u8 = 5; pub const ML_KEM_DECAPSULATION_ERROR: u8 = 6; // ML-KEM-512 #[repr(C)] pub struct ml_kem_512_encaps_key { data: [u8; fips203::ml_kem_512::EK_LEN], } #[repr(C)] pub struct ml_kem_512_decaps_key { data: [u8; fips203::ml_kem_512::DK_LEN], } #[repr(C)] pub struct ml_kem_512_ciphertext { data: [u8; fips203::ml_kem_512::CT_LEN], } #[no_mangle] pub extern "C" fn ml_kem_512_keygen( encaps_out: Option<&mut ml_kem_512_encaps_key>, decaps_out: Option<&mut ml_kem_512_decaps_key>, ) -> u8 { use fips203::traits::{KeyGen, SerDes}; let (Some(encaps_out), Some(decaps_out)) = (encaps_out, decaps_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok((ek, dk)) = fips203::ml_kem_512::KG::try_keygen() else { return ML_KEM_KEYGEN_ERROR; }; encaps_out.data = ek.into_bytes(); decaps_out.data = dk.into_bytes(); ML_KEM_OK } #[no_mangle] pub extern "C" fn ml_kem_512_encaps( encaps: Option<&ml_kem_512_encaps_key>, ciphertext_out: Option<&mut ml_kem_512_ciphertext>, shared_secret_out: Option<&mut ml_kem_shared_secret>, ) -> u8 { use fips203::traits::{Encaps, SerDes}; let (Some(encaps), Some(ciphertext_out), Some(shared_secret_out)) = (encaps, ciphertext_out, shared_secret_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok(ek) = fips203::ml_kem_512::EncapsKey::try_from_bytes(encaps.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok((ssk, ct)) = ek.try_encaps() else { return ML_KEM_ENCAPSULATION_ERROR; }; shared_secret_out.data = ssk.into_bytes(); ciphertext_out.data = ct.into_bytes(); ML_KEM_OK } #[no_mangle] pub extern "C" fn ml_kem_512_decaps( decaps: Option<&ml_kem_512_decaps_key>, ciphertext: Option<&ml_kem_512_ciphertext>, shared_secret_out: Option<&mut ml_kem_shared_secret>, ) -> u8 { use fips203::traits::{Decaps, SerDes}; let (Some(decaps), Some(ciphertext), Some(shared_secret_out)) = (decaps, ciphertext, shared_secret_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok(dk) = fips203::ml_kem_512::DecapsKey::try_from_bytes(decaps.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok(ct) = fips203::ml_kem_512::CipherText::try_from_bytes(ciphertext.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok(ssk) = dk.try_decaps(&ct) else { return ML_KEM_DECAPSULATION_ERROR; }; shared_secret_out.data = ssk.into_bytes(); ML_KEM_OK } // ML-KEM-768 #[repr(C)] pub struct ml_kem_768_encaps_key { data: [u8; fips203::ml_kem_768::EK_LEN], } #[repr(C)] pub struct ml_kem_768_decaps_key { data: [u8; fips203::ml_kem_768::DK_LEN], } #[repr(C)] pub struct ml_kem_768_ciphertext { data: [u8; fips203::ml_kem_768::CT_LEN], } #[no_mangle] pub extern "C" fn ml_kem_768_keygen( encaps_out: Option<&mut ml_kem_768_encaps_key>, decaps_out: Option<&mut ml_kem_768_decaps_key>, ) -> u8 { use fips203::traits::{KeyGen, SerDes}; let (Some(encaps_out), Some(decaps_out)) = (encaps_out, decaps_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok((ek, dk)) = fips203::ml_kem_768::KG::try_keygen() else { return ML_KEM_KEYGEN_ERROR; }; encaps_out.data = ek.into_bytes(); decaps_out.data = dk.into_bytes(); ML_KEM_OK } #[no_mangle] pub extern "C" fn ml_kem_768_encaps( encaps: Option<&ml_kem_768_encaps_key>, ciphertext_out: Option<&mut ml_kem_768_ciphertext>, shared_secret_out: Option<&mut ml_kem_shared_secret>, ) -> u8 { use fips203::traits::{Encaps, SerDes}; let (Some(encaps), Some(ciphertext_out), Some(shared_secret_out)) = (encaps, ciphertext_out, shared_secret_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok(ek) = fips203::ml_kem_768::EncapsKey::try_from_bytes(encaps.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok((ssk, ct)) = ek.try_encaps() else { return ML_KEM_ENCAPSULATION_ERROR; }; shared_secret_out.data = ssk.into_bytes(); ciphertext_out.data = ct.into_bytes(); ML_KEM_OK } #[no_mangle] pub extern "C" fn ml_kem_768_decaps( decaps: Option<&ml_kem_768_decaps_key>, ciphertext: Option<&ml_kem_768_ciphertext>, shared_secret_out: Option<&mut ml_kem_shared_secret>, ) -> u8 { use fips203::traits::{Decaps, SerDes}; let (Some(decaps), Some(ciphertext), Some(shared_secret_out)) = (decaps, ciphertext, shared_secret_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok(dk) = fips203::ml_kem_768::DecapsKey::try_from_bytes(decaps.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok(ct) = fips203::ml_kem_768::CipherText::try_from_bytes(ciphertext.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok(ssk) = dk.try_decaps(&ct) else { return ML_KEM_DECAPSULATION_ERROR; }; shared_secret_out.data = ssk.into_bytes(); ML_KEM_OK } // ML-KEM-1024 #[repr(C)] pub struct ml_kem_1024_encaps_key { data: [u8; fips203::ml_kem_1024::EK_LEN], } #[repr(C)] pub struct ml_kem_1024_decaps_key { data: [u8; fips203::ml_kem_1024::DK_LEN], } #[repr(C)] pub struct ml_kem_1024_ciphertext { data: [u8; fips203::ml_kem_1024::CT_LEN], } #[no_mangle] pub extern "C" fn ml_kem_1024_keygen( encaps_out: Option<&mut ml_kem_1024_encaps_key>, decaps_out: Option<&mut ml_kem_1024_decaps_key>, ) -> u8 { use fips203::traits::{KeyGen, SerDes}; let (Some(encaps_out), Some(decaps_out)) = (encaps_out, decaps_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok((ek, dk)) = fips203::ml_kem_1024::KG::try_keygen() else { return ML_KEM_KEYGEN_ERROR; }; encaps_out.data = ek.into_bytes(); decaps_out.data = dk.into_bytes(); ML_KEM_OK } #[no_mangle] pub extern "C" fn ml_kem_1024_encaps( encaps: Option<&ml_kem_1024_encaps_key>, ciphertext_out: Option<&mut ml_kem_1024_ciphertext>, shared_secret_out: Option<&mut ml_kem_shared_secret>, ) -> u8 { use fips203::traits::{Encaps, SerDes}; let (Some(encaps), Some(ciphertext_out), Some(shared_secret_out)) = (encaps, ciphertext_out, shared_secret_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok(ek) = fips203::ml_kem_1024::EncapsKey::try_from_bytes(encaps.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok((ssk, ct)) = ek.try_encaps() else { return ML_KEM_ENCAPSULATION_ERROR; }; shared_secret_out.data = ssk.into_bytes(); ciphertext_out.data = ct.into_bytes(); ML_KEM_OK } #[no_mangle] pub extern "C" fn ml_kem_1024_decaps( decaps: Option<&ml_kem_1024_decaps_key>, ciphertext: Option<&ml_kem_1024_ciphertext>, shared_secret_out: Option<&mut ml_kem_shared_secret>, ) -> u8 { use fips203::traits::{Decaps, SerDes}; let (Some(decaps), Some(ciphertext), Some(shared_secret_out)) = (decaps, ciphertext, shared_secret_out) else { return ML_KEM_NULL_PTR_ERROR; }; let Ok(dk) = fips203::ml_kem_1024::DecapsKey::try_from_bytes(decaps.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok(ct) = fips203::ml_kem_1024::CipherText::try_from_bytes(ciphertext.data) else { return ML_KEM_DESERIALIZATION_ERROR; }; let Ok(ssk) = dk.try_decaps(&ct) else { return ML_KEM_DECAPSULATION_ERROR; }; shared_secret_out.data = ssk.into_bytes(); ML_KEM_OK } fips203-ffi-0.2.1/tests/Makefile000064400000000000000000000013241046102023000143560ustar 00000000000000SO_LOCATION = ../../target/debug SIZES = 512 768 1024 FRAMES = encaps_key decaps_key ciphertext encaps decaps keygen # should derive SONAME somehow, e.g. from CARGO_PKG_VERSION_MAJOR SONAME = 0 BASELINES=$(foreach sz, $(SIZES), baseline-$(sz)) CHECKS=$(foreach sz, $(SIZES), runtest-$(sz)) check: $(CHECKS) $(SO_LOCATION)/libfips203.so.$(SONAME): $(SO_LOCATION)/libfips203.so ln -s $< $@ runtest-%: baseline-% $(SO_LOCATION)/libfips203.so.$(SONAME) LD_LIBRARY_PATH=$(SO_LOCATION) ./$< baseline-%: baseline.c ../fips203.h $(CC) -o $@ -g -D MLKEM_size=$* $(foreach v, $(FRAMES),-D MLKEM_$(v)=ml_kem_$*_$(v)) -Werror -Wall -pedantic -L $(SO_LOCATION) $< -Wall -lfips203 clean: rm -f $(BASELINES) .PHONY: clean check fips203-ffi-0.2.1/tests/baseline.c000064400000000000000000000064311046102023000146500ustar 00000000000000#include #include "../fips203.h" int main(int argc, const char **argv) { MLKEM_encaps_key encaps; MLKEM_decaps_key decaps; MLKEM_ciphertext ct; ml_kem_shared_secret ssk_a; ml_kem_shared_secret ssk_b; ml_kem_err err; MLKEM_encaps_key encaps_weird; MLKEM_decaps_key decaps_weird; if (MLKEM_keygen (&encaps, &decaps)) return 1; printf("Encaps (%d): ", MLKEM_size); for (int n = 0; n < sizeof(encaps.data); n++) printf ("%02x ", encaps.data[n]); printf("\n"); printf("Decaps (%d): ", MLKEM_size); for (int n = 0; n < sizeof(decaps.data); n++) printf ("%02x ", decaps.data[n]); printf("\n"); if (MLKEM_encaps (&encaps, &ct, &ssk_a)) return 2; printf("Ciphertext (%d): ", MLKEM_size); for (int n = 0; n < sizeof(ct.data); n++) printf ("%02x ", ct.data[n]); printf("\n"); printf("Shared Secret A: "); for (int n = 0; n < sizeof(ssk_a.data); n++) printf ("%02x ", ssk_a.data[n]); printf("\n"); if (MLKEM_decaps (&decaps, &ct, &ssk_b)) return 3; printf("Shared Secret B: "); for (int n = 0; n < sizeof(ssk_b.data); n++) printf ("%02x ", ssk_b.data[n]); printf("\n"); if (! MLKEM_keygen (&encaps, NULL)) { fprintf (stderr, "keygen should have failed with NULL decaps\n"); return 1; } if (! MLKEM_keygen (NULL, &decaps)) { fprintf (stderr, "keygen should have failed with NULL encaps\n"); return 1; } if (! MLKEM_keygen (NULL, NULL)) { fprintf (stderr, "keygen should have failed with NULL encaps and decaps\n"); return 1; } if (! MLKEM_encaps (&encaps, &ct, NULL)) { fprintf (stderr, "encaps should have failed with NULL shared_secret_out\n"); return 1; } if (! MLKEM_encaps (&encaps, NULL, &ssk_a)) { fprintf (stderr, "encaps should have failed with NULL ciphertext_out\n"); return 1; } if (! MLKEM_encaps (NULL, &ct, &ssk_a)) { fprintf (stderr, "encaps should have failed with NULL encaps_key\n"); return 1; } if (! MLKEM_encaps (NULL, NULL, NULL)) { fprintf (stderr, "encaps should have failed with NULL arguments\n"); return 1; } if (! MLKEM_decaps (&decaps, &ct, NULL)) { fprintf (stderr, "decaps should have failed with NULL shared_secret_out\n"); return 1; } if (! MLKEM_decaps (&decaps, NULL, &ssk_b)) { fprintf (stderr, "decaps should have failed with NULL ciphertext\n"); return 1; } if (! MLKEM_decaps (NULL, &ct, &ssk_b)) { fprintf (stderr, "decaps should have failed with NULL decaps_key\n"); return 1; } if (! MLKEM_decaps (NULL, NULL, NULL)) { fprintf (stderr, "decaps should have failed with NULL arguments\n"); return 1; } for (int i = 0; i < sizeof(encaps_weird.data); i++) encaps_weird.data[i] = 0xff; err = MLKEM_encaps (&encaps_weird, &ct, &ssk_a); if (err != ML_KEM_DESERIALIZATION_ERROR) { fprintf (stderr, "encaps against an encaps_key of all 0xff octets should have failed with deserialization error, got %d\n", err); return 1; } for (int i = 0; i < sizeof(decaps_weird.data); i++) decaps_weird.data[i] = 0xff; err = MLKEM_decaps (&decaps_weird, &ct, &ssk_a); if (err != ML_KEM_DESERIALIZATION_ERROR) { fprintf (stderr, "decaps against a tampered decaps_key should have failed with deserialization error, got %d\n", err); return 1; } return 0; }