Skip to main content

SWing

Struct SWing 

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

A stateful, high-throughput SWing key holder for signing.

SWing derives all three component signing keys from a single 160-byte master seed via from_seed and caches the composite verification key in heap-allocated memory. Subsequent calls to sign reuse the cached keys without any additional key-derivation overhead.

§Key Sizes

ConstantBytesComponents
VERIFICATION_KEY_SIZE2,688Ed25519(32) ‖ ML-DSA-87(2592) ‖ SLH-DSA(64)
SIGNATURE_SIZE54,547Ed25519(64) ‖ ML-DSA-87(4627) ‖ SLH-DSA(49856)

§Security Note

The verification key returned by get_pub_key is safe to distribute publicly. Secret key material is stored in fields that implement zeroize::ZeroizeOnDrop (via the underlying crates) and will be erased from memory when SWing is dropped.

§Example

use b_wing::SWing;

let master_seed = [0u8; 160]; // use a real CSPRNG in production
let swing = SWing::from_seed(&master_seed).unwrap();

let vk = swing.get_pub_key();
assert_eq!(vk.len(), SWing::VERIFICATION_KEY_SIZE);

Implementations§

Source§

impl SWing

Source

pub const VERIFICATION_KEY_SIZE: usize

Byte length of the composite verification key: 2,688 bytes.

Memory layout:

[ Ed25519 vk (32) | ML-DSA-87 vk (2592) | SLH-DSA-SHAKE256f vk (64) ]
Source

pub const SIGNATURE_SIZE: usize

Byte length of the composite signature: 54,547 bytes.

Memory layout:

[ Ed25519 sig (64) | ML-DSA-87 sig (4627) | SLH-DSA-SHAKE256f sig (49856) ]
Source

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

Expands a 160-byte master seed into a fully initialised SWing key holder.

The seed is partitioned according to MASTER_SEED_SIZE into the three component signing keys. All derivation is deterministic.

§Security Requirements
  • master_seed must be generated by a cryptographically secure random number generator (CSPRNG) such as getrandom.
  • Treat the master seed with the same care as a private key — it must never be logged, transmitted, or stored in plaintext.
  • If the seed is compromised, all signatures produced by this instance can be forged. Rotate by generating a new seed and re-publishing the corresponding verification key.
§Errors

Returns Error::InvalidFormat if an internal slice conversion fails (impossible for a correctly-sized input).

§Example
use b_wing::SWing;

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

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

Returns a reference to the cached composite verification key.

The returned slice is VERIFICATION_KEY_SIZE bytes long and is safe to distribute publicly. Pass it to verify on the verifier’s side.

Source

pub fn sign( &self, message: &[u8], context: &[u8], random_seed: impl Into<Zeroizing<[u8; 64]>>, ) -> Result<Vec<u8>, Error>

Signs message using all three underlying schemes and returns the composite signature.

The composite signature is the concatenation:

[ Ed25519 sig (64) | ML-DSA-87 sig (4627) | SLH-DSA-SHAKE256f sig (49856) ]

Each sub-signature is computed over a Sha3-512 digest that binds context and the composite verification key (see [hash_message]), preventing cross-context and cross-key reuse.

§Arguments
  • message — Arbitrary-length payload to authenticate.
  • context — Application-defined domain tag (e.g. b"my-app-v1"). Use a distinct context for each logical operation to prevent cross-context signature reuse.
  • random_seed — 64 bytes of fresh CSPRNG entropy. The value is consumed and zeroized after use. While SWing uses randomized signing to defend against side-channel attacks, reusing a seed against the same message/key remains secure (falling back to deterministic security).
§Errors
VariantCause
Error::SigningFailedAn underlying primitive returned an error
Error::InvalidFormatInternal slice conversion failed (should not occur)
§Example
use b_wing::SWing;

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

let sig = swing.sign(b"hello", b"my-app-v1", random_seed).unwrap();
assert_eq!(sig.len(), SWing::SIGNATURE_SIZE);
§Memory Usage

For the Ed25519 layer, this method performs a heap allocation (.concat()) proportional to the size of the message. When signing gigabyte-scale payloads, ensure the system has sufficient memory or pre-hash the payload before passing it to SWing.

Source

pub fn verify( vk: &[u8], message: &[u8], context: &[u8], signature: &[u8], ) -> Result<bool, Error>

Verifies a composite SWing signature against the provided verification key.

Verification proceeds sequentially through all three sub-signatures in the order Ed25519 → ML-DSA-87 → SLH-DSA-SHAKE256f and returns Error::VerificationFailed as soon as any one of them fails (fail-fast behaviour). A return value of Ok(true) guarantees that all three sub-signatures are valid.

§Arguments
  • vkVERIFICATION_KEY_SIZE-byte composite verification key obtained from get_pub_key.
  • message — The original payload that was signed.
  • context — The context tag used during signing (must match exactly).
  • signatureSIGNATURE_SIZE-byte composite signature produced by sign.
§Errors
VariantCause
Error::InvalidFormatvk or signature has wrong length, or a sub-key cannot be parsed
Error::VerificationFailedAny sub-signature is invalid
§Example
use b_wing::SWing;

let ok = SWing::verify(&vk, b"hello", b"ctx", &sig).unwrap();
assert!(ok);

// Tampered message must fail:
assert!(SWing::verify(&vk, b"TAMPERED", b"ctx", &sig).is_err());

Auto Trait Implementations§

§

impl Freeze for SWing

§

impl RefUnwindSafe for SWing

§

impl Send for SWing

§

impl Sync for SWing

§

impl Unpin for SWing

§

impl UnsafeUnpin for SWing

§

impl UnwindSafe for SWing

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