From dd431f6bdaaa742f6fb9c110d997061d3c3a4ba4 Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Thu, 20 Jun 2024 16:07:20 +0200 Subject: [PATCH 1/4] Unify key generation self-signature - Serialized signature sub-packets are now ordered by their ID. - If zlib is preferred for compression, ZIP is also added to the preferences. - The creation time, issuer key ID, and key flag sub-packets are now critical. - The user-ID self-signature is now of type "generic" instead of "positive" certification. --- openpgp/key_generation.go | 6 +- openpgp/packet/signature.go | 177 +++++++++++++++++------------------ openpgp/v2/key_generation.go | 6 +- 3 files changed, 98 insertions(+), 91 deletions(-) diff --git a/openpgp/key_generation.go b/openpgp/key_generation.go index a40e45be..ae62dbeb 100644 --- a/openpgp/key_generation.go +++ b/openpgp/key_generation.go @@ -122,6 +122,10 @@ func writeKeyProperties(selfSignature *packet.Signature, creationTime time.Time, selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)} if config.Compression() != packet.CompressionNone { selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression())) + if config.Compression() == packet.CompressionZLIB { + // If zlib is supported also add zip + selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(packet.CompressionZIP)) + } } // And for DefaultMode. @@ -151,7 +155,7 @@ func (t *Entity) addUserId(name, comment, email string, config *packet.Config, c primary := t.PrivateKey isPrimaryId := len(t.Identities) == 0 - selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config) + selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeGenericCert, config) if writeProperties { err := writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config) if err != nil { diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index b643ed6e..7b6eaec3 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -1182,28 +1182,68 @@ type outputSubpacket struct { func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) { creationTime := make([]byte, 4) binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) - subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) - + // Signature Creation Time + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, true, creationTime}) + // Signature Expiration Time + if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { + sigLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + } + // Trust Signature + if sig.TrustLevel != 0 { + subpackets = append(subpackets, outputSubpacket{true, trustSubpacket, true, []byte{byte(sig.TrustLevel), byte(sig.TrustAmount)}}) + } + // Regular Expression + if sig.TrustRegularExpression != nil { + // RFC specifies the string should be null-terminated; add a null byte to the end + subpackets = append(subpackets, outputSubpacket{true, regularExpressionSubpacket, true, []byte(*sig.TrustRegularExpression + "\000")}) + } + // Key Expiration Time + if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { + keyLifetime := make([]byte, 4) + binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) + subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) + } + // Preferred Symmetric Ciphers for v1 SEIPD + if len(sig.PreferredSymmetric) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) + } + // Issuer Key ID if sig.IssuerKeyId != nil && sig.Version == 4 { keyId := make([]byte, 8) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) - subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId}) } - if sig.IssuerFingerprint != nil { - contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...) - subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, sig.Version >= 5, contents}) + // Notation Data + for _, notation := range sig.Notations { + subpackets = append( + subpackets, + outputSubpacket{ + true, + notationDataSubpacket, + notation.IsCritical, + notation.getData(), + }) } - if sig.SignerUserId != nil { - subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)}) + // Preferred Hash Algorithms + if len(sig.PreferredHash) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) } - if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { - sigLifetime := make([]byte, 4) - binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) - subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) + // Preferred Compression Algorithms + if len(sig.PreferredCompression) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) } - + // Primary User ID + if sig.IsPrimaryId != nil && *sig.IsPrimaryId { + subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) + } + // Policy URI + if len(sig.PolicyURI) > 0 { + subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)}) + } + // Key Flags // Key flags may only appear in self-signatures or certification signatures. - if sig.FlagsValid { var flags byte if sig.FlagCertify { @@ -1227,33 +1267,19 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp if sig.FlagGroupKey { flags |= KeyFlagGroupKey } - subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) + subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, true, []byte{flags}}) } - - for _, notation := range sig.Notations { - subpackets = append( - subpackets, - outputSubpacket{ - true, - notationDataSubpacket, - notation.IsCritical, - notation.getData(), - }) + // Signer's User ID + if sig.SignerUserId != nil { + subpackets = append(subpackets, outputSubpacket{true, signerUserIdSubpacket, false, []byte(*sig.SignerUserId)}) } - - for _, recipient := range sig.IntendedRecipients { - subpackets = append( - subpackets, - outputSubpacket{ - true, - intendedRecipientSubpacket, - false, - recipient.Serialize(), - }) + // Reason for Revocation + // Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23. + if sig.RevocationReason != nil { + subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true, + append([]uint8{uint8(*sig.RevocationReason)}, []uint8(sig.RevocationReasonText)...)}) } - - // The following subpackets may only appear in self-signatures. - + // Features var features = byte(0x00) if sig.SEIPDv1 { features |= 0x01 @@ -1261,46 +1287,36 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp if sig.SEIPDv2 { features |= 0x08 } - if features != 0x00 { subpackets = append(subpackets, outputSubpacket{true, featuresSubpacket, false, []byte{features}}) } - - if sig.TrustLevel != 0 { - subpackets = append(subpackets, outputSubpacket{true, trustSubpacket, true, []byte{byte(sig.TrustLevel), byte(sig.TrustAmount)}}) - } - - if sig.TrustRegularExpression != nil { - // RFC specifies the string should be null-terminated; add a null byte to the end - subpackets = append(subpackets, outputSubpacket{true, regularExpressionSubpacket, true, []byte(*sig.TrustRegularExpression + "\000")}) - } - - if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { - keyLifetime := make([]byte, 4) - binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) - subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) - } - - if sig.IsPrimaryId != nil && *sig.IsPrimaryId { - subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) - } - - if len(sig.PreferredSymmetric) > 0 { - subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) - } - - if len(sig.PreferredHash) > 0 { - subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) + // Embedded Signature + // EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26. + if sig.EmbeddedSignature != nil { + var buf bytes.Buffer + err = sig.EmbeddedSignature.serializeBody(&buf) + if err != nil { + return + } + subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()}) } - - if len(sig.PreferredCompression) > 0 { - subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) + // Issuer Fingerprint + if sig.IssuerFingerprint != nil { + contents := append([]uint8{uint8(issuer.Version)}, sig.IssuerFingerprint...) + subpackets = append(subpackets, outputSubpacket{true, issuerFingerprintSubpacket, sig.Version >= 5, contents}) } - - if len(sig.PolicyURI) > 0 { - subpackets = append(subpackets, outputSubpacket{true, policyUriSubpacket, false, []uint8(sig.PolicyURI)}) + // Intended Recipient Fingerprint + for _, recipient := range sig.IntendedRecipients { + subpackets = append( + subpackets, + outputSubpacket{ + true, + intendedRecipientSubpacket, + false, + recipient.Serialize(), + }) } - + // Preferred AEAD Ciphersuites if len(sig.PreferredCipherSuites) > 0 { serialized := make([]byte, len(sig.PreferredCipherSuites)*2) for i, cipherSuite := range sig.PreferredCipherSuites { @@ -1309,23 +1325,6 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp } subpackets = append(subpackets, outputSubpacket{true, prefCipherSuitesSubpacket, false, serialized}) } - - // Revocation reason appears only in revocation signatures and is serialized as per section 5.2.3.23. - if sig.RevocationReason != nil { - subpackets = append(subpackets, outputSubpacket{true, reasonForRevocationSubpacket, true, - append([]uint8{uint8(*sig.RevocationReason)}, []uint8(sig.RevocationReasonText)...)}) - } - - // EmbeddedSignature appears only in subkeys capable of signing and is serialized as per section 5.2.3.26. - if sig.EmbeddedSignature != nil { - var buf bytes.Buffer - err = sig.EmbeddedSignature.serializeBody(&buf) - if err != nil { - return - } - subpackets = append(subpackets, outputSubpacket{true, embeddedSignatureSubpacket, true, buf.Bytes()}) - } - return } diff --git a/openpgp/v2/key_generation.go b/openpgp/v2/key_generation.go index 5537d4f8..5a9f7d5f 100644 --- a/openpgp/v2/key_generation.go +++ b/openpgp/v2/key_generation.go @@ -193,6 +193,10 @@ func writeKeyProperties(selfSignature *packet.Signature, selectedKeyProperties * selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)} if selectedKeyProperties.compression != packet.CompressionNone { selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(selectedKeyProperties.compression)) + if selectedKeyProperties.compression == packet.CompressionZLIB { + // If zlib is supported also add zip + selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(packet.CompressionZIP)) + } } // And for DefaultMode. @@ -222,7 +226,7 @@ func (t *Entity) addUserId(userIdData userIdData, config *packet.Config, selecte primary := t.PrivateKey isPrimaryId := len(t.Identities) == 0 - selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config) + selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeGenericCert, config) if selectedKeyProperties != nil { err := writeKeyProperties(selfSignature, selectedKeyProperties) if err != nil { From 20d98066bf49883040570c138d7a4edeba6dcdc7 Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Fri, 21 Jun 2024 11:22:35 +0200 Subject: [PATCH 2/4] Positive certification siganture in key generation --- openpgp/key_generation.go | 2 +- openpgp/v2/key_generation.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpgp/key_generation.go b/openpgp/key_generation.go index ae62dbeb..43dfe55a 100644 --- a/openpgp/key_generation.go +++ b/openpgp/key_generation.go @@ -155,7 +155,7 @@ func (t *Entity) addUserId(name, comment, email string, config *packet.Config, c primary := t.PrivateKey isPrimaryId := len(t.Identities) == 0 - selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeGenericCert, config) + selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config) if writeProperties { err := writeKeyProperties(selfSignature, creationTime, keyLifetimeSecs, config) if err != nil { diff --git a/openpgp/v2/key_generation.go b/openpgp/v2/key_generation.go index 5a9f7d5f..b8d5ae37 100644 --- a/openpgp/v2/key_generation.go +++ b/openpgp/v2/key_generation.go @@ -226,7 +226,7 @@ func (t *Entity) addUserId(userIdData userIdData, config *packet.Config, selecte primary := t.PrivateKey isPrimaryId := len(t.Identities) == 0 - selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypeGenericCert, config) + selfSignature := createSignaturePacket(&primary.PublicKey, packet.SigTypePositiveCert, config) if selectedKeyProperties != nil { err := writeKeyProperties(selfSignature, selectedKeyProperties) if err != nil { From 610cabb48f6fbb8f35f984acb0782d1454f843fe Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Fri, 21 Jun 2024 14:40:37 +0200 Subject: [PATCH 3/4] Remove auto zip preference in key generation --- openpgp/key_generation.go | 4 ---- openpgp/v2/key_generation.go | 4 ---- 2 files changed, 8 deletions(-) diff --git a/openpgp/key_generation.go b/openpgp/key_generation.go index 43dfe55a..a40e45be 100644 --- a/openpgp/key_generation.go +++ b/openpgp/key_generation.go @@ -122,10 +122,6 @@ func writeKeyProperties(selfSignature *packet.Signature, creationTime time.Time, selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)} if config.Compression() != packet.CompressionNone { selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression())) - if config.Compression() == packet.CompressionZLIB { - // If zlib is supported also add zip - selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(packet.CompressionZIP)) - } } // And for DefaultMode. diff --git a/openpgp/v2/key_generation.go b/openpgp/v2/key_generation.go index b8d5ae37..5537d4f8 100644 --- a/openpgp/v2/key_generation.go +++ b/openpgp/v2/key_generation.go @@ -193,10 +193,6 @@ func writeKeyProperties(selfSignature *packet.Signature, selectedKeyProperties * selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)} if selectedKeyProperties.compression != packet.CompressionNone { selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(selectedKeyProperties.compression)) - if selectedKeyProperties.compression == packet.CompressionZLIB { - // If zlib is supported also add zip - selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(packet.CompressionZIP)) - } } // And for DefaultMode. From a0326358fd635289f3489c10f0a0cc180c206150 Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter <10532077+lubux@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:18:15 +0200 Subject: [PATCH 4/4] Remove whitespaces in docs Co-authored-by: Daniel Huigens --- openpgp/packet/signature.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index 7b6eaec3..1dc88d7f 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -1234,7 +1234,7 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp if len(sig.PreferredCompression) > 0 { subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) } - // Primary User ID + // Primary User ID if sig.IsPrimaryId != nil && *sig.IsPrimaryId { subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) }