pub struct Wots<P: ReferenceProfile> { /* private fields */ }Expand description
Stateless HashSigsRS WOTS+ reference implementation for profile P.
Wots<LegacyKeccak> and Wots<HashSigsSha256GenericV1> have different key
and signature types. The following accidental cross-profile verification is
rejected at compile time:
use hashsigs_reference::{
HashSigsSha256GenericV1, LegacyKeccak, MessageDigest, PublicKey, Signature,
Wots, CHAIN_COUNT,
};
let key = PublicKey::<LegacyKeccak>::new([0; 32], [0; 32]);
let signature = Signature::<HashSigsSha256GenericV1>::new([[0; 32]; CHAIN_COUNT]);
let message = MessageDigest::new([0; 32]);
Wots::<LegacyKeccak>::new().verify(&key, &message, &signature);Serialized keys and signatures have no in-band profile tag. A protocol that
handles bytes must authenticate Profile::ID separately.
Implementations§
Source§impl<P: ReferenceProfile> Wots<P>
impl<P: ReferenceProfile> Wots<P>
Sourcepub const fn profile_id(&self) -> ProfileId
pub const fn profile_id(&self) -> ProfileId
Returns the cryptographic profile implemented by this oracle.
Sourcepub fn prf(&self, seed: &HashValue, index: u16) -> HashValue
pub fn prf(&self, seed: &HashValue, index: u16) -> HashValue
Applies the HashSigsRS domain-separated PRF.
The exact preimage is 0x03 || seed || u16_be(index), totaling 35
bytes. The two-byte index is big-endian, matching pinned upstream code.
Sourcepub fn randomization_masks(&self, public_seed: &HashValue) -> [HashValue; 16]
pub fn randomization_masks(&self, public_seed: &HashValue) -> [HashValue; 16]
Derives all randomization masks that can affect a result.
Pinned upstream allocates 67 PRF results. Its function key is mask zero;
signing and key generation start every chain at index zero and perform at
most 15 transitions; verification starts at a message digit and ends at
index 15. Consequently every observable access is in 0..=15, and
upstream masks 16 through 66 are provably unreachable. Computing exactly
these 16 entries preserves every key, signature, and verification result.
Sourcepub fn message_chain_indexes(&self, message: &MessageDigest) -> [u8; 67]
pub fn message_chain_indexes(&self, message: &MessageDigest) -> [u8; 67]
Converts a 32-byte message digest to 64 base-16 digits plus checksum.
Each byte contributes its high nibble followed by its low nibble. The
checksum is sum(15 - digit) and is appended as three big-endian base-16
digits, including leading zeroes.
Sourcepub fn private_key_from_seed(
&self,
private_seed: PrivateSeed<P>,
) -> PrivateKey<P>
pub fn private_key_from_seed( &self, private_seed: PrivateSeed<P>, ) -> PrivateKey<P>
Derives a one-time private key by hashing a caller-supplied seed once.
The seed is consumed to discourage accidental derivation of the same key for multiple signing calls.
Sourcepub fn public_key_from_private_key(
&self,
private_key: &PrivateKey<P>,
) -> PublicKey<P>
pub fn public_key_from_private_key( &self, private_key: &PrivateKey<P>, ) -> PublicKey<P>
Derives a public key from a one-time private key.
This computes all 67 complete, 15-transition chains and hashes their concatenated endpoints. The private key is borrowed so callers can derive the public key before consuming it in exactly one signing operation.
Sourcepub fn generate_key_pair(&self, private_seed: PrivateSeed<P>) -> KeyPair<P>
pub fn generate_key_pair(&self, private_seed: PrivateSeed<P>) -> KeyPair<P>
Generates a public/private WOTS key pair from a one-time seed.
Sourcepub fn sign(
&self,
private_key: PrivateKey<P>,
message: &MessageDigest,
) -> Signature<P>
pub fn sign( &self, private_key: PrivateKey<P>, message: &MessageDigest, ) -> Signature<P>
Signs one 32-byte message digest and consumes the one-time private key.
This compatibility-oriented path stops each chain at its message digit,
so its amount of hash work depends on the public message. Use
Self::sign_and_public_key_from_private_key for the fixed-work fused
hardware oracle.
Sourcepub fn verify(
&self,
public_key: &PublicKey<P>,
message: &MessageDigest,
signature: &Signature<P>,
) -> bool
pub fn verify( &self, public_key: &PublicKey<P>, message: &MessageDigest, signature: &Signature<P>, ) -> bool
Verifies a typed signature against a typed public key and message digest.
Profile mismatches are compile-time type errors. The final public-key hash comparison examines every byte before returning.
Sourcepub fn verify_bytes(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<bool, LengthError>
pub fn verify_bytes( &self, public_key: &[u8], message: &[u8], signature: &[u8], ) -> Result<bool, LengthError>
Parses and verifies untrusted serialized inputs.
Wrong-length message, public-key, or signature values return a
LengthError. Correctly sized but corrupted values return Ok(false).
§Errors
Returns LengthError when any input does not have its protocol-defined
fixed length.
Sourcepub fn sign_and_public_key_from_private_key(
&self,
private_key: PrivateKey<P>,
message: &MessageDigest,
) -> FusedOutput<P>
pub fn sign_and_public_key_from_private_key( &self, private_key: PrivateKey<P>, message: &MessageDigest, ) -> FusedOutput<P>
Signs and derives the public key in one fixed-work traversal.
Every one of the 67 chains is advanced through all 15 transitions. The signature segment is copied when the public message digit is reached, and the final value becomes the public-key endpoint. No message digit can skip a hash operation. The private key is consumed to discourage reuse.
Sourcepub fn sign_and_public_key_from_private_seed(
&self,
private_seed: PrivateSeed<P>,
message: &MessageDigest,
) -> FusedOutput<P>
pub fn sign_and_public_key_from_private_seed( &self, private_seed: PrivateSeed<P>, message: &MessageDigest, ) -> FusedOutput<P>
Derives a private key, signs, and derives its public key at fixed work.
The seed is hashed once and then every chain performs all 15 transitions.
For LEGACY_KECCAK this costs exactly 1,173 Keccak-f permutations. For
HASHSIGS_SHA256_GENERIC_V1 it costs exactly 1,258 SHA-256 compression
blocks. See the corresponding constants in hashsigs-types for the full
derivations.