xmssmt

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2019 License: MIT Imports: 25 Imported by: 6

README

XMSSMT for Go

This is a Go implementation of the stateful hash-based signature-scheme XMSSMT described in rfc8391 (XMSS: Extended Hash-Based Signatures).

package main

import (
    "github.com/bwesterb/go-xmssmt" // imported as xmssmt
    "fmt"
)

func main() {
    // Create a new keypair.  See ListNames().
    sk, pk, err := xmssmt.GenerateKeyPair("XMSSMT-SHA2_20/4_256", "key")
    if err != nil {
        panic(err)
    }

    // Sign a message
    sig, err := sk.Sign([]byte("Example message!"))
    if err != nil {
        panic(err)
    }

    sigBytes, _ := sig.MarshalBinary() // serialize signature
    pkBytes, _ := pk.MarshalBinary()   // serialize public key
    fmt.Printf("len(sigBytes)=%d  len(pkBytes)=%d\n",
        len(sigBytes), len(pkBytes))
    sk.Close() // close the private key container

    // To verify we can simply use the Verify() method on PublicKey
    valid, _ := pk.Verify(sig, []byte("Example message!"))
    fmt.Printf("Valid=%v\n", valid)

    // Or we can use the helper xmssmt.Verify() on serialized signature and pk
    valid, _ = xmssmt.Verify(pkBytes, sigBytes, []byte("Example message!"))
    fmt.Printf("Valid=%v\n", valid)

    // To sign a new message, we open the private key container again
    sk, pk, _, _ = xmssmt.LoadPrivateKey("key")
    sig2, _ := sk.Sign([]byte("Other message"))
    valid, _ = pk.Verify(sig2, []byte("Other message"))
    fmt.Printf("Valid=%v\n", valid)
    sk.Close()

    // Or we can simply use the xmssmt.Sign() helper.
    pkBytes, _ = pk.MarshalBinary()
    sig3Bytes, _ := xmssmt.Sign("key", []byte("Third message"))
    valid, _ = xmssmt.Verify(pkBytes, sig3Bytes, []byte("Third message"))
    fmt.Printf("Valid=%v\n", valid)
}

See godoc for further documentation of the API.

Documentation

Overview

Go implementation of the XMSS[MT] post-quantum stateful hash-based signature scheme as described in the RFC draft https://datatracker.ietf.org/doc/draft-irtf-cfrg-xmss-hash-based-signatures/

Example
// Create a new keypair.  See ListNames().
sk, pk, err := xmssmt.GenerateKeyPair("XMSSMT-SHA2_20/4_256", "key")
if err != nil {
	panic(err)
}

// Sign a message
sig, err := sk.Sign([]byte("Example message!"))
if err != nil {
	panic(err)
}

sigBytes, _ := sig.MarshalBinary() // serialize signature
pkBytes, _ := pk.MarshalBinary()   // serialize public key
fmt.Printf("len(sigBytes)=%d  len(pkBytes)=%d\n",
	len(sigBytes), len(pkBytes))
sk.Close() // close the private key container

// To verify we can simply use the Verify() method on PublicKey
valid, _ := pk.Verify(sig, []byte("Example message!"))
fmt.Printf("Valid=%v\n", valid)

// Or we can use the helper xmssmt.Verify() on serialized signature and pk
valid, _ = xmssmt.Verify(pkBytes, sigBytes, []byte("Example message!"))
fmt.Printf("Valid=%v\n", valid)

// To sign a new message, we open the private key container again
sk, pk, _, _ = xmssmt.LoadPrivateKey("key")
sig2, _ := sk.Sign([]byte("Other message"))
valid, _ = pk.Verify(sig2, []byte("Other message"))
fmt.Printf("Valid=%v\n", valid)
sk.Close()

// Or pwe can simply use the xmssmt.Sign() helper.
pkBytes, _ = pk.MarshalBinary()
sig3Bytes, _ := xmssmt.Sign("key", []byte("Third message"))
valid, _ = xmssmt.Verify(pkBytes, sig3Bytes, []byte("Third message"))
fmt.Printf("Valid=%v\n", valid)
Output:

Index

Examples

Constants

View Source
const (
	ADDR_TYPE_OTS      = 0
	ADDR_TYPE_LTREE    = 1
	ADDR_TYPE_HASHTREE = 2
)
View Source
const (
	// First 8 bytes (in hex) of the secret key file
	FS_CONTAINER_KEY_MAGIC = "4089430a5ced6844"

	// First 8 bytes (in hex) of the subtree cache file
	FS_CONTAINER_CACHE_MAGIC = "e77957607ef79446"
)
View Source
const (
	HASH_PADDING_F    = 0
	HASH_PADDING_H    = 1
	HASH_PADDING_HASH = 2
	HASH_PADDING_PRF  = 3
)

Variables

This section is empty.

Functions

func EnableLogging

func EnableLogging()

Enables logging to log package. For more flexibility, see SetLogger().

func GenerateKeyPair

func GenerateKeyPair(alg, privKeyPath string) (*PrivateKey, *PublicKey, Error)

Generate a new keypair for the given XMSS[MT] instance alg.

Stores the private key at privKeyPath. This will create two files: <privKeyPath> and <privKeyPath>.cache. The first contains the private key and the second contains sensitive cached information derived from the private key used to increase signing performance a lot.

Use ListNames() to list the supported instances of XMSS[MT].

For more flexibility use NewContextFromName() to create a Context and then call Context.GenerateKeyPair() or Context.DeriveInto().

NOTE Do not forget to Close() the PrivateKey.

func ListNames

func ListNames() (names []string)

List all named XMSS[MT] instances

func LoadPrivateKey

func LoadPrivateKey(path string) (
	sk *PrivateKey, pk *PublicKey, lostSigs uint32, err Error)

Loads the private key from the given filesystem container.

If the container wasn't properly closed, there might have been signatures lost. The amount of returned in lostSigs.

NOTE Takes ownership of ctr. Do not forget to Close() the PrivateKey.

func LoadPrivateKeyFrom

func LoadPrivateKeyFrom(ctr PrivateKeyContainer) (
	sk *PrivateKey, pk *PublicKey, lostSigs uint32, err Error)

Loads the private key from the given private key container.

If the container wasn't properly closed, there might have been signatures lost. The amount of returned in lostSigs.

NOTE Takes ownership of ctr. Do not forget to Close() the PrivateKey.

func NewContext

func NewContext(params Params) (ctx *Context, err Error)

Creates a new context.

func OpenFSPrivateKeyContainer

func OpenFSPrivateKeyContainer(path string) (PrivateKeyContainer, Error)

Returns a PrivateKeyContainer backed by the filesystem.

func SetLogger

func SetLogger(logger Logger)

Enables logging. Disable logging by passing nil.

Use EnableLogging if you want to log to the log package.

Types

type Context

type Context struct {
	// Number of worker goroutines ("threads") to use for expensive operations.
	// Will guess an appropriate number if set to 0.
	Threads int
	// contains filtered or unexported fields
}

XMSS[MT] instance. Create one using NewContextFromName, NewContextFromOid or NewContext.

func NewContextFromName

func NewContextFromName(name string) *Context

Return new context for the given XMSS[MT] algorithm name (and nil if the algorithm name is unknown).

func NewContextFromOid

func NewContextFromOid(mt bool, oid uint32) *Context

Return new context for the given XMSS[MT] oid (and nil if it's unknown).

func (*Context) Derive

func (ctx *Context) Derive(path string, pubSeed, skSeed, skPrf []byte) (
	*PrivateKey, *PublicKey, Error)

Derives an XMSS[MT] public/private keypair from the given seeds and stores it at the given path on the filesystem. NOTE Do not forget to Close() the returned PrivateKey

func (*Context) DeriveInto

func (ctx *Context) DeriveInto(ctr PrivateKeyContainer,
	pubSeed, skSeed, skPrf []byte) (*PrivateKey, *PublicKey, Error)

Derives an XMSS[MT] public/private keypair from the given seeds and stores it in the container. pubSeed, skSeed and skPrf should be secret random ctx.p.N length byte slices.

func (*Context) GenerateKeyPair

func (ctx *Context) GenerateKeyPair(path string) (
	*PrivateKey, *PublicKey, Error)

Generates an XMSS[MT] public/private keypair and stores it at the given path on the filesystem.

NOTE Do not forget to Close() the returned PrivateKey

func (*Context) MT

func (ctx *Context) MT() bool

Returns whether this is an XMSSMT instance (as opposed to XMSS)

func (*Context) Name

func (ctx *Context) Name() string

Returns the name of the XMSSMT instance and an empty string if it has no name.

func (*Context) Oid

func (ctx *Context) Oid() uint32

Returns the Oid of the XMSSMT instance and 0 if it has no Oid.

func (*Context) Params

func (ctx *Context) Params() Params

Get parameters of an XMSS[MT] instance

func (*Context) SignatureSize

func (ctx *Context) SignatureSize() uint32

Returns the size of signatures of this XMSS[MT] instance

type Error

type Error interface {
	error
	Locked() bool // Is this error because something (like a file) was locked?
	Inner() error // Returns the wrapped error, if any
}

func Sign

func Sign(privKeyPath string, msg []byte) (sig []byte, err Error)

Create a signature on msg using the private key stored at privKeyPath.

For more flexibility, use PrivateKey.Sign().

func Verify

func Verify(pk, sig, msg []byte) (bool, Error)

Checks whether sig is a valid signature of pk on msg.

type HashFunc

type HashFunc uint8
const (
	SHA2 HashFunc = iota
	SHAKE
)

func HashFuncString added in v1.0.1

func HashFuncString(s string) (HashFunc, error)

HashFuncString retrieves an enum value from the enum constants string name. Throws an error if the param is not part of the enum.

func HashFuncValues added in v1.0.1

func HashFuncValues() []HashFunc

HashFuncValues returns all values of the enum

func (HashFunc) IsAHashFunc added in v1.0.1

func (i HashFunc) IsAHashFunc() bool

IsAHashFunc returns "true" if the value is listed in the enum definition. "false" otherwise

func (HashFunc) String added in v1.0.1

func (i HashFunc) String() string

type Logger

type Logger interface {
	Logf(format string, a ...interface{})
}

type Params

type Params struct {
	Func       HashFunc // which has function to use
	N          uint32   // security parameter: influences length of hashes
	FullHeight uint32   // full height of tree
	D          uint32   // number of subtrees; 1 for XMSS, >1 for XMSSMT

	// WOTS+ Winternitz parameter.  Only 4, 16 and 256 are supported.
	WotsW uint16
}

Parameters of an XMSS[MT] instance

func ParamsFromName

func ParamsFromName(name string) *Params

Returns paramters for the named XMSS[MT] instance (and nil if there is no such algorithm).

func (*Params) BareSubTreeSize

func (params *Params) BareSubTreeSize() int

Returns the size of the subtrees for this parameter.

func (*Params) CachedSubTreeSize

func (params *Params) CachedSubTreeSize() int

Returns the size of the cached subtrees for this parameter.

func (*Params) LookupNameAndOid

func (params *Params) LookupNameAndOid() (string, uint32)

Returns the name and OID of this set of parameters, it is has them.

func (*Params) MarshalBinary

func (params *Params) MarshalBinary() ([]byte, error)

Encodes parameters in the reserved Oid space as follows (big endian).

  8-bit magic         should be 0xEA
  4-bit version       should be 0
  4-bit compr-n       contains (n/8)-1 for the parameter n
  2-bit hash          the hash function
  2-bit w             0 for WotsW=4, 1 for WotsW=16, 2 for WotsW=256
  6-bit full-height   the full height parameter
  6-bit d             the parameter d

We assume XMSS if d == 1 and XMSSMT otherwise.

func (*Params) MaxSignatureSeqNo

func (params *Params) MaxSignatureSeqNo() uint64

Returns the maximum signature sequence number

func (*Params) PrivateKeySize

func (params *Params) PrivateKeySize() int

Size of the private key as stored by PrivateKeyContainer. NOTE this is not equal to the privateKeySize of the spec, which includes

the signature sequence number, OID and root

func (Params) String added in v1.0.1

func (p Params) String() string

func (*Params) UnmarshalBinary

func (params *Params) UnmarshalBinary(buf []byte) error

Decodes parameters as encoded by MarshalBinary().

func (*Params) WotsLen

func (params *Params) WotsLen() uint32

Returns the total number of WOTS+ chains

func (*Params) WotsLen1

func (params *Params) WotsLen1() uint32

Returns the number of main WOTS+ chains

func (*Params) WotsLen2

func (params *Params) WotsLen2() uint32

Returns the number of WOTS+ checksum chains

func (*Params) WotsLogW

func (params *Params) WotsLogW() uint8

Returns the 2log of the Winternitz parameter

func (*Params) WotsSignatureSize

func (params *Params) WotsSignatureSize() uint32

Returns the size of a WOTS+ signature

func (*Params) WriteInto

func (params *Params) WriteInto(buf []byte) error

Write parameters into buf as encoded by MarshalBinary().

type PrivateKey

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

XMSS[MT] private key

func (*PrivateKey) BorrowExactly

func (sk *PrivateKey) BorrowExactly(amount uint32) Error

Ensures there are exactly the given number of signature sequence numbers are reserved for use by Sign().

In a typical setup, each call to Sign() will write and fsync() the current signature sequence number to disk, such that signature sequence numbers aren't reused in a crash. This does slow down Sign() quite a bit. To speed things up, we can reserve signatures; write the fact that we did this to disk and correct the signature sequence number on Close(). The drawback is that with a crash or a missing Close(), we will loose the signatures that were reserved.

func (*PrivateKey) BorrowExactlyIfBelow

func (sk *PrivateKey) BorrowExactlyIfBelow(amount, treshHold uint32) Error

Atomically runs BorrowExactly(amount) if BorrowedSeqNos() <= treshHold.

func (*PrivateKey) BorrowedSeqNos

func (sk *PrivateKey) BorrowedSeqNos() uint32

Returns the number of signature sequence numbers borrowed from the container. See BorrowExactly() or PrivateKeyContainer.BorrowSeqNos()

func (*PrivateKey) CachedSubTrees

func (sk *PrivateKey) CachedSubTrees() int

Returns the number of subtrees that are cached

func (*PrivateKey) Close

func (sk *PrivateKey) Close() Error

Close the underlying container

func (*PrivateKey) Context

func (sk *PrivateKey) Context() *Context

func (*PrivateKey) DangerousSetSeqNo

func (sk *PrivateKey) DangerousSetSeqNo(seqNo SignatureSeqNo)

You probably should not use this function

Sets the signature sequence number. Be very careful not to use the same signature sequence number twice.

func (*PrivateKey) EnableSubTreePrecomputation

func (sk *PrivateKey) EnableSubTreePrecomputation()

Enable subtree precomputation.

By default, a subtree is computed when it's needed. So with subtrees of height 10, every 1024th Sign() will be slow because a new subtree is generated.

When subtree precomputation is enabled, the next subtree is already computed in a separate thread when the previous subtree is consumed. This is useful when running a server which cannot tolerate a sudden spike in the duration of the Sign() function.

func (*PrivateKey) PublicKey

func (sk *PrivateKey) PublicKey() *PublicKey

Returns the PublicKey for this PrivateKey.

func (*PrivateKey) SeqNo

func (sk *PrivateKey) SeqNo() SignatureSeqNo

Returns the signature sequence used next.

func (*PrivateKey) Sign

func (sk *PrivateKey) Sign(msg []byte) (*Signature, Error)

Signs the given message.

func (*PrivateKey) SignFrom

func (sk *PrivateKey) SignFrom(msg io.Reader) (*Signature, Error)

Reads a message from the io.Reader and signs it.

func (*PrivateKey) UnretiredSeqNos

func (sk *PrivateKey) UnretiredSeqNos() uint32

Returns the number of unretired signature sequence numbers.

The PrivateKey keeps track of which signature sequence numbers might still be in use by a Sign() operation. If a Sign() operation finishes it "retires" the signature seqno it used so that private key container can drop caches that are no longer relevant.

type PrivateKeyContainer

type PrivateKeyContainer interface {
	// Reset (or initialize) the cache that stores the subtrees.  It is always
	// called before use.
	ResetCache() Error

	// Returns the buffer for the given subtree.  If the subtree does not
	// have a buffer yet, allocate it of the size params.CachedSubTreeSize()
	// with params as specified in the last call to Reset().
	// The exists return value indicates whether the subtree was present.
	// The container should write changes to buf back to the storage.
	// The containe does not have to ensure integrity, a checksum is added
	// to the end of the buffer.
	GetSubTree(address SubTreeAddress) (buf []byte, exists bool, err Error)

	// Returns whether the given subtree is in the cache.  Returns false
	// if the cache is not initialized.
	HasSubTree(address SubTreeAddress) bool

	// Drops the given subtree from the cache (if it was even cached to begin
	// with).
	DropSubTree(address SubTreeAddress) Error

	// Returns the list of cached subtrees
	ListSubTrees() ([]SubTreeAddress, Error)

	// Reset (or initialize) the container with the given private key
	// and parameters.  Calls ResetCache().
	Reset(privateKey []byte, params Params) Error

	// Returns the current signature sequence number and increment
	// the stored sequence number by the given amount.
	// The user can use the signatures in this range freely,
	// but should call SetSeqNo() later to record the actual number
	// of signatures used.
	BorrowSeqNos(amount uint32) (SignatureSeqNo, Error)

	// Sets the signature sequence number to the given value.
	// Removes the possible-lost-signatures record set by BorrowSeqNos.
	SetSeqNo(seqNo SignatureSeqNo) Error

	// Returns the current signature sequence number.
	// If BorrowSeqNos() has been called without corresponding SetSeqNo()
	// there might have been signatures lost.  In that case, calls to
	// GetSeqNo will return the number of possibly lost signatures
	// until SetSeqNo() has been called.
	GetSeqNo() (seqNo SignatureSeqNo, lostSigs uint32, err Error)

	// Returns the private key.
	GetPrivateKey() ([]byte, Error)

	// Returns the algorithm parameters if the container is initialized
	// (eg. the file exist) and nil if not.
	Initialized() *Params

	// Returns whether the cache is initialized.  If not,  it can be
	// initialized by calling ResetCache().
	CacheInitialized() bool

	// Closes the container.
	Close() Error
}

A PrivateKeyContainer has two tasks

  1. It has to store the XMSS[MT] secret key and sequence number of the first unused signature.
  2. It has to cache the precomputed subtrees to increase signing performance.

NOTE A PrivateKeyContainer does not have to be thread safe.

type PublicKey

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

XMSS[MT] public key

func (*PublicKey) Context

func (pk *PublicKey) Context() *Context

func (*PublicKey) MarshalBinary

func (pk *PublicKey) MarshalBinary() ([]byte, error)

Returns representation of the public key with parameters compressed into the reserved space of the Oid prefix. See Params.MarshalBinary().

func (*PublicKey) MarshalText

func (pk *PublicKey) MarshalText() ([]byte, error)

Returns base64 encoded version of the public key

func (*PublicKey) UnmarshalBinary

func (pk *PublicKey) UnmarshalBinary(buf []byte) error

Initializes the PublicKey as was stored by MarshalBinary.

func (*PublicKey) UnmarshalText

func (pk *PublicKey) UnmarshalText(text []byte) error

Initializes the Signature as stored by MarshalText.

func (*PublicKey) Verify

func (pk *PublicKey) Verify(sig *Signature, msg []byte) (bool, Error)

Check whether the sig is a valid signature of this public key for the given message.

func (*PublicKey) VerifyFrom

func (pk *PublicKey) VerifyFrom(sig *Signature, msg io.Reader) (bool, Error)

Reads a message from the io.Reader and verifies whether the provided signature is valid for this public key and message.

func (*PublicKey) WriteInto

func (pk *PublicKey) WriteInto(buf []byte) error

Writes the public key into buf in the same way as returned by PublicKey.MarshalBinary()

type Signature

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

Represents a XMSS[MT] signature

func (*Signature) Context

func (sig *Signature) Context() *Context

func (*Signature) MarshalBinary

func (sig *Signature) MarshalBinary() ([]byte, error)

Returns representation of signature with parameters compressed into the reserved space of the Oid prefix. See Params.MarshalBinary().

func (*Signature) SeqNo added in v1.0.1

func (sig *Signature) SeqNo() SignatureSeqNo

Returns the sequence number of this signature.

func (Signature) String added in v1.0.1

func (sig Signature) String() string

func (*Signature) UnmarshalBinary

func (sig *Signature) UnmarshalBinary(buf []byte) error

Initializes the Signature as stored by MarshalBinary.

func (*Signature) WriteInto

func (sig *Signature) WriteInto(buf []byte) error

Writes signature to buf in the same way as returned by Signature.MarshalBinary().

type SignatureSeqNo

type SignatureSeqNo uint64

Sequence number of signatures. (Corresponds with leaf indices in the implementation.)

type SubTreeAddress

type SubTreeAddress struct {
	// The height of the subtree.  The leaf-subtrees have layer=0
	Layer uint32

	// The offset in the subtree.  The leftmost subtrees have tree=0
	Tree uint64
}

Represents the position of a subtree in the full XMSSMT tree.

Jump to

Keyboard shortcuts

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