Skip to main content

KWing

Struct KWing 

Source
pub struct KWing { /* private fields */ }
Available on crate feature kem only.
Expand description

A stateful, high-throughput KWing key holder for decapsulation.

KWing pre-computes and heap-caches all three component secret keys and the composite encapsulation key at construction time. Subsequent calls to decapsulate reuse the cached material without any additional key-derivation overhead.

§Key Sizes

ConstantBytesLayout
ENCAPSULATION_KEY_SIZE23,120X25519(32) ‖ ML-KEM-1024(1568) ‖ FrodoKEM-1344(21520)
CIPHERTEXT_SIZE23,328X25519 eph(32) ‖ Salt(32) ‖ ML-KEM CT(1568) ‖ FrodoKEM CT(21696)

§Security Note

The encapsulation key (public key) returned by get_pub_key is safe to distribute freely. The underlying secret key material stored in The underlying X25519 and ML-KEM secret material stored in this struct is wrapped in zeroize::Zeroizing. FrodoKEM material depends on standard process memory cleanup.

§Example

use b_wing::KWing;

let secret_seed = [0u8; 128]; // use a real CSPRNG in production
let kwing = KWing::from_seed(&secret_seed).unwrap();

// The public encapsulation key can be shared with any sender.
let ek = kwing.get_pub_key();
assert_eq!(ek.len(), KWing::ENCAPSULATION_KEY_SIZE);

Implementations§

Source§

impl KWing

Source

pub const ENCAPSULATION_KEY_SIZE: usize = 23120

Byte length of the composite encapsulation (public) key: 23,120 bytes.

Memory layout:

[ X25519 pub (32) | ML-KEM-1024 ek (1568) | FrodoKEM-1344 pk (21520) ]
Source

pub const CIPHERTEXT_SIZE: usize = 23328

Byte length of the composite ciphertext: 23,328 bytes.

Memory layout:

[ X25519 eph pub (32) | Salt (32) | ML-KEM-1024 ct (1568) | FrodoKEM-1344 ct (21696) ]
Source

pub fn from_seed(secret_seed: &[u8; 128]) -> Result<Self, Error>

Expands a 128-byte secret seed into a fully initialized KWing key holder.

The seed is partitioned deterministically as follows:

BytesUsage
[0..32]X25519 static secret
[32..64]ML-KEM-1024 keygen parameter d
[64..96]ML-KEM-1024 keygen parameter z
[96..128]FrodoKEM-1344 keygen seed (fed into ChaCha20)
§Security Requirements
  • secret_seed must be generated by a cryptographically secure random number generator (CSPRNG) such as getrandom.
  • Never reuse the same seed for different recipients or sessions.
  • The seed should be treated with the same care as a private key.
§Errors

Returns Error::InvalidFormat if an internal slice conversion fails (this should be impossible given a correctly-sized input).

§Example
use b_wing::KWing;

let mut seed = [0u8; 128];
getrandom::fill(&mut seed).expect("CSPRNG failed");
let kwing = KWing::from_seed(&seed).expect("key generation failed");
Source

pub fn get_pub_key(&self) -> &[u8]

Returns a reference to the cached composite encapsulation key.

The returned slice is ENCAPSULATION_KEY_SIZE bytes long and is safe to distribute publicly. Pass it to encapsulate on the sender’s side.

Source

pub fn encapsulate( encaps_seed: &[u8; 128], ek: &[u8], ) -> Result<(Vec<u8>, [u8; 64]), Error>

Encapsulates a fresh shared secret against the recipient’s composite public key.

This is the sender-side operation. It runs all three component KEMs deterministically from encaps_seed and combines their outputs into a single composite ciphertext and a 64-byte OKM.

§Seed Layout
BytesUsage
[0..32]X25519 ephemeral secret
[32..64]ML-KEM-1024 randomness m
[64..96]FrodoKEM encapsulation randomness (ChaCha20 seed)
[96..128]HKDF salt (transmitted in the ciphertext)
§Security Requirements
  • encaps_seed must be freshly generated from a CSPRNG for every encapsulation. Reusing the seed against the same recipient leaks the X25519 secret key.
§Errors
VariantCause
Error::InvalidFormatek is not exactly ENCAPSULATION_KEY_SIZE bytes
Error::LowEntropyKeyX25519 DH output is a low-order (all-zero) point
Error::EncapsulateErrorAn underlying KEM primitive failed
§Example
use b_wing::KWing;

let mut encaps_seed = [0u8; 128];
getrandom::fill(&mut encaps_seed).expect("CSPRNG failed");

let (ciphertext, shared_secret) = KWing::encapsulate(&encaps_seed, ek).unwrap();
assert_eq!(ciphertext.len(), KWing::CIPHERTEXT_SIZE);
assert_eq!(shared_secret.len(), 64);
Source

pub fn decapsulate(&self, ct: &[u8]) -> Result<[u8; 64], Error>

Decapsulates a composite ciphertext to recover the 64-byte Output Keying Material.

This is the recipient-side operation. It parses the composite ciphertext, runs all three component decapsulations using the cached secret keys, and recomputes the HKDF transcript to produce the OKM.

The OKM is cryptographically bound to the ciphertext and to this specific KWing instance, so it will not match any other recipient or ciphertext.

§Errors
VariantCause
Error::InvalidFormatct is not exactly CIPHERTEXT_SIZE bytes
Error::LowEntropyKeyX25519 DH output is a low-order (all-zero) point
Error::DecapsulateErrorAn underlying KEM primitive failed
§Example
use b_wing::KWing;

let okm = kwing.decapsulate(&ct).unwrap();
assert_eq!(okm.len(), 64);
// Derive a 32-byte AES-256 key and 32-byte MAC key from the OKM:
let aes_key = &okm[..32];
let mac_key = &okm[32..];

Auto Trait Implementations§

§

impl Freeze for KWing

§

impl RefUnwindSafe for KWing

§

impl Send for KWing

§

impl Sync for KWing

§

impl Unpin for KWing

§

impl UnsafeUnpin for KWing

§

impl UnwindSafe for KWing

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V