An implementation of the Winternitz One-Time Signature (WOTS) scheme, designed for use with Solana. This implementation supports both SHA-256 and Keccak hash functions and is compatible with no_std environments.
- Support for multiple hash functions via the
WinternitzHash
trait - Optimized address generation via Merkle tree computation
- Split signature support for more efficient verification
- Address encoding/decoding with base58
- BIP32 derivation path support for key generation from seed
- no_std compatible for Solana programs
use winternitz::{hash::WinternitzKeccak, privkey::WinternitzPrivkey};
// Generate a new random private key
let privkey = WinternitzPrivkey::generate();
// Or derive from a seed using BIP32
let seed = [0u8; 64]; // Use a proper seed in practice
let path = "m/30583'/0'/0'";
let privkey = WinternitzPrivkey::from_seed(seed, path).unwrap();
// Derive the corresponding public key
let pubkey = privkey.pubkey::<WinternitzKeccak>();
// Sign a message
let message = b"Hello, World!";
let signature = privkey.sign::<WinternitzKeccak>(message);
// Recover public key from signature and message
let recovered_pubkey = signature.recover_pubkey::<WinternitzKeccak>(message);
// Verify by comparing public keys
assert_eq!(recovered_pubkey, pubkey);
// Or verify using address
let address = pubkey.address::<WinternitzKeccak>();
// Split the signature for more efficient verification
let (pairing_hash, commitment, execute) = signature.split::<WinternitzKeccak>(message);
// Recover the address using only the commitment part
let recovered_address = commitment.recover_address::<WinternitzKeccak>(message, &pairing_hash);
// Recover the pairing hash using only the execute part
let recovered_pairing_hash = execute.recover_pairing_hash::<WinternitzKeccak>(message);
// Get address from public key
let address = pubkey.address::<WinternitzKeccak>();
// Convert address to string
let address_str = address.to_array_string();
// Parse address from string
let parsed_address = WinternitzAddress::try_from(address_str.as_str()).unwrap();
WinternitzPrivkey
: Private key structure containing 32 32-byte seedsWinternitzPubkey
: Public key structure containing 32 32-byte valuesWinternitzSignature
: Full signature structure containing 32 32-byte valuesWinternitzCommitmentSignature
: Partial signature containing 28 32-byte valuesWinternitzExecuteSignature
: Partial signature containing 4 32-byte valuesWinternitzAddress
: 32-byte address value with Base58 encoding/decoding
The WinternitzHash
trait allows for flexible hash function selection:
pub trait WinternitzHash {
fn hash(msg: &[u8]) -> [u8; 32];
fn hashd(msg: &[u8]) -> [u8; 32];
fn hashv(msg: &[&[u8]]) -> [u8; 32];
fn hash_pair(a: &[u8], b: &[u8]) -> [u8; 32];
}
Implemented for both WinternitzSha256
and WinternitzKeccak
.
- One-Time Usage: Winternitz signatures are one-time signatures. Each private key should only be used once.
- Deterministic Signatures: The implementation produces deterministic signatures based on the message content.
- Key Secrecy: Always keep private keys secure; exposure compromises security.
solana_nostd_sha256
: SHA-256 implementationsolana_nostd_keccak
: Keccak implementationarraystring
: Fixed-capacity string implementation for no_stdfd_bs58
: Base58 encoding/decoding for addressesbip32
: For hierarchical deterministic key derivationrand
: Random number generation for key creation
All key structures use a fixed-size layout with 32-byte arrays:
#[repr(C)]
pub struct WinternitzPubkey(pub [[u8; 32]; 32]);
This ensures consistent memory representation and optimal performance.
When contributing, please ensure:
- All code is no_std compatible
- Tests cover new functionality
- Memory safety is maintained
- Documentation is updated appropriately
MIT