pub struct SWing { /* private fields */ }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
| Constant | Bytes | Components |
|---|---|---|
VERIFICATION_KEY_SIZE | 2,688 | Ed25519(32) ‖ ML-DSA-87(2592) ‖ SLH-DSA(64) |
SIGNATURE_SIZE | 54,547 | Ed25519(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
impl SWing
Sourcepub const VERIFICATION_KEY_SIZE: usize
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) ]Sourcepub const SIGNATURE_SIZE: usize
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) ]Sourcepub fn from_seed(master_seed: &[u8; 160]) -> Result<Self, Error>
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_seedmust be generated by a cryptographically secure random number generator (CSPRNG) such asgetrandom.- 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");Sourcepub fn get_pub_key(&self) -> &[u8] ⓘ
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.
Sourcepub fn sign(
&self,
message: &[u8],
context: &[u8],
random_seed: impl Into<Zeroizing<[u8; 64]>>,
) -> Result<Vec<u8>, Error>
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
| Variant | Cause |
|---|---|
Error::SigningFailed | An underlying primitive returned an error |
Error::InvalidFormat | Internal 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.
Sourcepub fn verify(
vk: &[u8],
message: &[u8],
context: &[u8],
signature: &[u8],
) -> Result<bool, Error>
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
vk—VERIFICATION_KEY_SIZE-byte composite verification key obtained fromget_pub_key.message— The original payload that was signed.context— The context tag used during signing (must match exactly).signature—SIGNATURE_SIZE-byte composite signature produced bysign.
§Errors
| Variant | Cause |
|---|---|
Error::InvalidFormat | vk or signature has wrong length, or a sub-key cannot be parsed |
Error::VerificationFailed | Any 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());