blob: 375333a70cf686096cc366714c8c0bfbceba31da [file] [edit]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !fips140v1.0
package mldsa_test
import (
"bytes"
"crypto"
"crypto/fips140"
"crypto/internal/cryptotest"
. "crypto/mldsa"
"crypto/sha3"
"encoding/hex"
"flag"
"math/rand/v2"
"strings"
"testing"
)
var _ crypto.Signer = (*PrivateKey)(nil)
var sixtyMillionFlag = flag.Bool("60million", false, "run 60M-iterations accumulated test")
// TestAccumulated accumulates 10k (or 100, or 60M) random vectors and checks
// the hash of the result, to avoid checking in megabytes of test vectors.
//
// 60M in particular is enough to give a 99.9% chance of hitting every value in
// the base field.
//
// 1-((q-1)/q)^60000000 ~= 0.9992
//
// If setting -60million, remember to also set -timeout 0.
func TestAccumulated(t *testing.T) {
t.Run("ML-DSA-44/100", func(t *testing.T) {
testAccumulated(t, MLDSA44(), 100,
"d51148e1f9f4fa1a723a6cf42e25f2a99eb5c1b378b3d2dbbd561b1203beeae4")
})
t.Run("ML-DSA-65/100", func(t *testing.T) {
testAccumulated(t, MLDSA65(), 100,
"8358a1843220194417cadbc2651295cd8fc65125b5a5c1a239a16dc8b57ca199")
})
t.Run("ML-DSA-87/100", func(t *testing.T) {
testAccumulated(t, MLDSA87(), 100,
"8c3ad714777622b8f21ce31bb35f71394f23bc0fcf3c78ace5d608990f3b061b")
})
if !testing.Short() {
t.Run("ML-DSA-44/10k", func(t *testing.T) {
t.Parallel()
testAccumulated(t, MLDSA44(), 10000,
"e7fd21f6a59bcba60d65adc44404bb29a7c00e5d8d3ec06a732c00a306a7d143")
})
t.Run("ML-DSA-65/10k", func(t *testing.T) {
t.Parallel()
testAccumulated(t, MLDSA65(), 10000,
"5ff5e196f0b830c3b10a9eb5358e7c98a3a20136cb677f3ae3b90175c3ace329")
})
t.Run("ML-DSA-87/10k", func(t *testing.T) {
t.Parallel()
testAccumulated(t, MLDSA87(), 10000,
"80a8cf39317f7d0be0e24972c51ac152bd2a3e09bc0c32ce29dd82c4e7385e60")
})
}
if *sixtyMillionFlag {
t.Run("ML-DSA-44/60M", func(t *testing.T) {
t.Parallel()
testAccumulated(t, MLDSA44(), 60000000,
"080b48049257f5cd30dee17d6aa393d6c42fe52a29099df84a460ebaf4b02330")
})
t.Run("ML-DSA-65/60M", func(t *testing.T) {
t.Parallel()
testAccumulated(t, MLDSA65(), 60000000,
"0af0165db2b180f7a83dbecad1ccb758b9c2d834b7f801fc49dd572a9d4b1e83")
})
t.Run("ML-DSA-87/60M", func(t *testing.T) {
t.Parallel()
testAccumulated(t, MLDSA87(), 60000000,
"011166e9d5032c9bdc5c9bbb5dbb6c86df1c3d9bf3570b65ebae942dd9830057")
})
}
}
func testAccumulated(t *testing.T, params Parameters, n int, expected string) {
s := sha3.NewSHAKE128()
o := sha3.NewSHAKE128()
seed := make([]byte, PrivateKeySize)
msg := make([]byte, 0)
for i := 0; i < n; i++ {
s.Read(seed)
dk, err := NewPrivateKey(params, seed)
if err != nil {
t.Fatalf("NewPrivateKey: %v", err)
}
pk := dk.PublicKey().Bytes()
o.Write(pk)
sig, err := dk.SignDeterministic(msg, nil)
if err != nil {
t.Fatalf("SignDeterministic: %v", err)
}
o.Write(sig)
pub, err := NewPublicKey(params, pk)
if err != nil {
t.Fatalf("NewPublicKey: %v", err)
}
if *pub != *dk.PublicKey() {
t.Fatalf("public key mismatch")
}
if err := Verify(dk.PublicKey(), msg, sig, nil); err != nil {
t.Fatalf("Verify: %v", err)
}
}
sum := make([]byte, 32)
o.Read(sum)
got := hex.EncodeToString(sum)
if got != expected {
t.Errorf("got %s, expected %s", got, expected)
}
}
func testAllParameters(t *testing.T, f func(*testing.T, Parameters)) {
for _, params := range []Parameters{MLDSA44(), MLDSA65(), MLDSA87()} {
t.Run(params.String(), func(t *testing.T) {
f(t, params)
})
}
}
func TestGenerateKey(t *testing.T) {
testAllParameters(t, testGenerateKey)
}
func testGenerateKey(t *testing.T, params Parameters) {
k1, err := GenerateKey(params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
k2, err := GenerateKey(params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
if k1.Equal(k2) {
t.Errorf("two generated keys are equal")
}
k1x, err := NewPrivateKey(params, k1.Bytes())
if err != nil {
t.Fatalf("NewPrivateKey: %v", err)
}
if !k1.Equal(k1x) {
t.Errorf("generated key and re-parsed key are not equal")
}
}
func TestAllocations(t *testing.T) {
// We allocate
//
// - the PrivateKey (k and kk) structs
// - their temporary inner structs (2x)
// - the public key (pkBytes) and signature (sig) byte slices
// - the Options argument to Sign
//
// on the heap. The structs are too large for the stack, the byte slices are
// variable-sized, and Options is cast into an interface.
//
// Still, check we are not slipping more allocations in.
var expected float64 = 7
if fips140.Enabled() {
// The PCT does a sign/verify cycle, which allocates a signature slice.
expected += 1
}
if fips140.Version() == "v1.26.0" {
// The v1.26.0 implementation precomputes PublicKey, making it large
// enough to require heap allocation. Add pk, its inner struct, and the
// return value of k.PublicKey().
expected += 3
}
cryptotest.SkipTestAllocations(t)
if allocs := testing.AllocsPerRun(100, func() {
k, err := GenerateKey(MLDSA44())
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
seed := k.Bytes()
kk, err := NewPrivateKey(MLDSA44(), seed)
if err != nil {
t.Fatalf("NewPrivateKey: %v", err)
}
if !k.Equal(kk) {
t.Fatalf("keys not equal")
}
pkBytes := k.PublicKey().Bytes()
pk, err := NewPublicKey(MLDSA44(), pkBytes)
if err != nil {
t.Fatalf("NewPublicKey: %v", err)
}
message := []byte("Hello, world!")
context := "test"
sig, err := k.Sign(nil, message, &Options{Context: context})
if err != nil {
t.Fatalf("Sign: %v", err)
}
if err := Verify(pk, message, sig, &Options{Context: context}); err != nil {
t.Fatalf("Verify: %v", err)
}
}); allocs > expected {
t.Errorf("expected %0.0f allocations, got %0.1f", expected, allocs)
}
}
func TestParametersIdentity(t *testing.T) {
// Per the MLDSA*() docs, repeated calls return the same value, suitable for
// equality checks and switch statements.
if MLDSA44() != MLDSA44() || MLDSA65() != MLDSA65() || MLDSA87() != MLDSA87() {
t.Errorf("MLDSA*() returned different values across calls")
}
if MLDSA44() == MLDSA65() || MLDSA65() == MLDSA87() || MLDSA44() == MLDSA87() {
t.Errorf("distinct parameter sets compare equal")
}
}
// computeMu reproduces μ = SHAKE256(SHAKE256(pk, 64) || 0x00 || ctxlen || ctx ||
// msg, 64) per FIPS 204, used to drive the External μ signing path.
func computeMu(pk, ctx, msg []byte) []byte {
tr := sha3.NewSHAKE256()
tr.Write(pk)
trOut := make([]byte, 64)
tr.Read(trOut)
h := sha3.NewSHAKE256()
h.Write(trOut)
h.Write([]byte{0x00, byte(len(ctx))})
h.Write(ctx)
h.Write(msg)
out := make([]byte, 64)
h.Read(out)
return out
}
// fakeSignerOpts is a [crypto.SignerOpts] whose [HashFunc] returns h, used to
// exercise the opts-dispatch paths in [PrivateKey.Sign] without going through
// [Options].
type fakeSignerOpts struct{ h crypto.Hash }
func (f fakeSignerOpts) HashFunc() crypto.Hash { return f.h }
func TestSign(t *testing.T) {
testAllParameters(t, func(t *testing.T, params Parameters) {
sk, err := GenerateKey(params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
pk := sk.PublicKey()
msg := []byte("test message")
// nil opts and &Options{} must be equivalent (and both interoperable
// with a nil/zero Verify opts).
sig1, err := sk.Sign(nil, msg, nil)
if err != nil {
t.Fatalf("Sign(nil opts): %v", err)
}
if got := len(sig1); got != params.SignatureSize() {
t.Errorf("len(sig) = %d, want %d", got, params.SignatureSize())
}
if err := Verify(pk, msg, sig1, nil); err != nil {
t.Errorf("Verify of nil-opts signature with nil opts: %v", err)
}
if err := Verify(pk, msg, sig1, &Options{}); err != nil {
t.Errorf("Verify of nil-opts signature with empty Options: %v", err)
}
sig2, err := sk.Sign(nil, msg, &Options{})
if err != nil {
t.Fatalf("Sign(&Options{}): %v", err)
}
if err := Verify(pk, msg, sig2, nil); err != nil {
t.Errorf("Verify of empty-Options signature with nil opts: %v", err)
}
// A non-*Options crypto.SignerOpts whose HashFunc returns 0 must
// also sign directly, with empty context.
sig3, err := sk.Sign(nil, msg, fakeSignerOpts{h: 0})
if err != nil {
t.Fatalf("Sign(fakeSignerOpts{0}): %v", err)
}
if err := Verify(pk, msg, sig3, nil); err != nil {
t.Errorf("Verify of fake-opts signature: %v", err)
}
// crypto.Hash(0) similarly: HashFunc returns 0.
sig4, err := sk.Sign(nil, msg, crypto.Hash(0))
if err != nil {
t.Fatalf("Sign(crypto.Hash(0)): %v", err)
}
if err := Verify(pk, msg, sig4, nil); err != nil {
t.Errorf("Verify of Hash(0)-opts signature: %v", err)
}
// A wrong HashFunc must produce errInvalidSignerOpts.
if _, err := sk.Sign(nil, msg, crypto.SHA256); err == nil {
t.Errorf("Sign with crypto.SHA256 opts: want error, got nil")
}
if _, err := sk.SignDeterministic(msg, crypto.SHA256); err == nil {
t.Errorf("SignDeterministic with crypto.SHA256 opts: want error, got nil")
}
// SignDeterministic with nil and &Options{} must agree byte-for-byte.
detA, err := sk.SignDeterministic(msg, nil)
if err != nil {
t.Fatalf("SignDeterministic(nil): %v", err)
}
detB, err := sk.SignDeterministic(msg, &Options{})
if err != nil {
t.Fatalf("SignDeterministic(&Options{}): %v", err)
}
if !bytes.Equal(detA, detB) {
t.Errorf("SignDeterministic with nil and &Options{} differ")
}
// A different Context produces a different deterministic signature
// and verification with a mismatched context must fail.
detCtx, err := sk.SignDeterministic(msg, &Options{Context: "ctx"})
if err != nil {
t.Fatalf("SignDeterministic(ctx): %v", err)
}
if string(detCtx) == string(detA) {
t.Errorf("SignDeterministic with empty and non-empty context match")
}
if err := Verify(pk, msg, detCtx, nil); err == nil {
t.Errorf("Verify of context signature with empty context: want error, got nil")
}
if err := Verify(pk, msg, detCtx, &Options{Context: "ctx"}); err != nil {
t.Errorf("Verify with matching context: %v", err)
}
// Context >255 bytes is rejected by the underlying implementation.
longCtx := strings.Repeat("x", 256)
if _, err := sk.Sign(nil, msg, &Options{Context: longCtx}); err == nil {
t.Errorf("Sign with 256-byte context: want error, got nil")
}
if _, err := sk.SignDeterministic(msg, &Options{Context: longCtx}); err == nil {
t.Errorf("SignDeterministic with 256-byte context: want error, got nil")
}
if err := Verify(pk, msg, detA, &Options{Context: longCtx}); err == nil {
t.Errorf("Verify with 256-byte context: want error, got nil")
}
// Tampered signature must not verify.
sigTampered := bytes.Clone(sig1)
sigTampered[len(sigTampered)/2] ^= 0x01
if err := Verify(pk, msg, sigTampered, nil); err == nil {
t.Errorf("Verify of tampered signature: want error, got nil")
}
// Modified message must not verify against the original signature.
msgTampered := bytes.Clone(msg)
msgTampered[0] ^= 0x01
if err := Verify(pk, msgTampered, sig1, nil); err == nil {
t.Errorf("Verify of modified message: want error, got nil")
}
// Signature from a different key must not verify.
skOther, err := GenerateKey(params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
sigOther, err := skOther.SignDeterministic(msg, nil)
if err != nil {
t.Fatalf("SignDeterministic: %v", err)
}
if err := Verify(pk, msg, sigOther, nil); err == nil {
t.Errorf("Verify of signature from a different key: want error, got nil")
}
})
}
func TestExternalMu(t *testing.T) {
testAllParameters(t, func(t *testing.T, params Parameters) {
sk, err := GenerateKey(params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
pk := sk.PublicKey()
pkBytes := pk.Bytes()
msg := []byte("hello mu")
for _, ctx := range []string{"", "ctx"} {
μ := computeMu(pkBytes, []byte(ctx), msg)
sig, err := sk.Sign(nil, μ, crypto.MLDSAMu)
if err != nil {
t.Fatalf("Sign(MLDSAMu, ctx=%q): %v", ctx, err)
}
if err := Verify(pk, msg, sig, &Options{Context: ctx}); err != nil {
t.Errorf("Verify of MLDSAMu signature, ctx=%q: %v", ctx, err)
}
detSig, err := sk.SignDeterministic(μ, crypto.MLDSAMu)
if err != nil {
t.Fatalf("SignDeterministic(MLDSAMu, ctx=%q): %v", ctx, err)
}
if err := Verify(pk, msg, detSig, &Options{Context: ctx}); err != nil {
t.Errorf("Verify of deterministic MLDSAMu signature, ctx=%q: %v", ctx, err)
}
detSig2, err := sk.SignDeterministic(μ, crypto.MLDSAMu)
if err != nil {
t.Fatalf("SignDeterministic(MLDSAMu) second call: %v", err)
}
if string(detSig) != string(detSig2) {
t.Errorf("SignDeterministic(MLDSAMu) is not deterministic")
}
}
// Cross-context: μ computed under one ctx must not verify under another.
μA := computeMu(pkBytes, []byte("a"), msg)
sigA, err := sk.Sign(nil, μA, crypto.MLDSAMu)
if err != nil {
t.Fatalf("Sign(MLDSAMu, ctx=a): %v", err)
}
if err := Verify(pk, msg, sigA, &Options{Context: "b"}); err == nil {
t.Errorf("Verify of MLDSAMu(ctx=a) signature with ctx=b: want error, got nil")
}
// Tampered MLDSAMu signature must not verify.
sigTampered := bytes.Clone(sigA)
sigTampered[len(sigTampered)/2] ^= 0x01
if err := Verify(pk, msg, sigTampered, &Options{Context: "a"}); err == nil {
t.Errorf("Verify of tampered MLDSAMu signature: want error, got nil")
}
// Wrong-length μ must be rejected.
if _, err := sk.Sign(nil, make([]byte, 32), crypto.MLDSAMu); err == nil {
t.Errorf("Sign(MLDSAMu) with 32-byte input: want error, got nil")
}
if _, err := sk.SignDeterministic(make([]byte, 32), crypto.MLDSAMu); err == nil {
t.Errorf("SignDeterministic(MLDSAMu) with 32-byte input: want error, got nil")
}
})
}
func TestPublicKey(t *testing.T) {
cases := []struct {
params Parameters
name string
pkSize int
sigSize int
}{
{MLDSA44(), "ML-DSA-44", MLDSA44PublicKeySize, MLDSA44SignatureSize},
{MLDSA65(), "ML-DSA-65", MLDSA65PublicKeySize, MLDSA65SignatureSize},
{MLDSA87(), "ML-DSA-87", MLDSA87PublicKeySize, MLDSA87SignatureSize},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := tc.params.String(); got != tc.name {
t.Errorf("Parameters.String() = %q, want %q", got, tc.name)
}
if got := tc.params.PublicKeySize(); got != tc.pkSize {
t.Errorf("Parameters.PublicKeySize() = %d, want %d", got, tc.pkSize)
}
if got := tc.params.SignatureSize(); got != tc.sigSize {
t.Errorf("Parameters.SignatureSize() = %d, want %d", got, tc.sigSize)
}
sk, err := GenerateKey(tc.params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
pk := sk.PublicKey()
if got := pk.Parameters(); got != tc.params {
t.Errorf("PublicKey.Parameters() = %v, want %v", got, tc.params)
}
if got := len(pk.Bytes()); got != tc.params.PublicKeySize() {
t.Errorf("len(PublicKey.Bytes()) = %d, want %d", got, tc.params.PublicKeySize())
}
if got := len(sk.Bytes()); got != PrivateKeySize {
t.Errorf("len(PrivateKey.Bytes()) = %d, want %d", got, PrivateKeySize)
}
// Public() returns the same key as PublicKey().
anyPub := sk.Public()
pub2, ok := anyPub.(*PublicKey)
if !ok {
t.Fatalf("PrivateKey.Public() = %T, want *PublicKey", anyPub)
}
if !pk.Equal(pub2) {
t.Errorf("PrivateKey.Public() does not equal PublicKey()")
}
// Round-trip via NewPrivateKey/NewPublicKey.
sk2, err := NewPrivateKey(tc.params, sk.Bytes())
if err != nil {
t.Fatalf("NewPrivateKey round-trip: %v", err)
}
if !sk.Equal(sk2) {
t.Errorf("PrivateKey round-trip not equal")
}
pk2, err := NewPublicKey(tc.params, pk.Bytes())
if err != nil {
t.Fatalf("NewPublicKey round-trip: %v", err)
}
if !pk.Equal(pk2) {
t.Errorf("PublicKey round-trip not equal")
}
})
}
}
func TestEqualWrongType(t *testing.T) {
sk, err := GenerateKey(MLDSA44())
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
if sk.Equal("not a key") {
t.Errorf("PrivateKey.Equal(string) = true, want false")
}
if sk.Equal((*PublicKey)(nil)) {
t.Errorf("PrivateKey.Equal(*PublicKey) = true, want false")
}
if sk.PublicKey().Equal("not a key") {
t.Errorf("PublicKey.Equal(string) = true, want false")
}
if sk.PublicKey().Equal((*PrivateKey)(nil)) {
t.Errorf("PublicKey.Equal(*PrivateKey) = true, want false")
}
// Distinct keys are not Equal.
sk2, err := GenerateKey(MLDSA44())
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
if sk.Equal(sk2) {
t.Errorf("two random PrivateKeys are Equal")
}
if sk.PublicKey().Equal(sk2.PublicKey()) {
t.Errorf("two random PublicKeys are Equal")
}
}
func TestInvalidParameters(t *testing.T) {
var zero Parameters
if _, err := GenerateKey(zero); err == nil {
t.Errorf("GenerateKey(zero Parameters): want error, got nil")
}
if _, err := NewPrivateKey(zero, make([]byte, PrivateKeySize)); err == nil {
t.Errorf("NewPrivateKey(zero Parameters): want error, got nil")
}
if _, err := NewPublicKey(zero, make([]byte, MLDSA44PublicKeySize)); err == nil {
t.Errorf("NewPublicKey(zero Parameters): want error, got nil")
}
}
func TestInvalidSize(t *testing.T) {
testAllParameters(t, func(t *testing.T, params Parameters) {
if _, err := NewPrivateKey(params, make([]byte, PrivateKeySize-1)); err == nil {
t.Errorf("NewPrivateKey with short seed: want error, got nil")
}
if _, err := NewPrivateKey(params, make([]byte, PrivateKeySize+1)); err == nil {
t.Errorf("NewPrivateKey with long seed: want error, got nil")
}
if _, err := NewPublicKey(params, make([]byte, params.PublicKeySize()-1)); err == nil {
t.Errorf("NewPublicKey with short encoding: want error, got nil")
}
if _, err := NewPublicKey(params, make([]byte, params.PublicKeySize()+1)); err == nil {
t.Errorf("NewPublicKey with long encoding: want error, got nil")
}
sk, err := GenerateKey(params)
if err != nil {
t.Fatalf("GenerateKey: %v", err)
}
msg := []byte("test message")
sig, err := sk.SignDeterministic(msg, nil)
if err != nil {
t.Fatalf("SignDeterministic: %v", err)
}
if err := Verify(sk.PublicKey(), msg, sig[:len(sig)-1], nil); err == nil {
t.Errorf("Verify with short signature: want error, got nil")
}
if err := Verify(sk.PublicKey(), msg, append(sig, 0), nil); err == nil {
t.Errorf("Verify with long signature: want error, got nil")
}
})
// Cross-parameter mismatch: an MLDSA65 public key encoding is rejected by
// MLDSA44 (and vice versa), because the lengths differ.
sk65, err := GenerateKey(MLDSA65())
if err != nil {
t.Fatalf("GenerateKey(MLDSA65): %v", err)
}
if _, err := NewPublicKey(MLDSA44(), sk65.PublicKey().Bytes()); err == nil {
t.Errorf("NewPublicKey(MLDSA44, MLDSA65 encoding): want error, got nil")
}
}
func BenchmarkSign(b *testing.B) {
// Signing works by rejection sampling, which introduces massive variance in
// individual signing times. To get stable but correct results, we benchmark
// a series of representative operations, engineered to have the same
// distribution of rejection counts and reasons as the average case. See also
// https://words.filippo.io/rsa-keygen-bench/ for a similar approach.
b.Run("ML-DSA-44", func(b *testing.B) {
benchmarkSign(b, MLDSA44(), benchmarkMessagesMLDSA44)
})
b.Run("ML-DSA-65", func(b *testing.B) {
benchmarkSign(b, MLDSA65(), benchmarkMessagesMLDSA65)
})
b.Run("ML-DSA-87", func(b *testing.B) {
benchmarkSign(b, MLDSA87(), benchmarkMessagesMLDSA87)
})
}
func benchmarkSign(b *testing.B, params Parameters, messages []string) {
seed := make([]byte, 32)
priv, err := NewPrivateKey(params, seed)
if err != nil {
b.Fatalf("NewPrivateKey: %v", err)
}
rand.Shuffle(len(messages), func(i, j int) {
messages[i], messages[j] = messages[j], messages[i]
})
i := 0
for b.Loop() {
msg := messages[i]
if i++; i >= len(messages) {
i = 0
}
priv.SignDeterministic([]byte(msg), nil)
}
}
func BenchmarkVerify(b *testing.B) {
b.Run("ML-DSA-44", func(b *testing.B) {
benchmarkVerify(b, MLDSA44())
})
b.Run("ML-DSA-65", func(b *testing.B) {
benchmarkVerify(b, MLDSA65())
})
b.Run("ML-DSA-87", func(b *testing.B) {
benchmarkVerify(b, MLDSA87())
})
}
func benchmarkVerify(b *testing.B, params Parameters) {
priv, err := GenerateKey(params)
if err != nil {
b.Fatalf("GenerateKey: %v", err)
}
msg := make([]byte, 128)
sig, err := priv.SignDeterministic(msg, &Options{Context: "context"})
if err != nil {
b.Fatalf("SignDeterministic: %v", err)
}
pub := priv.PublicKey().Bytes()
// "Whole" runs both public key parsing and signature verification,
// since pre-computation can be easily moved between the two, but in practice
// most uses of verification are for fresh public keys (unlike signing).
b.Run("Whole", func(b *testing.B) {
for b.Loop() {
pk, err := NewPublicKey(params, pub)
if err != nil {
b.Fatalf("NewPublicKey: %v", err)
}
if err := Verify(pk, msg, sig, &Options{Context: "context"}); err != nil {
b.Fatalf("Verify: %v", err)
}
}
})
// "Precomputed" runs only Verify with a pre-parsed public key.
b.Run("Precomputed", func(b *testing.B) {
pk, err := NewPublicKey(params, pub)
if err != nil {
b.Fatalf("NewPublicKey: %v", err)
}
for b.Loop() {
if err := Verify(pk, msg, sig, &Options{Context: "context"}); err != nil {
b.Fatalf("Verify: %v", err)
}
}
})
}
func BenchmarkKeygen(b *testing.B) {
b.Run("ML-DSA-44", func(b *testing.B) {
for b.Loop() {
NewPrivateKey(MLDSA44(), make([]byte, 32))
}
})
b.Run("ML-DSA-65", func(b *testing.B) {
for b.Loop() {
NewPrivateKey(MLDSA65(), make([]byte, 32))
}
})
b.Run("ML-DSA-87", func(b *testing.B) {
for b.Loop() {
NewPrivateKey(MLDSA87(), make([]byte, 32))
}
})
}
var benchmarkMessagesMLDSA44 = []string{
"BUS7IAZWYOZ4JHJQYDWRTJL4V7",
"MK5HFFNP4TB5S6FM4KUFZSIXPD",
"DBFETUV4O56J57FXTXTIVCDIAR",
"I4FCMZ7UNLYAE2VVPKTE5ETXKL",
"56U76XRPOVFX3AU7MB2JHAP6JX",
"3ER6UPKIIDGCXLGLPU7KI3ODTN",
"JPQDX2IL3W5CYAFRZ4XUJOHQ3G",
"6AJOEI33Z3MLEBVC2Q67AYWK5L",
"WE3U36HYOPJ72RN3C74F6IOTTJ",
"NMPF5I3B2BKQG5RK26LMPQECCX",
"JRGAN2FA6IY7ESFGZ7PVI2RGWA",
"UIKLF6KNSIUHIIVNRKNUFRNR4W",
"HA252APFYUWHSZZFKP7CWGIBRY",
"JFY774TXRITQ6CIR56P2ZOTOL6",
"ZASYLW5Y3RAOC5NDZ2NCH5A4UY",
"42X4JXNPXMFRCFAE5AKR7XTFO7",
"YAHQUWUH534MUI2TYEKQR7VR3A",
"HBP7FGEXGSOZ5HNOVRGXZJU2KG",
"HG4O7DCRMYMQXASFLMYQ6NMIXK",
"2KPQMDZKS65CLJU4DHTMVV5WI3",
"G6YSUTEX4HHL44ISK2JVVK45BV",
"PUJGPEQUBQM3IK2EXDQFJ2WGBG",
"PNS6HMQAWA3RORSMSNEUAINMIR",
"L35MZS4XYIJK453OFXCZG4WHIK",
"CRY54YZMFRF6JTB3FPNNBWPUOG",
"Y25TSZBWGU4HJCRMWZHAWXQ2DN",
"23W64TW3AKZPKCM4HMKEHFI6VQ",
"PWQAOZ24B4VLNEQR4XKN7LZHDI",
"YINPDR3ZSAKPPXP6J6VAXHIPYO",
"JDBB52ZRAB3PYBPNE7P4COY5PJ",
"4DYU52LQLVG3LTREOTLBCJK3XC",
"AB45MV6RKUGPCW4EUK7DX23MJX",
"HEJSITE5K7J6YJ74OEATVTCERV",
"ZKI5QCFCGM26UK7F5KYTENXKD2",
"VH5G3ZLF5XC22QAEJ6JDGOBE5Y",
"HYGXFHH3JW5SENG26MXLL54IGV",
"MJUCRL36JZ757UYHBFPCJBPZRH",
"IBH3T6NAVLCJQBYSVHAQFUITYA",
"VMWCS7JMIMFQB6TPRAMOUXIKWD",
"SXRPGPNNW2MMBKQS3HJURIQ3XV",
"YPPYMJZW6WYXPSCZIPI57NTP5L",
"N3SH6DUH6UOPU7YMQ6BJJEQSPI",
"Q243DGA6VC6CW66FFUAB5V3VLB",
"OUUBXEU4NJBRN5XZJ7YQUPIZLA",
"H5TWHVGC7FXG6MCKJQURD3RNWG",
"OONG2ZZ7H3P5BREEEURNJHBBQG",
"HWROSSRTBCQOAIQAY5S4EQG4FX",
"AJW6PW62JQNU72VKGIQMPBX64C",
"OXECVUVAWBBBXGGQGQBTYVEP4S",
"M5XN6V2LQJDEIN3G4Z6WJO6AVT",
"NHGJUX3WGRTEIRPFWC2I467ST4",
"SEOADTJDKAYYLDSC4VAES2CRDJ",
"J5AT674S577ZFGEURNIAGYOHKW",
"VJQVNMGHG4ITFX2XSPSDEWVZWD",
"ZWY3KJPXTAVWWVHNAJDUXZ52TG",
"HY46PBUGP4EMH34C6Q56MO7CJP",
"MQTUO7CF6R6CRJPVV6F673M6VW",
"35Z2Z5KV2RBJPQ7OZ24ZJE6BKR",
"OVUEVXBLCU2BBY25QP5WJACDIX",
"LNJX7PCLYL35WYJBW6CTXENPUU",
"IH7E766LCENOQ5ZKZVCMLEPACU",
"T2HZFGDDSFQ6YADB52NIFLBFEV",
"RHQUJMN4MB5SYY4FP4ARZH52QJ",
"W7GZC5ZM63UF2EJ7OC4WJM3OTH",
"T2NHNFVOMICY33AQZSR53HXFQ6",
"7ZVB4Y4K4Y2VAM5NC7HHAJNZIB",
"UX2I4VF62XJGP2XTNN6LDKXTOH",
"HJAMJR5RQTQW7JMW7ZLPRBZE7E",
"HKWSKX7MB5346PHYNWNBAYDSYK",
"BVWSB75HFLLE45MWA6EPHPTCFR",
"YDH2J6NMM7UINHGUOPIUI7PSSR",
"SYQPZLK52HMUAQFMVHGRJYKBEY",
"7AA6UQFGSPBGNUDPLWXSGNKKPP",
"AYXRJGRWZ5S3QOEDVWYHHCICHV",
"KFJYAWO7IATSBCSTDUAA5EPFAN",
"3JABTLB6T2ICHGVT3HXZZ3OAIT",
"WCM3IBOCQJ36WSG627CCNK3QA7",
"5FB5H3BZN2J4RGR2DUW7M37NKZ",
"VKDDAD3BVOMPSNEDGIRHKX5S6R",
"LFH5HVUR726OSFD3YVYM3ZHEIH",
"Y4ETQB2KZVFB4M7SALLCTHX2FB",
"E6SAU3C25MO2WBBVBKCKP2N4ZE",
"3JA54Q3NEKURB5EAPL2FOFIESD",
"FZPBW7BIQIW3FTKQD4TLKNWLMD",
"LY5W6XFA2ZRI53FTUJYGWZ5RX6",
"QID236JY3ICR55O5YRED33O7YT",
"HDRU3L6MFEBCBQFNLF5IRPMOAL",
"232ANKJBDBG4TSKQ7GJMWTHT23",
"CDWE3CELZM5AOJGYEFHMUNSP5O",
"7LNJRBOKN6W7RXUU34MDJ2SNKL",
"S3IZOADTW2A6E5IGRO5WKX7FVH",
"ZAISTLXC55EBMTN6KZ6QX5S7OS",
"4Z5ZIVCMFR2PY2PY4Z47T4YPYA",
"NE36L53Z6AMYQU7Q5REFUF76MK",
"WND5UP5M6KWPBRFP5WIWTOWV3I",
"7OC54DLFWMADJEMKEJ3Y2FMMZS",
"BWJVZHGEN43ULNIOZCPZOB64HG",
"VDFPQSR7RE54A75GT4JDZY5JK2",
"HFCD5EPBZBSVMXIDA47DZ6MRD6",
"RNBVFIUUJUM7EHRE3VNWSTORGO",
"VO5NLQJBR22CRRYUETGTU6JLMR",
"RZOMNFHBTL6HMGWH4PEEDASK7U",
"QL73UBTOLK5O2TW43YWAIKS6T3",
"NE3QVSMWS5G3W5C3BMKTJNMI2L",
"YHI6EYQ4GZMB2QPGHPUG2ZUOEL",
"6MBATW7MFNRUQBFD3GM35B7YPM",
"AIYRY6P5T4XU44CGVPEV6W43FR",
"MIAQ2FHXMAPY5NXSS45VRDPRMG",
"2SNLHQYKK2K6NSWOF6KPGZ3CPC",
"RVBHIQO5LH77ZWEAO3SVL72M2V",
"XXTGJCJNRSNLE7ARAH2UU6LVKR",
"DQMGILY5IDMWN5OYQYYXH26ZGR",
"627VTXXMM455KMTFNUUTKNFXPY",
"HC7IBFGLZCWGUR4K7REPMPW6W4",
"CHL6JRQUS7D4NML3PFT37PPZAA",
"Y767HXJAGJ75KE3JLO4DTLQIXC",
"NTIODXI5I7TF2KXXWXOAYGT7G4",
"PKZYEK2WAI4D4HEYYZH6H5IOMP",
"FG6J6G7HZDEDF4JQBQOTC7RQGZ",
"3VHM2VZU77Y25E3UUYZJLB2QLA",
"WRZQJQW7ARH4DXYHVLCJ4HRTTB",
"LQXKV5HD2AZHENSJ2VFLJ5YU5L",
"MF6Q4OA2EN6TG6BUDK7RWCQNPU",
"3USKYKPC5CB3EC4ZRMZVE3R2UO",
"3WICO2GVS3IRBFUHNDLNKWVP7N",
"P6ZR2UZZOVUZKT4KUS5WICW5XE",
"PYPZUU76RYVOUZGUUX33HLDKYA",
"2FTSURHV34VYTVIUU7W6V5C3NK",
"YABDYMGXS2MD2CYF3S4ALG4FLG",
"MHIBDH25RRPWV3P4VAWT6SAX3I",
"OINSMWJQ2UTOOKZ3X6ICXXBQR7",
"PFTQS7JNU2Q3Q6L4CGBXVLOYNE",
"A4MZ7CCVYQUDJ2AFHNXBBQ3D24",
"CPUB5R3ORTCMSMCLUQURE6AN5O",
"NF5E7U3DFTXWFFXXHUXTEP4VZQ",
"AWB5WDFERWSSJG53YGJMDORQKR",
"U5JQUILKD6SEL6LXAMNFZP6VSW",
"M45NLOAFLO74EJKG5EXNET6J5Y",
"P2KTEUMZ5DZZMYSPOHDR2WJXAN",
"KVO7AXZNFBUBPYLOTZQQ42TFNS",
"WGJJ7SAEV6SBBWWYS4BTLD63WM",
"Y6GURVDV4ESRBPWSTV25T4PE4K",
"ESK7MPFPUZ5ZAQ52RP4SQIYCCC",
"623M3CIABZ3RANERQ2IREXAVYO",
"OQ4CQCFO42RS4BMMSGSDLUTOQO",
"AMFHRDVGM6G2TIR3TKIFGFSDVM",
"7VVSGGCVC53PLOYG7YHPFUJM5X",
"Z3HMESVL7EZUSZNZ33WXEBHA2N",
"AWWVRQD5W7IBSQPS26XOJVDV5H",
"OQBZ5ZST3U3NZYHSIWRNROIG6L",
"II573BW7DJLBYJSPSYIABQWDZD",
"MOKXOQFOCUCLQQH4UKH2DPE7VN",
"XR54NGUOU6BBUUTINNWBPJ35HX",
"DNK36COZGFXI6DY7WLCNUETIRT",
"R5M2PV7E3EHEM3TLGRCL3HSFMC",
"ITKENZQYDQMZFCUPOT7VF3BMU7",
"5GDCB74PPPHEP5N5G3DVRCYT7R",
"ZMKXVRPLI5PY5BDVEPOA3NQZGN",
"GBLIALWTHTUDTOMDERQFVB77CS",
"VKRTTXUTFOK4PJAQQZCCT7TV3T",
"ZJBUJJ4SW62BXOID3XO2W2M2PF",
"SKWT5T6QJTCD3FCINIK22KMVBJ",
"EHINNU6L33HRLOOJ3A2XFJSYQL",
"N4HRQJEFPAT5SU3YPO74WSMQIR",
"TGPTZ3ENMFWB5CZKJFR5WHIRI4",
"O4HNFTAUJJ2LZPQXPXRAXOVABA",
"4JVB5STP2YG5GYOXDWIF4KCKFB",
"MY554X3YZHBECLHNNZ7A3SPJTU",
"ASCJMAH7VCQAD2QJSWXPSVSM3H",
"NBNGL5DZ623KCG2JNZFGZMZ7KD",
"KGMZSW35AEQOJ6FA7IR7BHZI52",
"Q7QUHHS4OJFMJ4I3FY6TDKSMZQ",
"MZAE7TOEXAS76T7KIC73FEYRU4",
"2BVESR3REAWADCGYOYM7T646RG",
"EK3L2ORP4LT3HU3EMXDSQWFOKJ",
"3X4A6VMGMIDLVK72FZSDHSERWY",
"I3UHWI6M6HQFRBSQ6W2SABUNUP",
"REKPXW4DIB4MTKMPHN3RBVHVME",
"W37FNFZE35NX65Z7CVQ7L5U4L5",
"4AGYK6U2KP6RAOADCBUDDCBECV",
"IXM4SFQUDW2NOTXZIPWTNGET3F",
"6YE4G3VELF27MN3Z5B4VIQ3XYK",
"LPOZCPZAG3MD47MIWGR4FIOCDH",
"WGREKUL2LD7C7SYGKH7APIY2A6",
"WWW277FKTKUXQMP4BECSRHLWJI",
"UYE4IQPMSTXVQG7EJALKWWEGDN",
"TIV2L5Z6K7SNGNUVWSNKTAF4UE",
"I3FQOAW3PINUK26P62HCX657FO",
}
var benchmarkMessagesMLDSA65 = []string{
"NDGEUBUDWGRJJ3A4UNZZQOEKNL",
"ACGYQUXN4POOFUENCLNCIPHFAZ",
"Z3XETEYKROVJH7SIHOIAYCTO42",
"DXWCVCEFULV7XHRWHJWSEXWES7",
"BCR2D5PNLGFYX6B3QFQFV23JZP",
"2DVP5HNG54ES64QK4D37PWUYTJ",
"UJM4ADPJLURAIQH4XA6QYUGNJ6",
"B5WRCIPK5IVZW52R6TJOKNPKZH",
"7QNL6JTSP62IGX6RCM2NHRMTKK",
"EJSZQYLM7G7AJCGIEVBV2UW7NN",
"UFNA2NKJ3QFWNHHL5CXZ4R5H46",
"QZAXRTT3E4DOGVTJCOTBG3WXQV",
"KH2ETOYZO5UHIHIKATWJMUVG27",
"V5HVVQTOWRXZ2PB4XWXSEKXUN5",
"5LA7NAFI2LESMH533XY45QVCQW",
"SMF4TWPTMJA2Z4F4OVETTLVRAY",
"FWZ5OJAFMLTQRREPYF4VDRPPGI",
"OK3QMNO3OZSKSR6Q4BFVOVRWTH",
"NQOVN6F6AOBOEGMJTVMF67KTIJ",
"CCLC4Y6YT3AQ3HGT2QNSYAUGNV",
"CAZJHCHBUYQ6OKZ7DMWMDDLIZQ",
"LVW5XDTHPKOW5D452SYD7AFO6Q",
"EYA6O6FTYPC6TRKZPRPX5N2KQ4",
"Z6SGAEZ2SAAZHPQO7GL7CUMBAG",
"FKUCKW6JQVF4WQYXUSXYZQMAVY",
"LN2KDF4DANPE4SC4GKJ4BES3IZ",
"AVCRTWB6ALOQHY34XI7NTMP2JH",
"A5WHIS6CBWPCYIEC6N2MBAOEZ6",
"JC2BH476BXUQFIDA6UCR5V4G4F",
"NU6XH6VLSSFHVSRZCYXPFYKYCD",
"GSUXVZBDDYSZYFGXNP6AZW3PTC",
"XJPRNJ26XP4MIYH2Q7M7MPZ73M",
"INUTUP3IRFWIIT23DNFTIYKCFY",
"T4KH7HKLEYGXHBIRFGFCRUZCC4",
"GGQX4JFVWZHE5Y73YTLMSSOXNS",
"BUA4Q3TQZGLVHMMJU62GQOSHLV",
"WXW3SJXLSZO2MYF4YFIMXL2IQP",
"Q32XBVVGFQTSXAIDJE6XSEPRZG",
"6TEXT6SA7INRCTDSCSVZJEQ2YG",
"ZBN4UL43C3SJIG4HYR236PXCVS",
"TVWPLLC7NROBREWOM75VA3XCR3",
"CCDGL2FURLBABQ4IJBYCB75JFR",
"XBZGCOVTZHCPAARBTMAKPIE6GJ",
"TPRAENJ7I54XRIVH6LL6FDIA3I",
"RKOM3PHFILPIIQZL4ILQWGRYWI",
"CEEZIZ2WUXHQQFATYYGQ3ZDBTI",
"SLKOVAP6WLIVJBVU7VZG3ZGEOW",
"TWMCLJJSWEEQQPQGGDKEJ5SU2R",
"IFMUXXCD2LC7IGQLZ2QEK5UOQ2",
"C7IWFEBHW2CXN4XBJS7VLWH3VK",
"7KJYUEW3F264727TM4LE6RMGDO",
"BPG2XAPBMBTA4VMPUM7IZVZPK3",
"Y5X577BWRZNPLNUHJVSKGMUXYB",
"ZCKMKM23E4IUPTNQDFN2LTLZVX",
"4RKK223JNBDAP4G5DOAHHZ3VNO",
"5UZ3TQZHZT22ISTB4WJEVO6MC4",
"YMVS4HFSJ32CRZRL23PXZUEJFJ",
"UQEUJUTPSZLZARNBXWMCTMHPFF",
"CZAAZ5WK7EIPMW7NA3EZNNBF45",
"227PBHH23WM7F2QLEZSPFYXVW4",
"YUYS2J5CRFXZ4J4KJT2ZKIZVW3",
"MFLHZJOZV44SN4AH6OJ3QZWM2O",
"H2B3CRBCXYN7QWDGYUPHQZP23A",
"T4L6YWQUQ3CTACENAJ5WUXZWFH",
"N723H6MUGPZSRZ72C635OD4BP7",
"NI4TUMVA6LQPQV2TXPN4QOIGBZ",
"CQI3S4LSTQASSJJVZXEFPOVW7K",
"ANPY4HJ64LLSB3GK2R4C6WDBS3",
"RGWQCZKQLMT5FZRDE4B3VMASVK",
"Q3WCCF2HA3CA4WWRJBMGBW7WI7",
"2AKJRXFHXLUQPOXPTLSZN5PW4A",
"IJWOOTI4N7RWXJIHAPXN6KEWEN",
"4D53T6N6ATOVTD4LKSTAAWBJMU",
"B4G5HDD6RITG6NIH6FXCRZDYZM",
"TJCDFKMRUY2OG6KRSMNVCGQFUP",
"PB33IHQKALAY6H6GVBVLI6ZRXK",
"SCCWGW2J5S4WL4FTTMQ435F6DB",
"ZVJH2HSMTLHGXMGPMXLJCKCLLE",
"62LG37U6JXR77YRZQQCDSBHVCS",
"BU4CBWOXQ352TEOKIXO245ID4O",
"UEZOH7KEIODSEVRUF6GMWGA2RB",
"IPJWROME4GM66CGLUWP5BJ4SX6",
"355GDC7TG64AZJ7IJX6K62KZCZ",
"AHTFKX3V7XUB3EWOMQVCGZYGUE",
"N4RV2GKXJ4SPHHJ52Z7K5EGLER",
"ZY7V7NE5F66XHDHWM6YNFEWZA6",
"DIKFO5KAVT4WAP7BOEFM56ZUSR",
"4TDFOFKDAPIOM3MU5GD7NPXNWQ",
"AD7YZO756HDK6YWFILAKW3JWA7",
"NUA53JS2ZK2BGHH3A7BJTJZYW7",
"QLCNC3AQNKLRMSYR62WQSQP5VI",
"SJ7OBS7ZYXSGXOYXPE5KW2XKN6",
"44HBMOGMIMJS63CEXQU7FCXE2E",
"KCK3J7ZL6QF4SLHHSWTJURK7PG",
"HLH4CLUGBSOOBSS3BPO62N5MC3",
"3FNS4GITO6OEUBAVDDXK4WOBTD",
"IAC3K3I4AQGY3G6UHG7PL2N6TE",
"KUKLNH74POJI5DYAEWUD7RABTQ",
"ETM6N7VU3GBSQ7P5MCD6UF3E3S",
"IZITM5NYBGJZLSI3BI4VEMW43U",
"46OPQU4LL6N3Z2U7KYPKUMBAGI",
"EV7YZ5DMAV7VKYJQUFSRD37GPP",
"AV7W2PGYDJIAKLFVEBL6BXQSGC",
"M2FOX5QZEZKV4QXKPI5XUZDHEM",
"R4IFPLVMOVYCHRTR6LXAUGP3LL",
"JGH6XJUMP4DRVAM27P2JNOKXVO",
"D2XN3ZLLU6VFPMDYM7NBHSQEOI",
"2PO3BYENOMQK6SHQDCFSRPJQI3",
"IBVQ7U3QEUC6PQRE4PV53JTZTK",
"ZBCOX4P7NG2IXXFB2R43MG2SLV",
"5NJDPQVVDO7ADNZ2CV7L6QBNGZ",
"V7ASFIIYUMXFGW4B7ZM6LOGUTE",
"PX5IJZ7W2LUPKM6YN4PMZ43ZLM",
"AYK7SZ23DHC7Q56MWAJXBG76LB",
"UYCAPXJM4HNGKLIDSZ4NCEDJLN",
"UWMDZ3C2ODLACKGJPGETNQ3TA4",
"Q6OI6R3WYYJ4CCZCDJBQMCRCZR",
"LCMJHLP7354APCEGPKE7HHWTWB",
"N7T7ZKOYPAMEYTTDOWZNCN6PRD",
"UZADPU4UNHAF7L7LQDMTKA2EQH",
"DC2OEPQDECVLRVNNCS6BMH4CRA",
"37IZ427XHUMZ66EJ62U2YEZDAC",
"6BCZDQZDPZLS5OGESKNUBPSSFV",
"ST2LEMJ4OLQ32TJTLH2WCWT4WA",
"GA2TL4SFLEW4G2B5PQMIKJT5XG",
"L7PPBIET26EH7LQTLEFC4I4EIA",
"6YSM7MC2W4DEV6ULAHMX27LH56",
"QL26Z5KZ4YRRG2BXXGDRRLV357",
"677TWRAJ5NSNHCE243POQPEG7K",
"66MEBQJLGAGVXDX3KZ2YFTTVJM",
"6D4VUWAQD6R65ICSDLFAATC67V",
"7GXLD5CNU3TDUQSSW42SHL7B5D",
"RQETUMEBG2ZM2NF2EZAQHGHWWE",
"DCRX5ANWDMXZFIDVAXYLQZYMRN",
"5SDWT7YAF7L4WWANAGYINZAYXH",
"PZILRV7I2S6WKUSHKYRLA2JQY3",
"2G66TK2PZ5MOTAZDN7BFS3LAIH",
"QOLJ3WGJ6JS3FMMXBNTNAIKXVK",
"FMAL67YTHDCCYVZ5CRMN2XJPDN",
"UOTZDXTJKQ3YAIRKHTYNX6G55P",
"X3DLNPJ3V62LRHGEY4DTT35H3R",
"DKU7CHNXPB5QRZVGIQZW46XCKC",
"RAKBD4LQKEDTVDSK3DVTRWG23B",
"INTRA7BWHLVQMBRKBJNUSMF7MU",
"AUYRBNVCOYYHOHUYOOFIZ2FWMD",
"22EJVDEQ7PASLBAMTVKXOQP5RJ",
"3S6NATWA57SFTZEW7UZUOUYAEU",
}
var benchmarkMessagesMLDSA87 = []string{
"LQQPGPNUME6QDNDTQTS4BA7I7M",
"PTYEEJ7RMI6MXNN6PZH222Y6QI",
"R6DTHAADKNMEADDK5ECPNOTOAT",
"S2QM7VDC6UKRQNRETZMNAZ6SJT",
"EYULPTSJORQJCNYNYVHDFN4N3F",
"YETZNHZ75SXFU672VQ5WXYEPV2",
"KTSND3JGA4AN3PCMG4455JEXGR",
"JGE6HK37O6XMWZQZCHFUPNUEXP",
"CRYB2FZD2BYNANBFFO2HRZEHGZ",
"7MLNDZJ7OIEPBJZOMULOMQH2BA",
"4WQCNTIFVSX2DNALMWUKZRA6CI",
"Y5NK4OBDSDWC5WLL27CEEXYYOT",
"C4SSWSPBVCDAWJXH2CDMXR36LH",
"THDBKXRTKWJUGJMAAYTWTFMX7Z",
"NWXPUD4DAA6QOREW4AFFYQYQNG",
"3RQIJXMO7WYHBEBL3G6EOLNZNQ",
"R7JEOHFP2C7O4AVPRPRELXWOMM",
"LU6MWR7SZXVIKS54BY62X67NPA",
"FG2FFM4F2ECKHCSJ75KXK632JP",
"BF76ZDSVVUSYS5KK4FFD22YPS7",
"HCLBWZRLHEMYZLFWHLAN2BKCZ7",
"HGFVS4QC7AWXYPVRSWAK77KTQF",
"LUZ3C53PUUHBWCDJ7WAHK2UT3K",
"Y3WR6SMDUBW34N3MUT7EQYIJCV",
"F2X35AQTXVZBMPXTWNAAH4ZX2W",
"6MKFFDYWD6ZAKS3C6GRCRLZLRF",
"AFMZYYFRHKMQRNKU5UTSKQ74H6",
"TDTN7J3O367OVPWLESRNPLN4M2",
"WYMLD2X6N4CZ2RDOKF5CFTSYTG",
"UNPTSBLJ6HZRNR72T2VEEHCFX2",
"SNCM4R2P27AJOXBS67RMCARS3U",
"OU7QBE5QOXO7CIYTBJR3KOW2WK",
"2NNQOBQKZ2OD4ZAXI3SNEURYUP",
"YQTUPOYBT67XPCHIGKSGSKC3BZ",
"HGB4ZM3G76IXYWWCMVT3HONRIS",
"WZC6QUKRZZ2TOVA277JYKQITEW",
"XO2WT46A5HYL6CUJF7SGJ6YWOG",
"4QJA35PMYQIDRZ7ZHG7RLZJVGF",
"BMJZELWZ4I2UWXESU3NR6ATC4M",
"XWLFB7FN6D5PRY6YUXC5JUIBFM",
"WRAFFF27AVTIOYIBYA2IPTXI3R",
"VOXUTYTN2XZ362OJFO2R53UCUF",
"UHN73ARJ737WUJ6QYEI7U46OPO",
"3Y3K5E2A4ML3VYVNAFWEEIXTSN",
"QMU4322NKPRLE7JBGYFGS36H2S",
"NJAQTNCXPVDICTDVUKTPRCD2AX",
"OC373ZFBNV2H46T6OY3XRPSUHG",
"UBLAS6CDWE3A662MLKP7QDEOCC",
"BKFDLAL2RTPMERYVW3B7UJ5W3H",
"QFKFGXKGW5SAKLBAWQXUWW77OS",
"EJNUQHTLLOVB4ARETOGLY4WUTJ",
"N243OCMVLLAO6I2XLCYOIMQYGY",
"YRRFLWK7ZASUKYX7ZLQMW2PJ6X",
"3DGVPBWD2BIK6KQE65K72DNJNM",
"TJRYMNOAIW33VIHKLJG4GXAVUK",
"6DSRINAYXL34U54U355U7IVFGS",
"6CHA4MX7LVS77XKRWG7IYC3XVL",
"GM2CEGBEPBOHAPIOBUWJ4MJNTG",
"VJKHGBY33VUIJFEQLX3JVUNQBD",
"DTOHAD5M2KL46IZHE4TPLJWHTI",
"IYFG3UDN7ROOY2ZFSLM2BU2LMQ",
"A5OGJHPOE4PW6QSZYHZ5TKPGIC",
"FX4BCN67AEGCLUTLFPNDL3SQU5",
"MWIZQVOZOHTTBUXC3BEX62MNI5",
"BYHVJHBLK4O6LFSKEIQ3CAAKU7",
"QJU7P6KWSSKAA5GVA6RH4OV7MX",
"I3T3XM5Z5TAJHAYDQHFA2ZV7PU",
"L46MQCHV3TJ6FYIQQ2FCJXES74",
"QXZRQIYAJMXYR6PU3VDYGCIT5W",
"MFS53RR2XEYS22NYOJLGTHVTTM",
"FRWIWJRP4AQMXWX4WJ4WYVKM3E",
"X6GK6IGVLJWYSHLKHGXSW3TJDP",
"L5LPJ2HIWA4UY6G6FMZXGDEDAM",
"GD6FYOYUGDHXEQ5S2KLJEGNSN7",
"ODAL7ZRKXSPAAN5DVRBWJQCFQX",
"CV3QFBDXBPT3SCPJGUYSMDN6ZS",
"IGSLSACRZ6XID466KQIB4YNGYO",
"WZ2EACBN26RAML2S52YXRYP2OF",
"LB76VEVNOBYFMKFZ7SDFCBCHQE",
"TLFA7EU3JJFAP6EMUKNV2ZXRBM",
"SIIJF6OXAKRP25CBUYFBRCDDVP",
"TEPNI7TJ7HASJWIQMBS4VFLRQC",
"VK2JINYWEDV7IQFWH4OTAD4W5O",
"GILUH5AMVE4TM7EKPXJBZGT6EJ",
"DV7ALFRAW3TI4WMQQLDTO6RNHN",
"CAIB5G3NXC5ASPLFIWAFPVHS5B",
"MLFJXZUOAGN7EGPMXOOVTB2CL4",
"6MZYT3ANWHBOS67WGHZI3QPEAP",
"LVJDQB52C2PERSSQJRMRCJ4UBF",
"QY4VKAZAYQIZOX2L2VO2QHAQVC",
"UAA5SST2XA76JPKM3XOZ5RUHFI",
"VLZWF53JSQ6SCRUFDKVPXWAS4L",
"NX2DZIKMJIYXUNSAHFP23FHTBU",
"F5OAKDDDA34A2RPIKDPM5CYPMZ",
"E5PEP3ANIK2L4VLOST4NIYNKBD",
"IPBGFLHSMP4UFXF6XJX42T6CAL",
"XHPU7DBFTZB2TX5K34AD6DJTK3",
"2ZU7EJN2DG2UMT6HX5KGS2RFT6",
"SD5S7U34WSE4GBPKVDUDZLBIEH",
"WZFFL3BTQAV4VQMSAGCS45SGG3",
"QE7ZT2LI4CA5DLSVMHV6CP3E3V",
"YIWMS6AS72Z5N2ALZNFGCYC5QL",
"A4QJ5FNY54THAKBOB65K2JBIV7",
"6LORQGA3QO7TNADHEIINQZEE26",
"5V45M6RAKOZDMONYY4DIH3ZBL2",
"SVP7UYIZ5RTLWRKFLCWHAQV3Y2",
"C2UYQL2BBE4VLUJ3IFNFMHAN7O",
"P4DS44LGP2ERZB3OB7JISQKBXA",
"A6B4O5MWALOEHLILSVDOIXHQ4Z",
"DKQJTW5QF7KDZA3IR4X5R5F3CG",
"H6QFQX2C2QTH3YKEOO57SQS23J",
"DIF373ML2RWZMEOIVUHFXKUG7O",
"Z5PPIA3GJ74QXFFCOSUAQMN5YN",
"PM6XIDECSS5S77UXMB55VZHZSE",
}