A Ruby implementation of the Winternitz One-Time Signature Plus (WOTS+) scheme as described in RFC 8391.
WOTS+ is a post-quantum cryptographic signature scheme that provides security against quantum computer attacks.
- Complete implementation of WOTS+ as specified in RFC 8391
- Support for multiple hash functions:
WOTSP-SHA2_256(SHA-256)WOTSP-SHA2_512(SHA-512)WOTSP-SHAKE_256(SHAKE-256)
- Support for Winternitz parameters w=4 and w=16
- Key generation from seed
- Digital signature generation and verification
Add this line to your application's Gemfile:
gem 'wots'And then execute:
$ bundle install
Or install it yourself as:
$ gem install wots
require 'wots'
# Use predefined parameters
param = WOTS::Param::SHA256
# Generate private key from seed
seed = "693141c7ee701d13e1a7c733e0aa8326c19961429bfb54083f2f65b30c32e20b"
private_key = WOTS::PrivateKey.from_seed(param, seed)
# Generate public key
pub_seed = "46ece585b4c0bfa1186209270e22fa07c4716461b5a026c268e594fb94404f3a"
public_key = WOTS::PublicKey.from_private_key(private_key, pub_seed)
# Sign a message
message = "f16c96e88fb99a8287a43121962e89ed521699fa3e126c67eaaa168066354477"
signature = private_key.sign(pub_seed, message)
# Verify signature by reconstructing public key
recovered_pk = WOTS::PublicKey.from_signature(signature, pub_seed, message)
valid = (recovered_pk == public_key)
puts "Signature valid: #{valid}"# SHA-256 based (n=32 bytes, w=16)
param = WOTS::Param::SHA256
# SHA-512 based (n=64 bytes, w=16)
param = WOTS::Param::SHA512
# SHAKE-256 based (n=32 bytes, w=16)
param = WOTS::Param::SHAKE256All key material, seeds and messages are even-length hexadecimal strings. Raw binary strings and
odd-length hex strings are rejected with an ArgumentError; convert them with unpack1('H*') first.
message = Digest::SHA256.hexdigest("some payload") # 64 hex chars = 32 bytes
signature = private_key.sign(pub_seed, message)WOTS+ is a one-time signature scheme. Each private key should only be used to sign one message. Reusing a private key to sign multiple messages can leak information about the private key and compromise security.
The seed passed to WOTS::PrivateKey.from_seed is secret key material and must come from a
cryptographically secure random number generator, e.g. SecureRandom.hex(param.n).
Only the Winternitz parameters defined in RFC 8391 (w = 4 and w = 16) are accepted. Other values
overflow the checksum encoding, which drops the high bits of the checksum and allows an attacker to
forge signatures without the private key.
Private key material is held in Ruby String objects and is not zeroized; it remains in memory until
garbage collected.
This implementation follows RFC 8391 - XMSS: eXtended Merkle Signature Scheme, Section 3 (WOTS+).