Skip to content

Commit 0e37d00

Browse files
aviauFiloSottile
authored andcommitted
openpgp: don't treat extra subkey selfsigs as uid sigs
Consider the following packet ordering scenario: PUBKEY UID SELFSIG SUBKEY REV SELFSIG In this scenario, addSubkey would only consume the REV signature after the subkey, leaving SELFSIG to be read by ReadEntity, which in turn would add the last SELFSIG to the UID's signatures, which is wrong to do because this is a SUBKEY SELFSIG, not a UID signature. Remove "current" from the ReadEntity scope, it should only be visible to the UserId packet handling code. Keep the warning about signature packets found before user id packets. Without it, I would not have found this bug. Modify addSubKey so that it consumes all signatures following the SUBKEY packet, keeping eithier the first valid signature (like we did before) or any valid revocation. In a follow-up patch, we can improve this further by keeping the most recent signature, as suggested by RFC4880: > An implementation that encounters multiple self-signatures on the > same object may resolve the ambiguity in any way it sees fit, but it > is RECOMMENDED that priority be given to the most recent self- > signature. Fixes golang/go#26449 Change-Id: Id992676ef2363779a7028f4799180efb027fcf47 Reviewed-on: https://go-review.googlesource.com/118957 Reviewed-by: Filippo Valsorda <[email protected]>
1 parent 0709b30 commit 0e37d00

File tree

2 files changed

+104
-22
lines changed

2 files changed

+104
-22
lines changed

openpgp/keys.go

+38-22
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) {
333333
return nil, errors.StructuralError("primary key cannot be used for signatures")
334334
}
335335

336-
var current *Identity
337336
var revocations []*packet.Signature
338337
EachPacket:
339338
for {
@@ -349,7 +348,7 @@ EachPacket:
349348
// Make a new Identity object, that we might wind up throwing away.
350349
// We'll only add it if we get a valid self-signature over this
351350
// userID.
352-
current = new(Identity)
351+
current := new(Identity)
353352
current.Name = pkt.Id
354353
current.UserId = pkt
355354

@@ -384,11 +383,9 @@ EachPacket:
384383
// TODO: RFC4880 5.2.1 permits signatures
385384
// directly on keys (eg. to bind additional
386385
// revocation keys).
387-
} else if current == nil {
388-
return nil, errors.StructuralError("signature packet found before user id packet")
389-
} else {
390-
current.Signatures = append(current.Signatures, pkt)
391386
}
387+
// Else, ignoring the signature as it does not follow anything
388+
// we would know to attach it to.
392389
case *packet.PrivateKey:
393390
if pkt.IsSubkey == false {
394391
packets.Unread(p)
@@ -433,26 +430,45 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
433430
var subKey Subkey
434431
subKey.PublicKey = pub
435432
subKey.PrivateKey = priv
436-
p, err := packets.Next()
437-
if err == io.EOF {
438-
return io.ErrUnexpectedEOF
439-
}
440-
if err != nil {
441-
return errors.StructuralError("subkey signature invalid: " + err.Error())
433+
434+
for {
435+
p, err := packets.Next()
436+
if err == io.EOF {
437+
break
438+
} else if err != nil {
439+
return errors.StructuralError("subkey signature invalid: " + err.Error())
440+
}
441+
442+
sig, ok := p.(*packet.Signature)
443+
if !ok {
444+
packets.Unread(p)
445+
break
446+
}
447+
448+
if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation {
449+
return errors.StructuralError("subkey signature with wrong type")
450+
}
451+
452+
if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil {
453+
return errors.StructuralError("subkey signature invalid: " + err.Error())
454+
}
455+
456+
switch sig.SigType {
457+
case packet.SigTypeSubkeyRevocation:
458+
subKey.Sig = sig
459+
case packet.SigTypeSubkeyBinding:
460+
if subKey.Sig == nil {
461+
subKey.Sig = sig
462+
}
463+
}
442464
}
443-
var ok bool
444-
subKey.Sig, ok = p.(*packet.Signature)
445-
if !ok {
465+
466+
if subKey.Sig == nil {
446467
return errors.StructuralError("subkey packet not followed by signature")
447468
}
448-
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
449-
return errors.StructuralError("subkey signature with wrong type")
450-
}
451-
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
452-
if err != nil {
453-
return errors.StructuralError("subkey signature invalid: " + err.Error())
454-
}
469+
455470
e.Subkeys = append(e.Subkeys, subKey)
471+
456472
return nil
457473
}
458474

openpgp/keys_test.go

+66
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,44 @@ func TestKeyRevocation(t *testing.T) {
180180
}
181181
}
182182

183+
func TestKeyWithRevokedSubKey(t *testing.T) {
184+
// This key contains a revoked sub key:
185+
// pub rsa1024/0x4CBD826C39074E38 2018-06-14 [SC]
186+
// Key fingerprint = 3F95 169F 3FFA 7D3F 2B47 6F0C 4CBD 826C 3907 4E38
187+
// uid Golang Gopher <[email protected]>
188+
// sub rsa1024/0x945DB1AF61D85727 2018-06-14 [S] [revoked: 2018-06-14]
189+
190+
keys, err := ReadArmoredKeyRing(bytes.NewBufferString(keyWithSubKey))
191+
if err != nil {
192+
t.Fatal(err)
193+
}
194+
195+
if len(keys) != 1 {
196+
t.Fatal("Failed to read key with a sub key")
197+
}
198+
199+
identity := keys[0].Identities["Golang Gopher <[email protected]>"]
200+
201+
// Test for an issue where Subkey Binding Signatures (RFC 4880 5.2.1) were added to the identity
202+
// preceding the Subkey Packet if the Subkey Packet was followed by more than one signature.
203+
// For example, the current key has the following layout:
204+
// PUBKEY UID SELFSIG SUBKEY REV SELFSIG
205+
// The last SELFSIG would be added to the UID's signatures. This is wrong.
206+
if numIdentitySigs, numExpected := len(identity.Signatures), 0; numIdentitySigs != numExpected {
207+
t.Fatalf("got %d identity signatures, expected %d", numIdentitySigs, numExpected)
208+
}
209+
210+
if numSubKeys, numExpected := len(keys[0].Subkeys), 1; numSubKeys != numExpected {
211+
t.Fatalf("got %d subkeys, expected %d", numSubKeys, numExpected)
212+
}
213+
214+
subKey := keys[0].Subkeys[0]
215+
if subKey.Sig == nil {
216+
t.Fatalf("subkey signature is nil")
217+
}
218+
219+
}
220+
183221
func TestSubkeyRevocation(t *testing.T) {
184222
kring, err := ReadKeyRing(readerFromHex(revokedSubkeyHex))
185223
if err != nil {
@@ -547,3 +585,31 @@ heiQvzkApQup5c+BhH5zFDFdKJ2CBByxw9+7QjMFI/wgLixKuE0Ob2kAokXf7RlB
547585
7qTZOahrETw=
548586
=IKnw
549587
-----END PGP PUBLIC KEY BLOCK-----`
588+
589+
const keyWithSubKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
590+
591+
mI0EWyKwKQEEALwXhKBnyaaNFeK3ljfc/qn9X/QFw+28EUfgZPHjRmHubuXLE2uR
592+
s3ZoSXY2z7Dkv+NyHYMt8p+X8q5fR7JvUjK2XbPyKoiJVnHINll83yl67DaWfKNL
593+
EjNoO0kIfbXfCkZ7EG6DL+iKtuxniGTcnGT47e+HJSqb/STpLMnWwXjBABEBAAG0
594+
I0dvbGFuZyBHb3BoZXIgPG5vLXJlcGx5QGdvbGFuZy5jb20+iM4EEwEKADgWIQQ/
595+
lRafP/p9PytHbwxMvYJsOQdOOAUCWyKwKQIbAwULCQgHAwUVCgkICwUWAgMBAAIe
596+
AQIXgAAKCRBMvYJsOQdOOOsFBAC62mXww8XuqvYLcVOvHkWLT6mhxrQOJXnlfpn7
597+
2uBV9CMhoG/Ycd43NONsJrB95Apr9TDIqWnVszNbqPCuBhZQSGLdbiDKjxnCWBk0
598+
69qv4RNtkpOhYB7jK4s8F5oQZqId6JasT/PmJTH92mhBYhhTQr0GYFuPX2UJdkw9
599+
Sn9C67iNBFsisDUBBAC3A+Yo9lgCnxi/pfskyLrweYif6kIXWLAtLTsM6g/6jt7b
600+
wTrknuCPyTv0QKGXsAEe/cK/Xq3HvX9WfXPGIHc/X56ZIsHQ+RLowbZV/Lhok1IW
601+
FAuQm8axr/by80cRwFnzhfPc/ukkAq2Qyj4hLsGblu6mxeAhzcp8aqmWOO2H9QAR
602+
AQABiLYEKAEKACAWIQQ/lRafP/p9PytHbwxMvYJsOQdOOAUCWyK16gIdAAAKCRBM
603+
vYJsOQdOOB1vA/4u4uLONsE+2GVOyBsHyy7uTdkuxaR9b54A/cz6jT/tzUbeIzgx
604+
22neWhgvIEghnUZd0vEyK9k1wy5vbDlEo6nKzHso32N1QExGr5upRERAxweDxGOj
605+
7luDwNypI7QcifE64lS/JmlnunwRCdRWMKc0Fp+7jtRc5mpwyHN/Suf5RokBagQY
606+
AQoAIBYhBD+VFp8/+n0/K0dvDEy9gmw5B044BQJbIrA1AhsCAL8JEEy9gmw5B044
607+
tCAEGQEKAB0WIQSNdnkaWY6t62iX336UXbGvYdhXJwUCWyKwNQAKCRCUXbGvYdhX
608+
JxJSA/9fCPHP6sUtGF1o3G1a3yvOUDGr1JWcct9U+QpbCt1mZoNopCNDDQAJvDWl
609+
mvDgHfuogmgNJRjOMznvahbF+wpTXmB7LS0SK412gJzl1fFIpK4bgnhu0TwxNsO1
610+
8UkCZWqxRMgcNUn9z6XWONK8dgt5JNvHSHrwF4CxxwjL23AAtK+FA/UUoi3U4kbC
611+
0XnSr1Sl+mrzQi1+H7xyMe7zjqe+gGANtskqexHzwWPUJCPZ5qpIa2l8ghiUim6b
612+
4ymJ+N8/T8Yva1FaPEqfMzzqJr8McYFm0URioXJPvOAlRxdHPteZ0qUopt/Jawxl
613+
Xt6B9h1YpeLoJwjwsvbi98UTRs0jXwoY
614+
=3fWu
615+
-----END PGP PUBLIC KEY BLOCK-----`

0 commit comments

Comments
 (0)