Skip to content

Commit aef6224

Browse files
author
zugzwang
authored
Implement V5 keys and signatures (#58)
Add support for parsing and generating V5 keys, but still generate V4 keys by default, and add a Config.V5Keys option to generate V5 keys. Also, add support for the Issuer Fingerprint subpacket, and generate it for all signatures (both V4 and V5).
1 parent 576ad9c commit aef6224

20 files changed

Lines changed: 710 additions & 280 deletions

openpgp/ecdh/ecdh.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLead
126126
if _, err := param.Write([]byte("Anonymous Sender ")); err != nil {
127127
return nil, err
128128
}
129-
if _, err := param.Write(fingerprint); err != nil {
129+
// For v5 keys, the 20 leftmost octets of the fingerprint are used.
130+
if _, err := param.Write(fingerprint[:20]); err != nil {
130131
return nil, err
131132
}
132133
if param.Len() - len(curveOID) != 45 {

openpgp/integration_tests/end_to_end_test.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,16 @@ func encDecTest(t *testing.T, from testVector, testVectors []testVector) {
153153
pkTo := readArmoredPk(t, to.PublicKey)
154154
skTo := readArmoredSk(t, to.PrivateKey, to.Password)
155155
message := randMessage()
156+
hints := randFileHints()
156157

157158
// Encrypt message
158-
signed := skFrom[0]
159-
errDec := signed.PrivateKey.Decrypt([]byte(from.Password))
159+
signer := skFrom[0]
160+
errDec := signer.PrivateKey.Decrypt([]byte(from.Password))
160161
if errDec != nil {
161162
t.Error(errDec)
162163
}
163164
buf := new(bytes.Buffer)
164-
w, err := openpgp.Encrypt(buf, pkTo[:1], signed, nil, from.config)
165+
w, err := openpgp.Encrypt(buf, pkTo[:1], signer, hints, from.config)
165166
if err != nil {
166167
t.Fatalf("Error in Encrypt: %s", err)
167168
}
@@ -199,7 +200,7 @@ func encDecTest(t *testing.T, from testVector, testVectors []testVector) {
199200
if !md.IsEncrypted {
200201
t.Fatal("The message should be encrypted")
201202
}
202-
signKey, _ := signed.SigningKey(time.Now())
203+
signKey, _ := signer.SigningKey(time.Now())
203204
expectedKeyID := signKey.PublicKey.KeyId
204205
if md.SignedByKeyId != expectedKeyID {
205206
t.Fatalf(
@@ -227,7 +228,7 @@ func encDecTest(t *testing.T, from testVector, testVectors []testVector) {
227228
}
228229

229230
if md.SignatureError != nil {
230-
t.Errorf("Signature error: %s", md.SignatureError)
231+
t.Fatalf("Signature error: %s", md.SignatureError)
231232
}
232233
if md.Signature == nil {
233234
t.Error("Signature missing")
@@ -245,8 +246,7 @@ func signVerifyTest(
245246
skFrom, pkFrom openpgp.EntityList,
246247
binary bool,
247248
) {
248-
signed := skFrom[0]
249-
if err := signed.PrivateKey.Decrypt([]byte(from.Password)); err != nil {
249+
if err := skFrom[0].PrivateKey.Decrypt([]byte(from.Password)); err != nil {
250250
t.Error(err)
251251
}
252252

@@ -268,9 +268,9 @@ func signVerifyTest(
268268
buf := new(bytes.Buffer)
269269
var errSign error
270270
if binary {
271-
errSign = openpgp.ArmoredDetachSign(buf, signed, message, nil)
271+
errSign = openpgp.ArmoredDetachSign(buf, skFrom[0], message, nil)
272272
} else {
273-
errSign = openpgp.ArmoredDetachSignText(buf, signed, message, nil)
273+
errSign = openpgp.ArmoredDetachSignText(buf, skFrom[0], message, nil)
274274
}
275275
if errSign != nil {
276276
t.Error(errSign)
@@ -306,7 +306,7 @@ func signVerifyTest(
306306
if otherSigner == nil {
307307
t.Fatalf("signer is nil")
308308
}
309-
if otherSigner.PrimaryKey.KeyId != signed.PrimaryKey.KeyId {
309+
if otherSigner.PrimaryKey.KeyId != skFrom[0].PrimaryKey.KeyId {
310310
t.Errorf(
311311
"wrong signer got:%x want:%x", otherSigner.PrimaryKey.KeyId, 0)
312312
}
@@ -322,17 +322,21 @@ func signVerifyTest(
322322
t.Error(errSeek)
323323
}
324324

325-
signer, err := openpgp.CheckArmoredDetachedSignature(
325+
otherSigner, err = openpgp.CheckArmoredDetachedSignature(
326326
pkFrom, message, signatureReader, nil)
327327

328328
if err != nil {
329329
t.Fatalf("signature error: %s", err)
330330
}
331-
if signer == nil {
331+
if otherSigner == nil {
332332
t.Fatalf("signer is nil")
333333
}
334-
if signer.PrimaryKey.KeyId != signed.PrimaryKey.KeyId {
335-
t.Errorf("wrong signer got:%x want:%x", signer.PrimaryKey.KeyId, 0)
334+
if otherSigner.PrimaryKey.KeyId != skFrom[0].PrimaryKey.KeyId {
335+
t.Errorf(
336+
"wrong signer got:%x want:%x",
337+
skFrom[0].PrimaryKey.KeyId,
338+
skFrom[0].PrimaryKey.KeyId,
339+
)
336340
}
337341
}
338342

openpgp/integration_tests/utils_test.go

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package integrationtests
22

33
import (
4-
"time"
54
"bytes"
65
"crypto"
76
"crypto/rand"
7+
mathrand "math/rand"
8+
"strings"
9+
"time"
10+
811
"golang.org/x/crypto/openpgp"
912
"golang.org/x/crypto/openpgp/armor"
1013
"golang.org/x/crypto/openpgp/packet"
11-
mathrand "math/rand"
12-
"strings"
1314
)
1415

15-
1616
// This function produces random test vectors: generates keys according to the
1717
// given settings, associates a random message for each key. It returns the
1818
// test vectors.
@@ -24,9 +24,13 @@ func generateFreshTestVectors() (vectors []testVector, err error) {
2424
name, email, comment, password, message := randEntityData()
2525

2626
// Only for verbose display
27-
pkAlgoNames := map[packet.PublicKeyAlgorithm]string {
28-
packet.PubKeyAlgoRSA: "rsa_fresh",
29-
packet.PubKeyAlgoEdDSA: "ed25519_fresh",
27+
v := "v4"
28+
if config.V5Keys {
29+
v = "v5"
30+
}
31+
pkAlgoNames := map[packet.PublicKeyAlgorithm]string{
32+
packet.PubKeyAlgoRSA: "rsa_" + v,
33+
packet.PubKeyAlgoEdDSA: "ed25519_" + v,
3034
}
3135

3236
newVector := testVector{
@@ -69,7 +73,6 @@ func generateFreshTestVectors() (vectors []testVector, err error) {
6973
privateKey, _ := armorWithType(serialized, "PGP PRIVATE KEY BLOCK")
7074
newVector.PrivateKey = privateKey
7175
newVector.PublicKey, _ = publicKey(privateKey)
72-
7376
vectors = append(vectors, newVector)
7477
}
7578
return vectors, err
@@ -114,7 +117,7 @@ func publicKey(privateKey string) (string, error) {
114117
return outString, nil
115118
}
116119

117-
var runes = []rune("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKMNOPQRSTUVWXYZ.:;?/!@#$%^&*{}[]_'\"-+~()<>")
120+
var runes = []rune("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKMNOPQRSTUVWXYZ.@-_:;?/!#$%^&*{}[]'\"+~()<>")
118121

119122
func randName() string {
120123
firstName := make([]rune, 8)
@@ -132,6 +135,20 @@ func randName() string {
132135
return string(firstName) + " " + string(lastName)
133136
}
134137

138+
func randFileHints() *openpgp.FileHints {
139+
fileNameRunes := runes[:66]
140+
fileName := make([]rune, 1+mathrand.Intn(255))
141+
for i := range fileName {
142+
fileName[i] = fileNameRunes[mathrand.Intn(len(fileNameRunes))]
143+
}
144+
145+
return &openpgp.FileHints{
146+
IsBinary: mathrand.Intn(2) == 0,
147+
FileName: string(fileName),
148+
ModTime: time.Now(),
149+
}
150+
}
151+
135152
func randEmail() string {
136153
address := make([]rune, 20)
137154
addressRunes := runes[:38]
@@ -206,20 +223,20 @@ func randConfig() *packet.Config {
206223
}
207224
ciph := ciphers[mathrand.Intn(len(ciphers))]
208225

209-
compAlgos := []packet.CompressionAlgo {
226+
compAlgos := []packet.CompressionAlgo{
210227
packet.CompressionNone,
211228
packet.CompressionZIP,
212229
packet.CompressionZLIB,
213230
}
214231
compAlgo := compAlgos[mathrand.Intn(len(compAlgos))]
215232

216-
pkAlgos := []packet.PublicKeyAlgorithm {
233+
pkAlgos := []packet.PublicKeyAlgorithm{
217234
packet.PubKeyAlgoRSA,
218235
packet.PubKeyAlgoEdDSA,
219236
}
220237
pkAlgo := pkAlgos[mathrand.Intn(len(pkAlgos))]
221238

222-
aeadModes := []packet.AEADMode {
239+
aeadModes := []packet.AEADMode{
223240
packet.AEADModeEAX,
224241
packet.AEADModeOCB,
225242
packet.AEADModeExperimentalGCM,
@@ -242,18 +259,24 @@ func randConfig() *packet.Config {
242259
}
243260
}
244261

245-
level := mathrand.Intn(11)-1
262+
level := mathrand.Intn(11) - 1
246263
compConf := &packet.CompressionConfig{level}
247264

265+
var v5 bool
266+
if mathrand.Int()%2 == 0 {
267+
v5 = true
268+
}
269+
248270
return &packet.Config{
249-
Rand: rand.Reader,
250-
DefaultHash: hash,
251-
DefaultCipher: ciph,
271+
V5Keys: v5,
272+
Rand: rand.Reader,
273+
DefaultHash: hash,
274+
DefaultCipher: ciph,
252275
DefaultCompressionAlgo: compAlgo,
253-
CompressionConfig: compConf,
254-
S2KCount: 1024 + mathrand.Intn(65010689),
255-
RSABits: rsaBits,
256-
Algorithm: pkAlgo,
257-
AEADConfig: &aeadConf,
276+
CompressionConfig: compConf,
277+
S2KCount: 1024 + mathrand.Intn(65010689),
278+
RSABits: rsaBits,
279+
Algorithm: pkAlgo,
280+
AEADConfig: &aeadConf,
258281
}
259282
}

openpgp/key_generation.go

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,26 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
3434
return nil, err
3535
}
3636
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
37+
if config != nil && config.V5Keys {
38+
primary.UpgradeToV5()
39+
}
3740

3841
isPrimaryId := true
3942
selfSignature := &packet.Signature{
40-
SigType: packet.SigTypePositiveCert,
41-
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
42-
Hash: config.Hash(),
43-
CreationTime: creationTime,
44-
IssuerKeyId: &primary.PublicKey.KeyId,
45-
IsPrimaryId: &isPrimaryId,
46-
FlagsValid: true,
47-
FlagSign: true,
48-
FlagCertify: true,
49-
MDC: true, // true by default, see 5.8 vs. 5.14
50-
AEAD: config.AEAD() != nil,
43+
Version: primary.PublicKey.Version,
44+
SigType: packet.SigTypePositiveCert,
45+
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
46+
Hash: config.Hash(),
47+
CreationTime: creationTime,
48+
IssuerKeyId: &primary.PublicKey.KeyId,
49+
IssuerFingerprint: primary.PublicKey.Fingerprint,
50+
IsPrimaryId: &isPrimaryId,
51+
FlagsValid: true,
52+
FlagSign: true,
53+
FlagCertify: true,
54+
MDC: true, // true by default, see 5.8 vs. 5.14
55+
AEAD: config.AEAD() != nil,
56+
V5Keys: config != nil && config.V5Keys,
5157
}
5258

5359
// Set the PreferredHash for the SelfSignature from the packet.Config.
@@ -83,11 +89,15 @@ func NewEntity(name, comment, email string, config *packet.Config) (*Entity, err
8389
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
8490
sub.IsSubkey = true
8591
sub.PublicKey.IsSubkey = true
92+
if config != nil && config.V5Keys {
93+
sub.UpgradeToV5()
94+
}
8695

8796
subKey := Subkey{
8897
PublicKey: &sub.PublicKey,
8998
PrivateKey: sub,
9099
Sig: &packet.Signature{
100+
Version: primary.PublicKey.Version,
91101
CreationTime: creationTime,
92102
SigType: packet.SigTypeSubkeyBinding,
93103
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
@@ -136,6 +146,7 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error {
136146
PublicKey: &sub.PublicKey,
137147
PrivateKey: sub,
138148
Sig: &packet.Signature{
149+
Version: e.PrimaryKey.Version,
139150
CreationTime: creationTime,
140151
KeyLifetimeSecs: &keyLifetimeSecs,
141152
SigType: packet.SigTypeSubkeyBinding,
@@ -145,6 +156,7 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error {
145156
FlagSign: true,
146157
IssuerKeyId: &e.PrimaryKey.KeyId,
147158
EmbeddedSignature: &packet.Signature{
159+
Version: e.PrimaryKey.Version,
148160
CreationTime: creationTime,
149161
SigType: packet.SigTypePrimaryKeyBinding,
150162
PubKeyAlgo: sub.PublicKey.PubKeyAlgo,
@@ -153,6 +165,9 @@ func (e *Entity) AddSigningSubkey(config *packet.Config) error {
153165
},
154166
},
155167
}
168+
if config != nil && config.V5Keys {
169+
subkey.PublicKey.UpgradeToV5()
170+
}
156171

157172
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config)
158173
if err != nil {
@@ -185,6 +200,7 @@ func (e *Entity) AddEncryptionSubkey(config *packet.Config) error {
185200
PublicKey: &sub.PublicKey,
186201
PrivateKey: sub,
187202
Sig: &packet.Signature{
203+
Version: e.PrimaryKey.Version,
188204
CreationTime: creationTime,
189205
KeyLifetimeSecs: &keyLifetimeSecs,
190206
SigType: packet.SigTypeSubkeyBinding,
@@ -196,6 +212,9 @@ func (e *Entity) AddEncryptionSubkey(config *packet.Config) error {
196212
IssuerKeyId: &e.PrimaryKey.KeyId,
197213
},
198214
}
215+
if config != nil && config.V5Keys {
216+
subkey.PublicKey.UpgradeToV5()
217+
}
199218

200219
subkey.PublicKey.IsSubkey = true
201220
subkey.PrivateKey.IsSubkey = true

openpgp/keys.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error {
420420
break
421421
}
422422

423-
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
423+
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.CheckKeyIdOrFingerprint(e.PrimaryKey) {
424424
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
425425
return errors.StructuralError("user ID self-signature invalid: " + err.Error())
426426
}
@@ -468,7 +468,6 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
468468
case packet.SigTypeSubkeyRevocation:
469469
subKey.Sig = sig
470470
case packet.SigTypeSubkeyBinding:
471-
472471
if shouldReplaceSubkeySig(subKey.Sig, sig) {
473472
subKey.Sig = sig
474473
}
@@ -620,6 +619,7 @@ func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Co
620619
}
621620

622621
sig := &packet.Signature{
622+
Version: signer.PrivateKey.Version,
623623
SigType: packet.SigTypeGenericCert,
624624
PubKeyAlgo: signer.PrivateKey.PubKeyAlgo,
625625
Hash: config.Hash(),
@@ -639,6 +639,7 @@ func (e *Entity) SignIdentity(identity string, signer *Entity, config *packet.Co
639639
func (e *Entity) RevokeKey(reason packet.ReasonForRevocation, reasonText string, config *packet.Config) error {
640640
reasonCode := uint8(reason)
641641
revSig := &packet.Signature{
642+
Version: e.PrimaryKey.Version,
642643
CreationTime: config.Now(),
643644
SigType: packet.SigTypeKeyRevocation,
644645
PubKeyAlgo: packet.PubKeyAlgoRSA,
@@ -665,6 +666,7 @@ func (e *Entity) RevokeSubkey(sk *Subkey, reason packet.ReasonForRevocation, rea
665666

666667
reasonCode := uint8(reason)
667668
revSig := &packet.Signature{
669+
Version: e.PrimaryKey.Version,
668670
CreationTime: config.Now(),
669671
SigType: packet.SigTypeSubkeyRevocation,
670672
PubKeyAlgo: packet.PubKeyAlgoRSA,

openpgp/keys_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ func TestRevokeSubkeyWithInvalidSignature(t *testing.T) {
965965
}
966966

967967
sk := entity.Subkeys[0]
968-
sk.Sig = &packet.Signature{}
968+
sk.Sig = &packet.Signature{Version: 4}
969969

970970
err = entity.RevokeSubkey(&sk, packet.NoReason, "Key revocation", nil)
971971
if err == nil {

0 commit comments

Comments
 (0)