From f1d63b8781a69b96278ee82b476ab4fedb338429 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Wed, 15 Jan 2025 16:12:26 +0100 Subject: [PATCH 1/3] Require and verify cross-signature for certification subkeys --- openpgp/packet/public_key.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go index f8da781bb..e4f56184e 100644 --- a/openpgp/packet/public_key.go +++ b/openpgp/packet/public_key.go @@ -908,9 +908,9 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error return err } - if sig.FlagSign { - // Signing subkeys must be cross-signed. See - // https://www.gnupg.org/faq/subkey-cross-certify.html. + if sig.FlagSign || sig.FlagCertify { + // Subkeys that can issue signatures must be cross-signed. See + // https://www.rfc-editor.org/rfc/rfc9580.html#section-10.1.5-8. if sig.EmbeddedSignature == nil { return errors.StructuralError("signing subkey is missing cross-signature") } From a51c34f8e6c3cb2ce3824fe61088f499fdc94d56 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Wed, 15 Jan 2025 16:13:04 +0100 Subject: [PATCH 2/3] Check cross-signature details when signing/certifying --- openpgp/v2/subkeys.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpgp/v2/subkeys.go b/openpgp/v2/subkeys.go index 3e9fc1890..fd5abdb21 100644 --- a/openpgp/v2/subkeys.go +++ b/openpgp/v2/subkeys.go @@ -189,6 +189,9 @@ func (s *Subkey) LatestValidBindingSignature(date time.Time, config *packet.Conf if err == nil { err = checkSignatureDetails(s.Primary.PrimaryKey, sig.Packet, date, config) } + if err == nil && (sig.Packet.FlagSign || sig.Packet.FlagCertify) { + err = checkSignatureDetails(s.PublicKey, sig.Packet.EmbeddedSignature, date, config) + } valid := err == nil sig.Valid = &valid } From 838ee0a7da5176528c8b5d4d66c83acb99d2656f Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Mon, 27 Jan 2025 16:38:57 +0100 Subject: [PATCH 3/3] Require and verify cross-signature for v6 authentication subkeys Not all v4 authentication subkeys have cross-signatures. --- openpgp/packet/public_key.go | 2 +- openpgp/v2/subkeys.go | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go index e4f56184e..6716febbe 100644 --- a/openpgp/packet/public_key.go +++ b/openpgp/packet/public_key.go @@ -908,7 +908,7 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) error return err } - if sig.FlagSign || sig.FlagCertify { + if sig.FlagSign || sig.FlagCertify || (sig.FlagAuthenticate && sig.Version == 6) { // Subkeys that can issue signatures must be cross-signed. See // https://www.rfc-editor.org/rfc/rfc9580.html#section-10.1.5-8. if sig.EmbeddedSignature == nil { diff --git a/openpgp/v2/subkeys.go b/openpgp/v2/subkeys.go index fd5abdb21..e61d17fd3 100644 --- a/openpgp/v2/subkeys.go +++ b/openpgp/v2/subkeys.go @@ -181,28 +181,29 @@ func (s *Subkey) Revoke(reason packet.ReasonForRevocation, reasonText string, co // Returns a StructuralError if no valid self-signature is found. func (s *Subkey) LatestValidBindingSignature(date time.Time, config *packet.Config) (selectedSig *packet.Signature, err error) { for sigIdx := len(s.Bindings) - 1; sigIdx >= 0; sigIdx-- { - sig := s.Bindings[sigIdx] - if (date.IsZero() || date.Unix() >= sig.Packet.CreationTime.Unix()) && - (selectedSig == nil || selectedSig.CreationTime.Unix() < sig.Packet.CreationTime.Unix()) { - if sig.Valid == nil { - err := s.Primary.PrimaryKey.VerifyKeySignature(s.PublicKey, sig.Packet) + binding := s.Bindings[sigIdx] + sig := binding.Packet + if (date.IsZero() || date.Unix() >= sig.CreationTime.Unix()) && + (selectedSig == nil || selectedSig.CreationTime.Unix() < sig.CreationTime.Unix()) { + if binding.Valid == nil { + err := s.Primary.PrimaryKey.VerifyKeySignature(s.PublicKey, sig) if err == nil { - err = checkSignatureDetails(s.Primary.PrimaryKey, sig.Packet, date, config) + err = checkSignatureDetails(s.Primary.PrimaryKey, sig, date, config) } - if err == nil && (sig.Packet.FlagSign || sig.Packet.FlagCertify) { - err = checkSignatureDetails(s.PublicKey, sig.Packet.EmbeddedSignature, date, config) + if err == nil && (sig.FlagSign || sig.FlagCertify || (sig.FlagAuthenticate && sig.Version == 6)) { + err = checkSignatureDetails(s.PublicKey, sig.EmbeddedSignature, date, config) } valid := err == nil - sig.Valid = &valid + binding.Valid = &valid } mainSigExpired := !date.IsZero() && - sig.Packet.SigExpired(date) + sig.SigExpired(date) embeddedSigExpired := !date.IsZero() && - sig.Packet.FlagSign && - sig.Packet.EmbeddedSignature != nil && - sig.Packet.EmbeddedSignature.SigExpired(date) - if *sig.Valid && !mainSigExpired && !embeddedSigExpired { - selectedSig = sig.Packet + sig.FlagSign && + sig.EmbeddedSignature != nil && + sig.EmbeddedSignature.SigExpired(date) + if *binding.Valid && !mainSigExpired && !embeddedSigExpired { + selectedSig = sig } } }