Skip to content

Commit f6ad483

Browse files
authored
No v6 ECC keys with legacy OIDs (#234)
This PR addresses a compliance issue with RFC 9580, which mandates: "Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs." Currently, the implementation allows generation of v6 keys with deprecated OIDs, which violates the specification. This MR resolves the issue by introducing an error during key generation and parsing when deprecated OIDs are detected.
1 parent 77090fe commit f6ad483

File tree

6 files changed

+86
-9
lines changed

6 files changed

+86
-9
lines changed

openpgp/internal/ecc/curve_info.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
1111
)
1212

13+
const Curve25519GenName = "Curve25519"
14+
1315
type CurveInfo struct {
1416
GenName string
1517
Oid *encoding.OID
@@ -43,7 +45,7 @@ var Curves = []CurveInfo{
4345
},
4446
{
4547
// Curve25519
46-
GenName: "Curve25519",
48+
GenName: Curve25519GenName,
4749
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}),
4850
Curve: NewCurve25519(),
4951
},
@@ -55,7 +57,7 @@ var Curves = []CurveInfo{
5557
},
5658
{
5759
// Ed25519
58-
GenName: "Curve25519",
60+
GenName: Curve25519GenName,
5961
Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}),
6062
Curve: NewEd25519(),
6163
},

openpgp/key_generation.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
4141
}
4242
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
4343
if config.V6() {
44-
primary.UpgradeToV6()
44+
if err := primary.UpgradeToV6(); err != nil {
45+
return nil, err
46+
}
4547
}
4648

4749
e := &Entity{
@@ -187,7 +189,9 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error {
187189
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw)
188190
sub.IsSubkey = true
189191
if config.V6() {
190-
sub.UpgradeToV6()
192+
if err := sub.UpgradeToV6(); err != nil {
193+
return err
194+
}
191195
}
192196

193197
subkey := Subkey{
@@ -232,7 +236,9 @@ func (e *Entity) addEncryptionSubkey(config *packet.Config, creationTime time.Ti
232236
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
233237
sub.IsSubkey = true
234238
if config.V6() {
235-
sub.UpgradeToV6()
239+
if err := sub.UpgradeToV6(); err != nil {
240+
return err
241+
}
236242
}
237243

238244
subkey := Subkey{

openpgp/packet/public_key.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ func (pk *PublicKey) UpgradeToV5() {
6363

6464
// UpgradeToV6 updates the version of the key to v6, and updates all necessary
6565
// fields.
66-
func (pk *PublicKey) UpgradeToV6() {
66+
func (pk *PublicKey) UpgradeToV6() error {
6767
pk.Version = 6
6868
pk.setFingerprintAndKeyId()
69+
return pk.checkV6Compatibility()
6970
}
7071

7172
// signingKey provides a convenient abstraction over signature verification
@@ -313,6 +314,23 @@ func (pk *PublicKey) setFingerprintAndKeyId() {
313314
}
314315
}
315316

317+
func (pk *PublicKey) checkV6Compatibility() error {
318+
// Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
319+
switch pk.PubKeyAlgo {
320+
case PubKeyAlgoECDH:
321+
curveInfo := ecc.FindByOid(pk.oid)
322+
if curveInfo == nil {
323+
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
324+
}
325+
if curveInfo.GenName == ecc.Curve25519GenName {
326+
return errors.StructuralError("cannot generate v6 key with deprecated OID: Curve25519Legacy")
327+
}
328+
case PubKeyAlgoEdDSA:
329+
return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy")
330+
}
331+
return nil
332+
}
333+
316334
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
317335
// section 5.5.2.
318336
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
@@ -437,6 +455,11 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
437455
return errors.UnsupportedError(fmt.Sprintf("unknown oid: %x", pk.oid))
438456
}
439457

458+
if pk.Version == 6 && curveInfo.GenName == ecc.Curve25519GenName {
459+
// Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
460+
return errors.StructuralError("cannot read v6 key with deprecated OID: Curve25519Legacy")
461+
}
462+
440463
pk.p = new(encoding.MPI)
441464
if _, err = pk.p.ReadFrom(r); err != nil {
442465
return
@@ -474,6 +497,11 @@ func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
474497
}
475498

476499
func (pk *PublicKey) parseEdDSA(r io.Reader) (err error) {
500+
if pk.Version == 6 {
501+
// Implementations MUST NOT accept or generate version 6 key material using the deprecated OIDs.
502+
return errors.StructuralError("cannot generate v6 key with deprecated algorithm: EdDSALegacy")
503+
}
504+
477505
pk.oid = new(encoding.OID)
478506
if _, err = pk.oid.ReadFrom(r); err != nil {
479507
return

openpgp/v2/key_generation.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ func newEntity(uid *userIdData, config *packet.Config) (*Entity, error) {
8383
}
8484
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
8585
if config.V6() {
86-
primary.UpgradeToV6()
86+
if err := primary.UpgradeToV6(); err != nil {
87+
return nil, err
88+
}
8789
}
8890

8991
keyProperties := selectKeyProperties(creationTime, config, primary)
@@ -259,7 +261,9 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error {
259261
sub.IsSubkey = true
260262
// Every subkey for a v6 primary key MUST be a v6 subkey.
261263
if e.PrimaryKey.Version == 6 {
262-
sub.UpgradeToV6()
264+
if err := sub.UpgradeToV6(); err != nil {
265+
return err
266+
}
263267
}
264268

265269
subkey := Subkey{
@@ -308,7 +312,9 @@ func (e *Entity) addEncryptionSubkey(config *packet.Config, creationTime time.Ti
308312
sub.IsSubkey = true
309313
// Every subkey for a v6 primary key MUST be a v6 subkey.
310314
if e.PrimaryKey.Version == 6 {
311-
sub.UpgradeToV6()
315+
if err := sub.UpgradeToV6(); err != nil {
316+
return err
317+
}
312318
}
313319

314320
subkey := Subkey{

openpgp/v2/keys_test_data.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,20 @@ VppQxdtxPvAA/34snHBX7Twnip1nMt7P4e2hDiw/hwQ7oqioOvc6jMkP
536536
=Z8YJ
537537
-----END PGP PRIVATE KEY BLOCK-----
538538
`
539+
540+
const LegacyECCKeyV6 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
541+
542+
xVoGZtnEsRYAAAAtCSsGAQQB2kcPAQEHQJ8u3Y3xhq7vDoYgyIjhniUavG9lhqqn
543+
eoQLj08i1UyCAAEAnZMljJWA74axuVNrL6uhyBLGCwypSeWCXBRqKzR4yjzCswYf
544+
FgoAAABBBQJm2cSxAhsDAh4JCAsJCAcKDQwLBRUKCQgLAhYCIiEG6/BxiEOhwOXw
545+
t2eAShAq9G0keONgsN0KdQSyvNnuD3YAAAAAFEcgEheAMaYvmoAP4UKwK0hOPtFC
546+
wnuf7v+O9tpko93/9GQA9Am6sr0XTsa8RDRc49lm5k2WYwGx5yk2JgsFLX26RJAA
547+
/j0NorcLtTCV/uejDw26ZsWg2kF69ovLed4wF26iCaAHx18GZtnEsRIAAAAyCisG
548+
AQQBl1UBBQEBB0DavdPV3M/mAO5/IkYVdHu006uVFO3eIuZ2ffJPoe5KGQMBCAcA
549+
AP9V2OXFg/QewXMGMNol6S1DUbKhGuyKEK0hrmzUz224qMKZBhgWCAAAACwFAmbZ
550+
xLECGwwiIQbr8HGIQ6HA5fC3Z4BKECr0bSR442Cw3Qp1BLK82e4PdgAAAAoJEOvw
551+
cYhDocDl37EQls/BpUt572AJX8ZBqbsycgD/QfCjSRcPHelHFHkaAHMzXscDBaUS
552+
jiCm2KiM+33wOYYA/2Hu4xmHg7wN2prRcB4+2qkUIdEzMyFs5TYcOU7Hst0N
553+
=pm0Y
554+
-----END PGP PRIVATE KEY BLOCK-----
555+
`

openpgp/v2/keys_v6_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,21 @@ func TestKeyGenerationHighSecurityLevel(t *testing.T) {
224224
}
225225

226226
}
227+
228+
func TestKeyGenLegacyECC(t *testing.T) {
229+
c := &packet.Config{
230+
V6Keys: true,
231+
Algorithm: packet.PubKeyAlgoEdDSA,
232+
}
233+
_, err := NewEntity("V6 Key Owner", "V6 Key", "[email protected]", c)
234+
if err == nil {
235+
t.Fatal("should not be able to generate v6 keys with legacy OIDs")
236+
}
237+
}
238+
239+
func TestReadLegacyECC(t *testing.T) {
240+
_, err := ReadArmoredKeyRing(strings.NewReader(LegacyECCKeyV6))
241+
if err == nil {
242+
t.Fatal("should not be able to read v6 legacy ECC key")
243+
}
244+
}

0 commit comments

Comments
 (0)