Skip to content

Commit

Permalink
Remove v6 binding for PQC KEMs
Browse files Browse the repository at this point in the history
  • Loading branch information
Aron Wussler committed Nov 5, 2023
1 parent d3f636b commit 419fab5
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 89 deletions.
3 changes: 0 additions & 3 deletions openpgp/key_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,6 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
fallthrough // When passing Dilithium + EdDSA or ECDSA, we generate a Kyber + ECDH subkey
case packet.PubKeyAlgoKyber768X25519, packet.PubKeyAlgoKyber1024X448, packet.PubKeyAlgoKyber768P256,
packet.PubKeyAlgoKyber1024P384, packet.PubKeyAlgoKyber768Brainpool256, packet.PubKeyAlgoKyber1024Brainpool384:
if !config.V6() {
return nil, goerrors.New("openpgp: cannot create a non-v6 kyber_ecdh key")
}

c, err := packet.GetECDHCurveFromAlgID(pubKeyAlgo)
if err != nil {
Expand Down
87 changes: 87 additions & 0 deletions openpgp/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1848,3 +1848,90 @@ mQ00BF00000BCAD0000000000000000000000000000000000000000000000000
000000000000000000000000000000000000ABE000G0Dn000000000000000000iQ00BB0BAgAGBCG00000`
ReadArmoredKeyRing(strings.NewReader(data))
}

func TestAddV4KyberSubkey(t *testing.T) {
eddsaConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoEdDSA,
V6Keys: false,
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
},
}

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

testAddKyberSubkey(t, entity, false)
}


func testAddKyberSubkey(t *testing.T, entity *Entity, v6Keys bool) {
var err error

asymmAlgos := map[string] packet.PublicKeyAlgorithm{
"Kyber768_X25519": packet.PubKeyAlgoKyber768X25519,
"Kyber1024_X448": packet.PubKeyAlgoKyber1024X448,
"Kyber768_P256": packet.PubKeyAlgoKyber768P256,
"Kyber1024_P384":packet.PubKeyAlgoKyber1024P384,
"Kyber768_Brainpool256": packet.PubKeyAlgoKyber768Brainpool256,
"Kyber1024_Brainpool384":packet.PubKeyAlgoKyber1024Brainpool384,
}

for name, algo := range asymmAlgos {
// Remove existing subkeys
entity.Subkeys = []Subkey{}

t.Run(name, func(t *testing.T) {
kyberConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: algo,
V6Keys: v6Keys,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
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)
}

if len(entity.Subkeys) != 1 {
t.Fatalf("Expected 1 subkey, got %d", len(entity.Subkeys))
}

if entity.Subkeys[0].PublicKey.PubKeyAlgo != algo {
t.Fatalf("Expected subkey algorithm: %v, got: %v", packet.PubKeyAlgoEdDSA,
entity.Subkeys[0].PublicKey.PubKeyAlgo)
}

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

read, err := ReadEntity(packet.NewReader(bytes.NewBuffer(serializedEntity.Bytes())))
if err != nil {
t.Fatal(err)
}

if len(read.Subkeys) != 1 {
t.Fatalf("Expected 1 subkey, got %d", len(entity.Subkeys))
}

if read.Subkeys[0].PublicKey.PubKeyAlgo != algo {
t.Fatalf("Expected subkey algorithm: %v, got: %v", packet.PubKeyAlgoEdDSA,
entity.Subkeys[0].PublicKey.PubKeyAlgo)
}
})
}
}
69 changes: 4 additions & 65 deletions openpgp/keys_v6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,11 @@ func testKyberSubkey(t *testing.T, subkey Subkey, randomPassword []byte) {
}


func TestAddKyberSubkey(t *testing.T) {
func TestAddV6KyberSubkey(t *testing.T) {
eddsaConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: packet.PubKeyAlgoEdDSA,
V6Keys: true,
Algorithm: packet.PubKeyAlgoEd25519,
V6Keys: true,
Time: func() time.Time {
parsed, _ := time.Parse("2006-01-02", "2013-07-01")
return parsed
Expand All @@ -344,66 +344,5 @@ func TestAddKyberSubkey(t *testing.T) {
t.Fatal(err)
}

asymmAlgos := map[string] packet.PublicKeyAlgorithm{
"Kyber768_X25519": packet.PubKeyAlgoKyber768X25519,
"Kyber1024_X448": packet.PubKeyAlgoKyber1024X448,
"Kyber768_P256": packet.PubKeyAlgoKyber768P256,
"Kyber1024_P384":packet.PubKeyAlgoKyber1024P384,
"Kyber768_Brainpool256": packet.PubKeyAlgoKyber768Brainpool256,
"Kyber1024_Brainpool384":packet.PubKeyAlgoKyber1024Brainpool384,
}

for name, algo := range asymmAlgos {
// Remove existing subkeys
entity.Subkeys = []Subkey{}

t.Run(name, func(t *testing.T) {
kyberConfig := &packet.Config{
DefaultHash: crypto.SHA512,
Algorithm: algo,
V6Keys: true,
AEADConfig: &packet.AEADConfig{
DefaultMode: packet.AEADModeOCB,
},
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)
}

if len(entity.Subkeys) != 1 {
t.Fatalf("Expected 1 subkey, got %d", len(entity.Subkeys))
}

if entity.Subkeys[0].PublicKey.PubKeyAlgo != algo {
t.Fatalf("Expected subkey algorithm: %v, got: %v", packet.PubKeyAlgoEdDSA,
entity.Subkeys[0].PublicKey.PubKeyAlgo)
}

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

read, err := ReadEntity(packet.NewReader(bytes.NewBuffer(serializedEntity.Bytes())))
if err != nil {
t.Fatal(err)
}

if len(read.Subkeys) != 1 {
t.Fatalf("Expected 1 subkey, got %d", len(entity.Subkeys))
}

if read.Subkeys[0].PublicKey.PubKeyAlgo != algo {
t.Fatalf("Expected subkey algorithm: %v, got: %v", packet.PubKeyAlgoEdDSA,
entity.Subkeys[0].PublicKey.PubKeyAlgo)
}
})
}
testAddKyberSubkey(t, entity, true)
}
47 changes: 29 additions & 18 deletions openpgp/packet/encrypted_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,35 +139,35 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {
return
}
case PubKeyAlgoKyber768X25519:
if err = e.readKyberECDHKey(r, 32, 1088); err != nil {
if cipherFunction, err = e.readKyberECDHKey(r, 32, 1088, e.Version == 6); err != nil {
return err
}
case PubKeyAlgoKyber1024X448:
if err = e.readKyberECDHKey(r, 56, 1568); err != nil {
if cipherFunction, err = e.readKyberECDHKey(r, 56, 1568, e.Version == 6); err != nil {
return err
}
case PubKeyAlgoKyber768P256:
if err = e.readKyberECDHKey(r, 65, 1088); err != nil {
if cipherFunction, err = e.readKyberECDHKey(r, 65, 1088, e.Version == 6); err != nil {
return err
}
case PubKeyAlgoKyber1024P384:
if err = e.readKyberECDHKey(r, 97, 1568); err != nil {
if cipherFunction, err = e.readKyberECDHKey(r, 97, 1568, e.Version == 6); err != nil {
return err
}
case PubKeyAlgoKyber768Brainpool256:
if err = e.readKyberECDHKey(r, 65, 1088); err != nil {
if cipherFunction, err = e.readKyberECDHKey(r, 65, 1088, e.Version == 6); err != nil {
return err
}
case PubKeyAlgoKyber1024Brainpool384:
if err = e.readKyberECDHKey(r, 97, 1568); err != nil {
if cipherFunction, err = e.readKyberECDHKey(r, 97, 1568, e.Version == 6); err != nil {
return err
}
}
if e.Version < 6 {
switch e.Algo {
case PubKeyAlgoX25519, PubKeyAlgoX448:
e.CipherFunc = CipherFunction(cipherFunction)
// Check for validiy is in the Decrypt method
// Check for validity is in the Decrypt method
}
}

Expand All @@ -177,11 +177,7 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) {

// readKyberECDHKey reads Kyber + ECC PKESK as specified in
// https://www.ietf.org/archive/id/draft-wussler-openpgp-pqc-00.html#section-4.3.1
func (e *EncryptedKey) readKyberECDHKey(r io.Reader, lenEcc, lenKyber int) (err error) {
if e.Version != 6 {
return errors.StructuralError("reading non-v6 kyber PKESK")
}

func (e *EncryptedKey) readKyberECDHKey(r io.Reader, lenEcc, lenKyber int, v6 bool) (cipherFunction byte, err error) {
e.encryptedMPI1 = encoding.NewEmptyOctetArray(lenEcc)
if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
return
Expand All @@ -197,6 +193,15 @@ func (e *EncryptedKey) readKyberECDHKey(r io.Reader, lenEcc, lenKyber int) (err
return
}

if !v6 {
var buf [1]byte
_, err = io.ReadFull(r, buf[:])
if err != nil {
return
}
cipherFunction = buf[0]
}

return
}

Expand Down Expand Up @@ -271,19 +276,17 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
}
}
key, err = decodeChecksumKey(b[keyOffset:])
case PubKeyAlgoX25519, PubKeyAlgoX448:
case PubKeyAlgoX25519, PubKeyAlgoX448, PubKeyAlgoKyber768X25519, PubKeyAlgoKyber1024X448, PubKeyAlgoKyber768P256,
PubKeyAlgoKyber1024P384, PubKeyAlgoKyber768Brainpool256, PubKeyAlgoKyber1024Brainpool384:
if e.Version < 6 {
switch e.CipherFunc {
case CipherAES128, CipherAES192, CipherAES256:
break
default:
return errors.StructuralError("v3 PKESK mandates AES as cipher function for x25519 and x448")
return errors.StructuralError("v3 PKESK mandates AES as cipher function for x25519, x448, and PQC")
}
}
key = b[:]
case PubKeyAlgoKyber768X25519, PubKeyAlgoKyber1024X448, PubKeyAlgoKyber768P256, PubKeyAlgoKyber1024P384,
PubKeyAlgoKyber768Brainpool256, PubKeyAlgoKyber1024Brainpool384:
key = b[:]
}
if err != nil {
return err
Expand All @@ -309,6 +312,9 @@ func (e *EncryptedKey) Serialize(w io.Writer) error {
case PubKeyAlgoKyber768X25519, PubKeyAlgoKyber1024X448, PubKeyAlgoKyber768P256, PubKeyAlgoKyber1024P384,
PubKeyAlgoKyber768Brainpool256, PubKeyAlgoKyber1024Brainpool384:
encodedLength = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength()) + int(e.encryptedMPI3.EncodedLength())
if e.Version < 6 {
encodedLength += 1
}
default:
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
}
Expand Down Expand Up @@ -387,7 +393,12 @@ func (e *EncryptedKey) Serialize(w io.Writer) error {
if _, err := w.Write(e.encryptedMPI2.EncodedBytes()); err != nil {
return err
}
_, err := w.Write(e.encryptedMPI3.EncodedBytes())
if _, err := w.Write(e.encryptedMPI3.EncodedBytes()); err != nil {
return err
}
if e.Version < 6 {
_, err = w.Write([]byte{byte(e.CipherFunc)})
}
return err
default:
panic("internal error")
Expand Down
3 changes: 0 additions & 3 deletions openpgp/packet/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -1252,9 +1252,6 @@ func (pk *PrivateKey) parseDilithiumEdDSAPrivateKey(data []byte, ecLen, dLen int
// parseKyberECDHPrivateKey parses a Kyber + ECC private key as specified in
// https://www.ietf.org/archive/id/draft-wussler-openpgp-pqc-00.html#section-4.3.2
func (pk *PrivateKey) parseKyberECDHPrivateKey(data []byte, ecLen, kLen int) (err error) {
if pk.Version != 6 {
return goerrors.New("openpgp: cannot parse non-v6 kyber_ecdh key")
}
pub := pk.PublicKey.PublicKey.(*kyber_ecdh.PublicKey)
priv := new(kyber_ecdh.PrivateKey)
priv.PublicKey = *pub
Expand Down

0 comments on commit 419fab5

Please sign in to comment.