slhdsa

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 14, 2025 License: MIT Imports: 9 Imported by: 0

README

slhdsa

A simple, dependency-free Go implementation of SLH-DSA (Stateless Hash-Based Digital Signature Algorithm) as specified in FIPS 205.

SLH-DSA is a post-quantum digital signature scheme, designed to be secure against attacks by quantum computers.

Goals

  • Zero external dependencies - Uses only Go 1.24+ standard library
  • Simple API - Just GenerateKey, Sign, and Verify
  • Easy to read - Clean, straightforward implementation
  • FIPS 205 compliant - Validated against official NIST test vectors

Installation

go get github.com/KarpelesLab/slhdsa

Requires Go 1.24 or later.

Usage

package main

import (
	"crypto/rand"
	"fmt"

	"github.com/KarpelesLab/slhdsa"
)

func main() {
	// Generate a key pair
	sk, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_128f)
	if err != nil {
		panic(err)
	}

	// Sign a message (implements crypto.Signer)
	message := []byte("Hello, post-quantum world!")
	signature, err := sk.Sign(nil, message, nil)
	if err != nil {
		panic(err)
	}

	// Verify the signature
	pk := sk.Public().(*slhdsa.PublicKey)
	valid := pk.Verify(signature, message, nil)
	fmt.Println("Signature valid:", valid)
}

Parameter Sets

All 12 FIPS 205 parameter sets are supported:

Parameter Set Security Signature Size Public Key Private Key
SHA2_128s 128-bit 7,856 bytes 32 bytes 64 bytes
SHA2_128f 128-bit 17,088 bytes 32 bytes 64 bytes
SHA2_192s 192-bit 16,224 bytes 48 bytes 96 bytes
SHA2_192f 192-bit 35,664 bytes 48 bytes 96 bytes
SHA2_256s 256-bit 29,792 bytes 64 bytes 128 bytes
SHA2_256f 256-bit 49,856 bytes 64 bytes 128 bytes
SHAKE_128s 128-bit 7,856 bytes 32 bytes 64 bytes
SHAKE_128f 128-bit 17,088 bytes 32 bytes 64 bytes
SHAKE_192s 192-bit 16,224 bytes 48 bytes 96 bytes
SHAKE_192f 192-bit 35,664 bytes 48 bytes 96 bytes
SHAKE_256s 256-bit 29,792 bytes 64 bytes 128 bytes
SHAKE_256f 256-bit 49,856 bytes 64 bytes 128 bytes

The s variants are slower but produce smaller signatures. The f variants are faster but produce larger signatures.

API

The PrivateKey type implements crypto.Signer and crypto.MessageSigner (Go 1.25+).

Key Generation
sk, err := slhdsa.GenerateKey(rand.Reader, slhdsa.SHA2_128f)
Signing
// Basic signing (deterministic) - implements crypto.Signer
sig, err := sk.Sign(nil, message, nil)

// With context (for domain separation)
sig, err := sk.Sign(nil, message, &slhdsa.Options{Context: []byte("my-context")})

// With randomness (hedged signing, may help against side-channel attacks)
sig, err := sk.Sign(rand.Reader, message, nil)
Verification
pk := sk.Public().(*slhdsa.PublicKey)
valid := pk.Verify(signature, message, nil)

// With context (must match what was used during signing)
valid := pk.Verify(signature, message, []byte("my-context"))
Key Serialization
// Export keys
skBytes := sk.Bytes()
pkBytes := sk.Public().Bytes()

// Import keys
sk, err := slhdsa.NewPrivateKey(slhdsa.SHA2_128f, skBytes)
pk, err := slhdsa.NewPublicKey(slhdsa.SHA2_128f, pkBytes)

License

MIT License - see LICENSE file.

Documentation

Overview

Package slhdsa implements the SLH-DSA (Stateless Hash-Based Digital Signature Algorithm) as specified in FIPS 205.

SLH-DSA is a post-quantum digital signature scheme based on hash functions. It provides strong security guarantees against both classical and quantum attacks.

Index

Constants

This section is empty.

Variables

View Source
var (
	// SHA2-based parameter sets
	SHA2_128s = &Params{
		name: "SLH-DSA-SHA2-128s", isShake: false,
		n: 16, h: 63, d: 7, hPrime: 9, a: 12, k: 14, m: 30, w: 16, len: 35,
		sigSize: 7856, pkSize: 32, skSize: 64,
	}
	SHA2_128f = &Params{
		name: "SLH-DSA-SHA2-128f", isShake: false,
		n: 16, h: 66, d: 22, hPrime: 3, a: 6, k: 33, m: 34, w: 16, len: 35,
		sigSize: 17088, pkSize: 32, skSize: 64,
	}
	SHA2_192s = &Params{
		name: "SLH-DSA-SHA2-192s", isShake: false,
		n: 24, h: 63, d: 7, hPrime: 9, a: 14, k: 17, m: 39, w: 16, len: 51,
		sigSize: 16224, pkSize: 48, skSize: 96,
	}
	SHA2_192f = &Params{
		name: "SLH-DSA-SHA2-192f", isShake: false,
		n: 24, h: 66, d: 22, hPrime: 3, a: 8, k: 33, m: 42, w: 16, len: 51,
		sigSize: 35664, pkSize: 48, skSize: 96,
	}
	SHA2_256s = &Params{
		name: "SLH-DSA-SHA2-256s", isShake: false,
		n: 32, h: 64, d: 8, hPrime: 8, a: 14, k: 22, m: 47, w: 16, len: 67,
		sigSize: 29792, pkSize: 64, skSize: 128,
	}
	SHA2_256f = &Params{
		name: "SLH-DSA-SHA2-256f", isShake: false,
		n: 32, h: 68, d: 17, hPrime: 4, a: 9, k: 35, m: 49, w: 16, len: 67,
		sigSize: 49856, pkSize: 64, skSize: 128,
	}

	// SHAKE-based parameter sets
	SHAKE_128s = &Params{
		name: "SLH-DSA-SHAKE-128s", isShake: true,
		n: 16, h: 63, d: 7, hPrime: 9, a: 12, k: 14, m: 30, w: 16, len: 35,
		sigSize: 7856, pkSize: 32, skSize: 64,
	}
	SHAKE_128f = &Params{
		name: "SLH-DSA-SHAKE-128f", isShake: true,
		n: 16, h: 66, d: 22, hPrime: 3, a: 6, k: 33, m: 34, w: 16, len: 35,
		sigSize: 17088, pkSize: 32, skSize: 64,
	}
	SHAKE_192s = &Params{
		name: "SLH-DSA-SHAKE-192s", isShake: true,
		n: 24, h: 63, d: 7, hPrime: 9, a: 14, k: 17, m: 39, w: 16, len: 51,
		sigSize: 16224, pkSize: 48, skSize: 96,
	}
	SHAKE_192f = &Params{
		name: "SLH-DSA-SHAKE-192f", isShake: true,
		n: 24, h: 66, d: 22, hPrime: 3, a: 8, k: 33, m: 42, w: 16, len: 51,
		sigSize: 35664, pkSize: 48, skSize: 96,
	}
	SHAKE_256s = &Params{
		name: "SLH-DSA-SHAKE-256s", isShake: true,
		n: 32, h: 64, d: 8, hPrime: 8, a: 14, k: 22, m: 47, w: 16, len: 67,
		sigSize: 29792, pkSize: 64, skSize: 128,
	}
	SHAKE_256f = &Params{
		name: "SLH-DSA-SHAKE-256f", isShake: true,
		n: 32, h: 68, d: 17, hPrime: 4, a: 9, k: 35, m: 49, w: 16, len: 67,
		sigSize: 49856, pkSize: 64, skSize: 128,
	}
)

Predefined parameter sets following FIPS 205. The naming convention is: HashFunction_SecurityLevel[s|f] - s = small signatures (slower signing) - f = fast signing (larger signatures)

Functions

This section is empty.

Types

type Options

type Options struct {
	// Context is an optional domain separation string (max 255 bytes).
	Context []byte
}

Options contains signing options for SLH-DSA. It implements crypto.SignerOpts.

func (*Options) HashFunc

func (o *Options) HashFunc() crypto.Hash

HashFunc returns 0 to indicate that SLH-DSA operates on raw messages.

type Params

type Params struct {
	// contains filtered or unexported fields
}

Params defines the parameters for an SLH-DSA instance. Use the predefined parameter sets like SHA2_128s, SHA2_128f, etc.

func ParamsByName

func ParamsByName(name string) *Params

ParamsByName returns the parameter set for the given algorithm name. Returns nil if the name is not recognized.

func (*Params) GenerateKey

func (p *Params) GenerateKey(rand io.Reader) (*PrivateKey, error)

GenerateKey generates a new key pair using the provided random source.

func (*Params) PrivateKeySize

func (p *Params) PrivateKeySize() int

PrivateKeySize returns the size of private keys in bytes.

func (*Params) PublicKeySize

func (p *Params) PublicKeySize() int

PublicKeySize returns the size of public keys in bytes.

func (*Params) SignatureSize

func (p *Params) SignatureSize() int

SignatureSize returns the size of signatures in bytes.

func (*Params) String

func (p *Params) String() string

String returns the algorithm name.

type PrivateKey

type PrivateKey struct {
	PublicKey
	// contains filtered or unexported fields
}

PrivateKey represents an SLH-DSA private key.

func GenerateKey

func GenerateKey(rand io.Reader, params *Params) (*PrivateKey, error)

GenerateKey generates a new SLH-DSA key pair.

func NewPrivateKey

func NewPrivateKey(params *Params, keyBytes []byte) (*PrivateKey, error)

NewPrivateKey parses a serialized private key. The key bytes must be: SK.seed || SK.prf || PK.seed || PK.root

func (*PrivateKey) Bytes

func (sk *PrivateKey) Bytes() []byte

Bytes returns the serialized private key (SK.seed || SK.prf || PK.seed || PK.root).

func (*PrivateKey) Equal

func (sk *PrivateKey) Equal(other *PrivateKey) bool

Equal reports whether sk and other have the same value.

func (*PrivateKey) Params

func (sk *PrivateKey) Params() *Params

Params returns the parameter set used by this key.

func (*PrivateKey) Public

func (sk *PrivateKey) Public() crypto.PublicKey

Public returns the public key corresponding to this private key. It implements the crypto.Signer interface.

func (*PrivateKey) Sign

func (sk *PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error)

Sign implements crypto.Signer. It signs the message with the private key.

SLH-DSA does not support pre-hashing, so opts.HashFunc() must be 0. Pass nil or &Options{} for default options, or &Options{Context: ctx} for domain separation.

If rand is nil, signing is deterministic. If rand is provided, it supplies entropy for hedged signing which may help against side-channel attacks.

func (*PrivateKey) SignMessage

func (sk *PrivateKey) SignMessage(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error)

SignMessage implements crypto.MessageSigner. It signs the message with the private key.

SLH-DSA does not support pre-hashing, so opts.HashFunc() must be 0. Pass nil or &Options{} for default options, or &Options{Context: ctx} for domain separation.

If rand is nil, signing is deterministic. If rand is provided, it supplies entropy for hedged signing which may help against side-channel attacks.

type PublicKey

type PublicKey struct {
	// contains filtered or unexported fields
}

PublicKey represents an SLH-DSA public key.

func NewPublicKey

func NewPublicKey(params *Params, keyBytes []byte) (*PublicKey, error)

NewPublicKey parses a serialized public key. The key bytes must be: PK.seed || PK.root

func (*PublicKey) Bytes

func (pk *PublicKey) Bytes() []byte

Bytes returns the serialized public key (PK.seed || PK.root).

func (*PublicKey) Equal

func (pk *PublicKey) Equal(x crypto.PublicKey) bool

Equal reports whether pk and other have the same value. It implements the crypto.PublicKey interface.

func (*PublicKey) Params

func (pk *PublicKey) Params() *Params

Params returns the parameter set used by this key.

func (*PublicKey) Verify

func (pk *PublicKey) Verify(signature, message, context []byte) bool

Verify verifies an SLH-DSA signature.

The context parameter must match what was used during signing.

FIPS 205 Algorithm 24: slh_verify

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL