oracle/go is the Go code at 4c1507c, moved unchanged, plus test-only
generators that record what it computes. tests/vectors holds the
deterministic crypto/store vectors and synthetic fixtures they produce.
Co-Authored-By: Claude <noreply@anthropic.com>
136 lines
3.1 KiB
Go
136 lines
3.1 KiB
Go
// Copyright 2026 The fury Authors.
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package pqcrypt
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"testing"
|
|
)
|
|
|
|
// TestPQRoundTrip exercises the full Encrypt -> Decrypt path through the default
|
|
// post-quantum suite with a 256-bit file key.
|
|
func TestPQRoundTrip(t *testing.T) {
|
|
id, err := GeneratePQIdentity()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := id.Recipient()
|
|
|
|
plaintext := []byte("fury: post-quantum only, hard g like GIF")
|
|
|
|
buf := &bytes.Buffer{}
|
|
w, err := Encrypt(buf, r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := w.Write(plaintext); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
out, err := Decrypt(bytes.NewReader(buf.Bytes()), id)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := io.ReadAll(out)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(got, plaintext) {
|
|
t.Fatalf("round-trip mismatch: got %q want %q", got, plaintext)
|
|
}
|
|
}
|
|
|
|
func TestPQFileKeyIs256Bit(t *testing.T) {
|
|
if fileKeySize != 32 {
|
|
t.Fatalf("fileKeySize = %d, want 32 (256-bit)", fileKeySize)
|
|
}
|
|
}
|
|
|
|
// TestPQWrongIdentity confirms a different identity cannot decrypt.
|
|
func TestPQWrongIdentity(t *testing.T) {
|
|
id, _ := GeneratePQIdentity()
|
|
other, _ := GeneratePQIdentity()
|
|
|
|
buf := &bytes.Buffer{}
|
|
w, err := Encrypt(buf, id.Recipient())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.Write([]byte("secret"))
|
|
w.Close()
|
|
|
|
if _, err := Decrypt(bytes.NewReader(buf.Bytes()), other); err == nil {
|
|
t.Fatal("decryption with wrong identity unexpectedly succeeded")
|
|
}
|
|
}
|
|
|
|
// TestSuiteRegistry checks the default suite is registered and self-describing.
|
|
func TestSuiteRegistry(t *testing.T) {
|
|
s := defaultSuite()
|
|
if s.tag() != "frodo640shake+mlkem768" {
|
|
t.Fatalf("unexpected default suite %q", s.tag())
|
|
}
|
|
if pqSuitesByID[s.id()] != s {
|
|
t.Fatal("suite not registered by id")
|
|
}
|
|
if pqSuitesByTag[s.tag()] != s {
|
|
t.Fatal("suite not registered by tag")
|
|
}
|
|
}
|
|
|
|
// TestSeedDeterministic confirms an identity round-trips through its seed, which
|
|
// is what makes compact passphrase-wrapped identities possible.
|
|
func TestSeedDeterministic(t *testing.T) {
|
|
id, err := GeneratePQIdentity()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id2, err := newPQIdentityFromSeed(defaultSuite(), id.Seed())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(id.pub, id2.pub) || !bytes.Equal(id.priv, id2.priv) {
|
|
t.Fatal("identity not deterministic from seed")
|
|
}
|
|
}
|
|
|
|
func TestIdentityAndRecipientAccessorsReturnCopies(t *testing.T) {
|
|
id, err := GeneratePQIdentity()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
seed := id.Seed()
|
|
seed[0] ^= 0xff
|
|
if id.seed[0] == seed[0] {
|
|
t.Fatal("Seed returned mutable identity internals")
|
|
}
|
|
|
|
inputSeed := id.Seed()
|
|
id2, err := newPQIdentityFromSeed(defaultSuite(), inputSeed)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
inputSeed[0] ^= 0xff
|
|
if id2.seed[0] == inputSeed[0] {
|
|
t.Fatal("newPQIdentityFromSeed retained caller-owned seed slice")
|
|
}
|
|
|
|
recipient := id.Recipient()
|
|
pub := recipient.Bytes()
|
|
pub[0] ^= 0xff
|
|
if recipient.pub[0] == pub[0] {
|
|
t.Fatal("Bytes returned mutable recipient internals")
|
|
}
|
|
|
|
recipient.pub[0] ^= 0xff
|
|
if id.pub[0] == recipient.pub[0] {
|
|
t.Fatal("Recipient returned mutable identity public-key internals")
|
|
}
|
|
}
|