Skip to content

Commit 7852179

Browse files
Add support for keyserver preferences and preferred keyserver (closes #206) (#232)
* Parse keyserver prefs and preferred keyserver subpackets; update refs to RFC 9580 * Serialise keyserver preferences and preferred keyserver; add tests
1 parent 2add693 commit 7852179

File tree

2 files changed

+155
-44
lines changed

2 files changed

+155
-44
lines changed

openpgp/packet/signature.go

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
package packet
66

77
import (
8-
"math/big"
98
"bytes"
109
"crypto"
1110
"crypto/dsa"
1211
"encoding/asn1"
1312
"encoding/binary"
1413
"hash"
1514
"io"
15+
"math/big"
1616
"strconv"
1717
"time"
1818

@@ -26,7 +26,8 @@ import (
2626
)
2727

2828
const (
29-
// See RFC 4880, section 5.2.3.21 for details.
29+
// First octet of key flags.
30+
// See RFC 9580, section 5.2.3.29 for details.
3031
KeyFlagCertify = 1 << iota
3132
KeyFlagSign
3233
KeyFlagEncryptCommunications
@@ -37,16 +38,29 @@ const (
3738
KeyFlagGroupKey
3839
)
3940

41+
const (
42+
// First octet of keyserver preference flags.
43+
// See RFC 9580, section 5.2.3.25 for details.
44+
_ = 1 << iota
45+
_
46+
_
47+
_
48+
_
49+
_
50+
_
51+
KeyserverPrefNoModify
52+
)
53+
4054
const SaltNotationName = "[email protected]"
4155

42-
// Signature represents a signature. See RFC 4880, section 5.2.
56+
// Signature represents a signature. See RFC 9580, section 5.2.
4357
type Signature struct {
4458
Version int
4559
SigType SignatureType
4660
PubKeyAlgo PublicKeyAlgorithm
4761
Hash crypto.Hash
4862
// salt contains a random salt value for v6 signatures
49-
// See RFC the crypto refresh Section 5.2.3.
63+
// See RFC 9580 Section 5.2.4.
5064
salt []byte
5165

5266
// HashSuffix is extra data that is hashed in after the signed data.
@@ -87,27 +101,37 @@ type Signature struct {
87101
// TrustLevel and TrustAmount can be set by the signer to assert that
88102
// the key is not only valid but also trustworthy at the specified
89103
// level.
90-
// See RFC 4880, section 5.2.3.13 for details.
104+
// See RFC 9580, section 5.2.3.21 for details.
91105
TrustLevel TrustLevel
92106
TrustAmount TrustAmount
93107

94108
// TrustRegularExpression can be used in conjunction with trust Signature
95109
// packets to limit the scope of the trust that is extended.
96-
// See RFC 4880, section 5.2.3.14 for details.
110+
// See RFC 9580, section 5.2.3.22 for details.
97111
TrustRegularExpression *string
98112

113+
// KeyserverPrefsValid is set if any keyserver preferences were given. See RFC 9580, section
114+
// 5.2.3.25 for details.
115+
KeyserverPrefsValid bool
116+
KeyserverPrefNoModify bool
117+
118+
// PreferredKeyserver can be set to a URI where the latest version of the
119+
// key that this signature is made over can be found. See RFC 9580, section
120+
// 5.2.3.26 for details.
121+
PreferredKeyserver string
122+
99123
// PolicyURI can be set to the URI of a document that describes the
100-
// policy under which the signature was issued. See RFC 4880, section
101-
// 5.2.3.20 for details.
124+
// policy under which the signature was issued. See RFC 9580, section
125+
// 5.2.3.28 for details.
102126
PolicyURI string
103127

104-
// FlagsValid is set if any flags were given. See RFC 4880, section
105-
// 5.2.3.21 for details.
128+
// FlagsValid is set if any flags were given. See RFC 9580, section
129+
// 5.2.3.29 for details.
106130
FlagsValid bool
107131
FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage, FlagSplitKey, FlagAuthenticate, FlagGroupKey bool
108132

109133
// RevocationReason is set if this signature has been revoked.
110-
// See RFC 4880, section 5.2.3.23 for details.
134+
// See RFC 9580, section 5.2.3.31 for details.
111135
RevocationReason *ReasonForRevocation
112136
RevocationReasonText string
113137

@@ -147,7 +171,7 @@ func (sig *Signature) Salt() []byte {
147171
}
148172

149173
func (sig *Signature) parse(r io.Reader) (err error) {
150-
// RFC 4880, section 5.2.3
174+
// RFC 9580, section 5.2.3
151175
var buf [7]byte
152176
_, err = readFull(r, buf[:1])
153177
if err != nil {
@@ -319,7 +343,7 @@ func (sig *Signature) parse(r io.Reader) (err error) {
319343
}
320344

321345
// parseSignatureSubpackets parses subpackets of the main signature packet. See
322-
// RFC 4880, section 5.2.3.1.
346+
// RFC 9580, section 5.2.3.1.
323347
func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) {
324348
for len(subpackets) > 0 {
325349
subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed)
@@ -349,6 +373,8 @@ const (
349373
notationDataSubpacket signatureSubpacketType = 20
350374
prefHashAlgosSubpacket signatureSubpacketType = 21
351375
prefCompressionSubpacket signatureSubpacketType = 22
376+
keyserverPrefsSubpacket signatureSubpacketType = 23
377+
prefKeyserverSubpacket signatureSubpacketType = 24
352378
primaryUserIdSubpacket signatureSubpacketType = 25
353379
policyUriSubpacket signatureSubpacketType = 26
354380
keyFlagsSubpacket signatureSubpacketType = 27
@@ -363,7 +389,7 @@ const (
363389

364390
// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
365391
func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) {
366-
// RFC 4880, section 5.2.3.1
392+
// RFC 9580, section 5.2.3.7
367393
var (
368394
length uint32
369395
packetType signatureSubpacketType
@@ -421,7 +447,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
421447
t := binary.BigEndian.Uint32(subpacket)
422448
sig.CreationTime = time.Unix(int64(t), 0)
423449
case signatureExpirationSubpacket:
424-
// Signature expiration time, section 5.2.3.10
450+
// Signature expiration time, section 5.2.3.18
425451
if len(subpacket) != 4 {
426452
err = errors.StructuralError("expiration subpacket with bad length")
427453
return
@@ -438,15 +464,15 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
438464
err = errors.StructuralError("trust subpacket with bad length")
439465
return
440466
}
441-
// Trust level and amount, section 5.2.3.13
467+
// Trust level and amount, section 5.2.3.21
442468
sig.TrustLevel = TrustLevel(subpacket[0])
443469
sig.TrustAmount = TrustAmount(subpacket[1])
444470
case regularExpressionSubpacket:
445471
if len(subpacket) == 0 {
446472
err = errors.StructuralError("regexp subpacket with bad length")
447473
return
448474
}
449-
// Trust regular expression, section 5.2.3.14
475+
// Trust regular expression, section 5.2.3.22
450476
// RFC specifies the string should be null-terminated; remove a null byte from the end
451477
if subpacket[len(subpacket)-1] != 0x00 {
452478
err = errors.StructuralError("expected regular expression to be null-terminated")
@@ -455,19 +481,19 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
455481
trustRegularExpression := string(subpacket[:len(subpacket)-1])
456482
sig.TrustRegularExpression = &trustRegularExpression
457483
case keyExpirationSubpacket:
458-
// Key expiration time, section 5.2.3.6
484+
// Key expiration time, section 5.2.3.13
459485
if len(subpacket) != 4 {
460486
err = errors.StructuralError("key expiration subpacket with bad length")
461487
return
462488
}
463489
sig.KeyLifetimeSecs = new(uint32)
464490
*sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket)
465491
case prefSymmetricAlgosSubpacket:
466-
// Preferred symmetric algorithms, section 5.2.3.7
492+
// Preferred symmetric algorithms, section 5.2.3.14
467493
sig.PreferredSymmetric = make([]byte, len(subpacket))
468494
copy(sig.PreferredSymmetric, subpacket)
469495
case issuerSubpacket:
470-
// Issuer, section 5.2.3.5
496+
// Issuer, section 5.2.3.12
471497
if sig.Version > 4 && isHashed {
472498
err = errors.StructuralError("issuer subpacket found in v6 key")
473499
return
@@ -481,7 +507,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
481507
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket)
482508
}
483509
case notationDataSubpacket:
484-
// Notation data, section 5.2.3.16
510+
// Notation data, section 5.2.3.24
485511
if len(subpacket) < 8 {
486512
err = errors.StructuralError("notation data subpacket with bad length")
487513
return
@@ -503,15 +529,27 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
503529

504530
sig.Notations = append(sig.Notations, &notation)
505531
case prefHashAlgosSubpacket:
506-
// Preferred hash algorithms, section 5.2.3.8
532+
// Preferred hash algorithms, section 5.2.3.16
507533
sig.PreferredHash = make([]byte, len(subpacket))
508534
copy(sig.PreferredHash, subpacket)
509535
case prefCompressionSubpacket:
510-
// Preferred compression algorithms, section 5.2.3.9
536+
// Preferred compression algorithms, section 5.2.3.17
511537
sig.PreferredCompression = make([]byte, len(subpacket))
512538
copy(sig.PreferredCompression, subpacket)
539+
case keyserverPrefsSubpacket:
540+
// Keyserver preferences, section 5.2.3.25
541+
sig.KeyserverPrefsValid = true
542+
if len(subpacket) == 0 {
543+
return
544+
}
545+
if subpacket[0]&KeyserverPrefNoModify != 0 {
546+
sig.KeyserverPrefNoModify = true
547+
}
548+
case prefKeyserverSubpacket:
549+
// Preferred keyserver, section 5.2.3.26
550+
sig.PreferredKeyserver = string(subpacket)
513551
case primaryUserIdSubpacket:
514-
// Primary User ID, section 5.2.3.19
552+
// Primary User ID, section 5.2.3.27
515553
if len(subpacket) != 1 {
516554
err = errors.StructuralError("primary user id subpacket with bad length")
517555
return
@@ -521,7 +559,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
521559
*sig.IsPrimaryId = true
522560
}
523561
case keyFlagsSubpacket:
524-
// Key flags, section 5.2.3.21
562+
// Key flags, section 5.2.3.29
525563
sig.FlagsValid = true
526564
if len(subpacket) == 0 {
527565
return
@@ -551,7 +589,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
551589
userId := string(subpacket)
552590
sig.SignerUserId = &userId
553591
case reasonForRevocationSubpacket:
554-
// Reason For Revocation, section 5.2.3.23
592+
// Reason For Revocation, section 5.2.3.31
555593
if len(subpacket) == 0 {
556594
err = errors.StructuralError("empty revocation reason subpacket")
557595
return
@@ -560,7 +598,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
560598
*sig.RevocationReason = NewReasonForRevocation(subpacket[0])
561599
sig.RevocationReasonText = string(subpacket[1:])
562600
case featuresSubpacket:
563-
// Features subpacket, section 5.2.3.24 specifies a very general
601+
// Features subpacket, section 5.2.3.32 specifies a very general
564602
// mechanism for OpenPGP implementations to signal support for new
565603
// features.
566604
if len(subpacket) > 0 {
@@ -574,24 +612,21 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
574612
}
575613
case embeddedSignatureSubpacket:
576614
// Only usage is in signatures that cross-certify
577-
// signing subkeys. section 5.2.3.26 describes the
615+
// signing subkeys. section 5.2.3.34 describes the
578616
// format, with its usage described in section 11.1
579617
if sig.EmbeddedSignature != nil {
580618
err = errors.StructuralError("Cannot have multiple embedded signatures")
581619
return
582620
}
583621
sig.EmbeddedSignature = new(Signature)
584-
// Embedded signatures are required to be v4 signatures see
585-
// section 12.1. However, we only parse v4 signatures in this
586-
// file anyway.
587622
if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil {
588623
return nil, err
589624
}
590625
if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding {
591626
return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType)))
592627
}
593628
case policyUriSubpacket:
594-
// Policy URI, section 5.2.3.20
629+
// Policy URI, section 5.2.3.28
595630
sig.PolicyURI = string(subpacket)
596631
case issuerFingerprintSubpacket:
597632
if len(subpacket) == 0 {
@@ -611,8 +646,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
611646
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21])
612647
}
613648
case intendedRecipientSubpacket:
614-
// Intended Recipient Fingerprint
615-
// https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr
649+
// Intended Recipient Fingerprint, section 5.2.3.36
616650
if len(subpacket) < 1 {
617651
return nil, errors.StructuralError("invalid intended recipient fingerpring length")
618652
}
@@ -624,8 +658,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
624658
copy(fingerprint, subpacket[1:])
625659
sig.IntendedRecipients = append(sig.IntendedRecipients, &Recipient{int(version), fingerprint})
626660
case prefCipherSuitesSubpacket:
627-
// Preferred AEAD cipher suites
628-
// See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#name-preferred-aead-ciphersuites
661+
// Preferred AEAD cipher suites, section 5.2.3.15
629662
if len(subpacket)%2 != 0 {
630663
err = errors.StructuralError("invalid aead cipher suite length")
631664
return
@@ -676,7 +709,7 @@ func (sig *Signature) CheckKeyIdOrFingerprintExplicit(fingerprint []byte, keyId
676709

677710
// serializeSubpacketLength marshals the given length into to.
678711
func serializeSubpacketLength(to []byte, length int) int {
679-
// RFC 4880, Section 4.2.2.
712+
// RFC 9580, Section 4.2.1.
680713
if length < 192 {
681714
to[0] = byte(length)
682715
return 1
@@ -819,7 +852,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
819852
// The created hash object initially hashes a randomly generated salt
820853
// as required by v6 signatures. The generated salt is stored in sig. If the signature is not v6,
821854
// the method returns an empty hash object.
822-
// See RFC the crypto refresh Section 3.2.4.
855+
// See RFC 9580 Section 5.2.4.
823856
func (sig *Signature) PrepareSign(config *Config) (hash.Hash, error) {
824857
if !sig.Hash.Available() {
825858
return nil, errors.UnsupportedError("hash function")
@@ -843,7 +876,7 @@ func (sig *Signature) PrepareSign(config *Config) (hash.Hash, error) {
843876
// If the signature is not v6, the method ignores the salt.
844877
// Use PrepareSign whenever possible instead of generating and
845878
// hashing the salt externally.
846-
// See RFC the crypto refresh Section 3.2.4.
879+
// See RFC 9580 Section 5.2.4.
847880
func (sig *Signature) SetSalt(salt []byte) error {
848881
if sig.Version == 6 {
849882
expectedSaltLength, err := SaltLengthForHash(sig.Hash)
@@ -861,7 +894,7 @@ func (sig *Signature) SetSalt(salt []byte) error {
861894
// PrepareVerify must be called to create a hash object before verifying v6 signatures.
862895
// The created hash object initially hashes the internally stored salt.
863896
// If the signature is not v6, the method returns an empty hash object.
864-
// See crypto refresh Section 3.2.4.
897+
// See RFC 9580 Section 5.2.4.
865898
func (sig *Signature) PrepareVerify() (hash.Hash, error) {
866899
if !sig.Hash.Available() {
867900
return nil, errors.UnsupportedError("hash function")
@@ -1276,6 +1309,19 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
12761309
if len(sig.PreferredCompression) > 0 {
12771310
subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
12781311
}
1312+
// Keyserver Preferences
1313+
// Keyserver preferences may only appear in self-signatures or certification signatures.
1314+
if sig.KeyserverPrefsValid {
1315+
var prefs byte
1316+
if sig.KeyserverPrefNoModify {
1317+
prefs |= KeyserverPrefNoModify
1318+
}
1319+
subpackets = append(subpackets, outputSubpacket{true, keyserverPrefsSubpacket, false, []byte{prefs}})
1320+
}
1321+
// Preferred Keyserver
1322+
if len(sig.PreferredKeyserver) > 0 {
1323+
subpackets = append(subpackets, outputSubpacket{true, prefKeyserverSubpacket, false, []uint8(sig.PreferredKeyserver)})
1324+
}
12791325
// Primary User ID
12801326
if sig.IsPrimaryId != nil && *sig.IsPrimaryId {
12811327
subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}})
@@ -1316,7 +1362,7 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
13161362
subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)})
13171363
}
13181364
// Reason for Revocation
1319-
// Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23.
1365+
// Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.31.
13201366
if sig.RevocationReason != nil {
13211367
subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true,
13221368
append([]uint8{uint8(*sig.RevocationReason)}, []uint8(sig.RevocationReasonText)...)})
@@ -1333,7 +1379,7 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
13331379
subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}})
13341380
}
13351381
// Embedded Signature
1336-
// EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26.
1382+
// EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.34.
13371383
if sig.EmbeddedSignature != nil {
13381384
var buf bytes.Buffer
13391385
err = sig.EmbeddedSignature.serializeBody(&buf)
@@ -1418,7 +1464,7 @@ func (sig *Signature) AddMetadataToHashSuffix() {
14181464

14191465
// SaltLengthForHash selects the required salt length for the given hash algorithm,
14201466
// as per Table 23 (Hash algorithm registry) of the crypto refresh.
1421-
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#section-9.5|Crypto Refresh Section 9.5.
1467+
// See RFC 9580 Section 9.5.
14221468
func SaltLengthForHash(hash crypto.Hash) (int, error) {
14231469
switch hash {
14241470
case crypto.SHA256, crypto.SHA224, crypto.SHA3_256:
@@ -1434,7 +1480,7 @@ func SaltLengthForHash(hash crypto.Hash) (int, error) {
14341480

14351481
// SignatureSaltForHash generates a random signature salt
14361482
// with the length for the given hash algorithm.
1437-
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#section-9.5|Crypto Refresh Section 9.5.
1483+
// See RFC 9580 Section 9.5.
14381484
func SignatureSaltForHash(hash crypto.Hash, randReader io.Reader) ([]byte, error) {
14391485
saltLength, err := SaltLengthForHash(hash)
14401486
if err != nil {

0 commit comments

Comments
 (0)