Skip to content

Go implementation of XMSS[MT] post-quantum hash-based signature scheme (rfc8391)

License

Notifications You must be signed in to change notification settings

bwesterb/go-xmssmt

Repository files navigation

XMSSMT for Go

This is a Go implementation of the stateful hash-based signature-scheme XMSS(MT) described in rfc8391 (XMSS: Extended Hash-Based Signatures) and NIST SP 800-208.

There is also a convenient commandline tool. Warning, XMSS(MT) is stateful, that means you should never backup your private key or make a copy of it. See the README of cli tool.

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-SHAKE_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.

Note on compatibility

go-xmssmt supports instances of XMSS[MT] that are (currently) not listed in the RFC or NIST SP and so might not be supported by other implementations, such as XMSSMT-SHAKE_20/4_128_w256. go-xmssmt encodes the parameters of these non-standard instances in the reserved space of Oid numbers, see Params.MarshalBinary(). For maximum compatibility, one can check whether the instance is supported by the RFC by checking Context.FromRFC() and Context.FromNIST().

Changes

1.5.2 (5-7-2022)

  • Fix build problems on 32-bit platforms. Thanks @sietseringers.
  • Update dependencies

1.5.0 (22-06-2021)

  • Fixed a big issue with non-standard instances with w≠16: for w=256 too many checksum chains were included and with w=4 too few. In both cases this means signatures would not be compatible with proper implementations. In the case of w=4, signatures could be forged. This breaks compatibility for non-standard instances with w≠16.

1.4.3 (21-12-2020)

  • Support systems with pagesizes different from 4096.

1.4.0 (25-05-2020)

  • The way the private key is generated has been changed in the same way as was done for the reference implementation to prevent a multi-target attack identified by ETSI TC CYBER WG QSC. As XMSS hasn't been in wide use yet, old keys do not need to be regenerated. Note that this will change the output of Derive().
  • Add support for the instances listed in NIST SP 800-208. Note that the 192 bit instances listed in the NIST publication use a different PRF construction and so XMSSMT-SHAKE_20/4_192 changes meaning in this version. The previously unlisted instance using the RFC construction can be accessed via XMSSMT-SHAKE_20/4_192_RFC. To use the NIST PRF construction on other modes, one can add _NIST at the end, eg. XMSSMT-SHA2_20/4_128_NIST.
  • Fixed a memory corruption bug in the unlisted 128 bit SHA2 instances. Before this version, keys and signatures for 128 bit SHA2 instances were incorrectly generated and verified.

1.3.0 (17-05-2020)

  • When available, use AVX2 to compute SHAKE fourway. This makes SHAKE faster than SHA2.

1.2.0 (27-12-2019)

  • Add support for instance names not listed in RFC.

1.1.0 (20-12-2019)

  • Add support for security parameter N=16.