Skip to content

Commit

Permalink
(Temp) Generate PQC test vectors
Browse files Browse the repository at this point in the history
  • Loading branch information
lubux committed Jan 31, 2025
1 parent 4a0afdc commit 05536d9
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 0 deletions.
6 changes: 6 additions & 0 deletions openpgp/mlkem_ecdh/mlkem_ecdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package mlkem_ecdh

import (
"encoding/hex"
goerrors "errors"
"fmt"
"io"
Expand Down Expand Up @@ -108,6 +109,8 @@ func Encrypt(rand io.Reader, pub *PublicKey, msg []byte) (kEphemeral, ecEphemera
if err != nil {
return nil, nil, nil, err
}
fmt.Printf("sessionKey: %x\n", msg)
fmt.Printf("keyEncryptionKey: %x\n\n", keyEncryptionKey)

if ciphertext, err = keywrap.Wrap(keyEncryptionKey, msg); err != nil {
return nil, nil, nil, err
Expand Down Expand Up @@ -158,6 +161,9 @@ func buildKey(pub *PublicKey, eccSecretPoint, eccEphemeral, eccPublicKey, mlkemK
// eccEphemeral - the ECDH ciphertext encoded as an octet string
// eccPublicKey - The ECDH public key of the recipient as an octet string

fmt.Printf("ecdh key share: %s\n", hex.EncodeToString(eccKeyShare))
fmt.Printf("ml-kem key share: %s\n", hex.EncodeToString(mlkemKeyShare))

// SHA3-256(mlkemKeyShare || eccKeyShare || eccEphemeral || eccPublicKey ||
// mlkemEphemeral || mlkemPublicKey || algId || "OpenPGPCompositeKDFv1")
h := sha3.New256()
Expand Down
28 changes: 28 additions & 0 deletions openpgp/v2/keys_v6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package v2
import (
"bytes"
"crypto"
"fmt"
"strings"
"testing"
"time"

"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/packet"
)

Expand Down Expand Up @@ -242,3 +244,29 @@ func TestReadLegacyECC(t *testing.T) {
t.Fatal("should not be able to read v6 legacy ECC key")
}
}

func TestGeneratePQCTestVector(t *testing.T) {
c := &packet.Config{
V6Keys: true,
Algorithm: packet.PubKeyAlgoSlhdsaShake128s,
AEADConfig: &packet.AEADConfig{},
DefaultCipher: packet.CipherAES256,
DefaultHash: crypto.SHA3_256,
}
e, err := NewEntity("PQC user", "Test Key", "[email protected]", c)
if err != nil {
t.Fatal(err)
}

var armoredKey bytes.Buffer
armorer, _ := armor.Encode(&armoredKey, "PGP PRIVATE KEY BLOCK", nil)
e.SerializePrivateWithoutSigning(armorer, nil)
armorer.Close()
fmt.Println(armoredKey.String())

var armoredKeyPublic bytes.Buffer
armorer, _ = armor.Encode(&armoredKeyPublic, "PGP PUBLIC KEY BLOCK", nil)
e.Serialize(armorer)
armorer.Close()
fmt.Println(armoredKeyPublic.String())
}
247 changes: 247 additions & 0 deletions openpgp/v2/pqc_test_vectors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package v2

import (
"bytes"
"crypto"
"fmt"
"testing"
"time"

"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/packet"
)

func dumpTestVector(_ *testing.T, filename, vector string) {
fmt.Printf("Artifact: %s\n%s\n\n", filename, vector)
}

func serializePqSkVector(t *testing.T, filename string, entity *Entity, doChecksum bool) {
var serializedArmoredPrivate bytes.Buffer
serializedPrivate, err := armor.EncodeWithChecksumOption(&serializedArmoredPrivate, PrivateKeyType, nil, doChecksum)
if err != nil {
t.Fatalf("Failed to init armoring: %s", err)
}

if err = entity.SerializePrivate(serializedPrivate, nil); err != nil {
t.Fatalf("Failed to serialize entity: %s", err)
}

if err := serializedPrivate.Close(); err != nil {
t.Fatalf("Failed to close armoring: %s", err)
}

dumpTestVector(t, filename, serializedArmoredPrivate.String())
}

func serializePqPkVector(t *testing.T, filename string, entity *Entity, doChecksum bool) {
var serializedArmoredPublic bytes.Buffer
serializedPublic, err := armor.EncodeWithChecksumOption(&serializedArmoredPublic, PublicKeyType, nil, doChecksum)
if err != nil {
t.Fatalf("Failed to init armoring: %s", err)
}

if err = entity.Serialize(serializedPublic); err != nil {
t.Fatalf("Failed to serialize entity: %s", err)
}

if err := serializedPublic.Close(); err != nil {
t.Fatalf("Failed to close armoring: %s", err)
}

dumpTestVector(t, filename, serializedArmoredPublic.String())
}

func encryptPqcMessageVector(t *testing.T, filename string, entity *Entity, config *packet.Config, doChecksum bool) {
var serializedArmoredMessage bytes.Buffer
serializedMessage, err := armor.EncodeWithChecksumOption(&serializedArmoredMessage, MessageType, nil, doChecksum)
if err != nil {
t.Fatalf("Failed to init armoring: %s", err)
}

w, err := Encrypt(serializedMessage, []*Entity{entity}, nil, []*Entity{entity}, nil /* no hints */, config)
if err != nil {
t.Fatalf("Error in Encrypt: %s", err)
}

const message = "Testing\n"
_, err = w.Write([]byte(message))
if err != nil {
t.Fatalf("Error writing plaintext: %s", err)
}

err = w.Close()
if err != nil {
t.Fatalf("Error closing WriteCloser: %s", err)
}

err = serializedMessage.Close()
if err != nil {
t.Fatalf("Error closing armoring WriteCloser: %s", err)
}

dumpTestVector(t, filename, serializedArmoredMessage.String())
}

func TestV6EddsaPqKey(t *testing.T) {
eddsaConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoEd25519,
V6Keys: true,
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
},
}

entity, err := NewEntity("PQC user", "Test Key", "[email protected]", eddsaConfig)
if err != nil {
t.Fatal(err)
}

kyberConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoMlkem768X25519,
V6Keys: true,
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
},
}

err = entity.AddEncryptionSubkey(kyberConfig)
if err != nil {
t.Fatal(err)
}

serializePqSkVector(t, "v6-eddsa-sample-sk.asc", entity, false)
serializePqPkVector(t, "v6-eddsa-sample-pk.asc", entity, false)

fmt.Printf("Primary fingerprint: %x\n", entity.PrimaryKey.Fingerprint)
for i, subkey := range entity.Subkeys {
fmt.Printf("Sub-key %d fingerprint: %x\n", i, subkey.PublicKey.Fingerprint)
}

configV2 := &packet.Config{
DefaultCipher: packet.CipherAES256,
}

encryptPqcMessageVector(t, "v6-eddsa-sample-message-v2.asc", entity, configV2, false)
}

func TestV6MlDsa65PqKey(t *testing.T) {
eddsaConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoMldsa65Ed25519,
V6Keys: true,
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
},
}

entity, err := NewEntity("PQC user", "Test Key", "[email protected]", eddsaConfig)
if err != nil {
t.Fatal(err)
}

serializePqSkVector(t, "v6-mldsa-65-sample-sk.asc", entity, false)
serializePqPkVector(t, "v6-mldsa-65-sample-pk.asc", entity, false)

fmt.Printf("Primary fingerprint: %x\n", entity.PrimaryKey.Fingerprint)
for i, subkey := range entity.Subkeys {
fmt.Printf("Sub-key %d fingerprint: %x\n", i, subkey.PublicKey.Fingerprint)
}

var configV2 = &packet.Config{
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
}

encryptPqcMessageVector(t, "v6-mldsa-65-sample-message-v2.asc", entity, configV2, false)
}

func TestV6MlDsa87PqKey(t *testing.T) {
eddsaConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoMldsa87Ed448,
V6Keys: true,
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
},
}

entity, err := NewEntity("PQC user", "Test Key", "[email protected]", eddsaConfig)
if err != nil {
t.Fatal(err)
}

serializePqSkVector(t, "v6-mldsa-87-sample-sk.asc", entity, false)
serializePqPkVector(t, "v6-mldsa-87-sample-pk.asc", entity, false)

fmt.Printf("Primary fingerprint: %x\n", entity.PrimaryKey.Fingerprint)
for i, subkey := range entity.Subkeys {
fmt.Printf("Sub-key %d fingerprint: %x\n", i, subkey.PublicKey.Fingerprint)
}

var configV2 = &packet.Config{
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
}

encryptPqcMessageVector(t, "v6-mldsa-87-sample-message-v2.asc", entity, configV2, false)
}

func TestV6SlhDsa128sPqKey(t *testing.T) {
eddsaConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoSlhdsaShake128s,
V6Keys: true,
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
},
}

entity, err := NewEntity("PQC user", "Test Key", "[email protected]", eddsaConfig)
if err != nil {
t.Fatal(err)
}

serializePqSkVector(t, "v6-slhdsa-128s-sample-sk.asc", entity, false)
serializePqPkVector(t, "v6-slhdsa-128s-sample-pk.asc", entity, false)

fmt.Printf("Primary fingerprint: %x\n", entity.PrimaryKey.Fingerprint)
for i, subkey := range entity.Subkeys {
fmt.Printf("Sub-key %d fingerprint: %x\n", i, subkey.PublicKey.Fingerprint)
}

var configV2 = &packet.Config{
DefaultCipher: packet.CipherAES256,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
}

encryptPqcMessageVector(t, "v6-slhdsa-128s-sample-message-v2.asc", entity, configV2, false)
}
Empty file added pqc-test-vectors-jan-2025.txt
Empty file.

0 comments on commit 05536d9

Please sign in to comment.