Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allows Key Flags override, fixes #205 #272

Merged
merged 1 commit into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions openpgp/packet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ type Config struct {
// weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
// The default behavior, when the config or flag is nil, is to enable the feature.
NonDeterministicSignaturesViaNotation *bool

// InsecureAllowAllKeyFlagsWhenMissing determines how a key without valid key flags is handled.
// When set to true, a key without flags is treated as if all flags are enabled.
// This behavior is consistent with GPG.
InsecureAllowAllKeyFlagsWhenMissing bool
}

func (c *Config) Random() io.Reader {
Expand Down Expand Up @@ -403,6 +408,13 @@ func (c *Config) RandomizeSignaturesViaNotation() bool {
return *c.NonDeterministicSignaturesViaNotation
}

func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
if c == nil {
return false
}
return c.InsecureAllowAllKeyFlagsWhenMissing
}

// BoolPointer is a helper function to set a boolean pointer in the Config.
// e.g., config.CheckPacketSequence = BoolPointer(true)
func BoolPointer(value bool) *bool {
Expand Down
58 changes: 38 additions & 20 deletions openpgp/v2/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (e *Entity) EncryptionKey(now time.Time, config *packet.Config) (Key, bool)
for i, subkey := range e.Subkeys {
subkeySelfSig, err := subkey.Verify(now, config) // subkey has to be valid at time now
if err == nil &&
isValidEncryptionKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo) &&
isValidEncryptionKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo, config) &&
checkKeyRequirements(subkey.PublicKey, config) == nil &&
(maxTime.IsZero() || subkeySelfSig.CreationTime.Unix() >= maxTime.Unix()) {
candidateSubkey = i
Expand All @@ -138,7 +138,7 @@ func (e *Entity) EncryptionKey(now time.Time, config *packet.Config) (Key, bool)

// If we don't have any subkeys for encryption and the primary key
// is marked as OK to encrypt with, then we can use it.
if isValidEncryptionKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo) {
if isValidEncryptionKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo, config) {
return Key{
Entity: e,
PrimarySelfSignature: primarySelfSignature,
Expand All @@ -164,12 +164,12 @@ func (e *Entity) DecryptionKeys(id uint64, date time.Time, config *packet.Config
for _, subkey := range e.Subkeys {
subkeySelfSig, err := subkey.LatestValidBindingSignature(date, config)
if err == nil &&
(config.AllowDecryptionWithSigningKeys() || isValidEncryptionKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo)) &&
(config.AllowDecryptionWithSigningKeys() || isValidEncryptionKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo, config)) &&
(id == 0 || subkey.PublicKey.KeyId == id) {
keys = append(keys, Key{subkey.Primary, primarySelfSignature, subkey.PublicKey, subkey.PrivateKey, subkeySelfSig})
}
}
if config.AllowDecryptionWithSigningKeys() || isValidEncryptionKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo) {
if config.AllowDecryptionWithSigningKeys() || isValidEncryptionKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo, config) {
keys = append(keys, Key{e, primarySelfSignature, e.PrimaryKey, e.PrivateKey, primarySelfSignature})
}
return
Expand Down Expand Up @@ -219,8 +219,8 @@ func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int, config
for idx, subkey := range e.Subkeys {
subkeySelfSig, err := subkey.Verify(now, config)
if err == nil &&
(flags&packet.KeyFlagCertify == 0 || isValidCertificationKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo)) &&
(flags&packet.KeyFlagSign == 0 || isValidSigningKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo)) &&
(flags&packet.KeyFlagCertify == 0 || isValidCertificationKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo, config)) &&
(flags&packet.KeyFlagSign == 0 || isValidSigningKey(subkeySelfSig, subkey.PublicKey.PubKeyAlgo, config)) &&
checkKeyRequirements(subkey.PublicKey, config) == nil &&
(maxTime.IsZero() || subkeySelfSig.CreationTime.Unix() >= maxTime.Unix()) &&
(id == 0 || subkey.PublicKey.KeyId == id) {
Expand All @@ -243,8 +243,8 @@ func (e *Entity) signingKeyByIdUsage(now time.Time, id uint64, flags int, config

// If we don't have any subkeys for signing and the primary key
// is marked as OK to sign with, then we can use it.
if (flags&packet.KeyFlagCertify == 0 || isValidCertificationKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo)) &&
(flags&packet.KeyFlagSign == 0 || isValidSigningKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo)) &&
if (flags&packet.KeyFlagCertify == 0 || isValidCertificationKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo, config)) &&
(flags&packet.KeyFlagSign == 0 || isValidSigningKey(primarySelfSignature, e.PrimaryKey.PubKeyAlgo, config)) &&
(id == 0 || e.PrimaryKey.KeyId == id) {
return Key{
Entity: e,
Expand Down Expand Up @@ -770,20 +770,38 @@ func checkKeyRequirements(usedKey *packet.PublicKey, config *packet.Config) erro
return nil
}

func isValidSigningKey(signature *packet.Signature, algo packet.PublicKeyAlgorithm) bool {
return algo.CanSign() &&
signature.FlagsValid &&
signature.FlagSign
func isValidSigningKey(signature *packet.Signature, algo packet.PublicKeyAlgorithm, config *packet.Config) bool {
if !algo.CanSign() {
return false
}

if signature.FlagsValid {
return signature.FlagSign
}

return config.AllowAllKeyFlagsWhenMissing()
}

func isValidCertificationKey(signature *packet.Signature, algo packet.PublicKeyAlgorithm) bool {
return algo.CanSign() &&
signature.FlagsValid &&
signature.FlagCertify
func isValidCertificationKey(signature *packet.Signature, algo packet.PublicKeyAlgorithm, config *packet.Config) bool {
if !algo.CanSign() {
return false
}

if signature.FlagsValid {
return signature.FlagCertify
}

return config.AllowAllKeyFlagsWhenMissing()
}

func isValidEncryptionKey(signature *packet.Signature, algo packet.PublicKeyAlgorithm) bool {
return algo.CanEncrypt() &&
signature.FlagsValid &&
(signature.FlagEncryptCommunications || signature.FlagEncryptStorage)
func isValidEncryptionKey(signature *packet.Signature, algo packet.PublicKeyAlgorithm, config *packet.Config) bool {
if !algo.CanEncrypt() {
return false
}

if signature.FlagsValid {
return signature.FlagEncryptCommunications || signature.FlagEncryptStorage
}

return config.AllowAllKeyFlagsWhenMissing()
}
57 changes: 55 additions & 2 deletions openpgp/v2/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,6 @@ func TestKeyWithRevokedSubKey(t *testing.T) {
if len(subKey.Bindings) == 0 {
t.Fatalf("no binding subkey signature")
}

}

func TestSubkeyRevocation(t *testing.T) {
Expand Down Expand Up @@ -683,7 +682,6 @@ func TestKeyWithSubKeyAndBadSelfSigOrder(t *testing.T) {
if lifetime := selfSig.KeyLifetimeSecs; lifetime != nil {
t.Errorf("The signature has a key lifetime (%d), but it should be nil", *lifetime)
}

}

func TestIdVerification(t *testing.T) {
Expand Down Expand Up @@ -1063,6 +1061,7 @@ func TestAddUserId(t *testing.T) {
t.Fatal(err)
}
}

func TestAddSubkey(t *testing.T) {
entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", nil)
if err != nil {
Expand Down Expand Up @@ -2022,3 +2021,57 @@ NciH07RTRuMS/aRhRg4OB8PQROmTnZ+iZS0=
t.Fatal(err)
}
}

func TestAllowAllKeyFlagsWhenMissing(t *testing.T) {
// Make a master key.
entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", nil)
if err != nil {
t.Fatal(err)
}

config := &packet.Config{}

primarySelfSignature, err := entity.VerifyPrimaryKey(time.Now(), config)
if err != nil {
t.Fatal(err)
}

if !entity.PrimaryKey.PubKeyAlgo.CanEncrypt() ||
!entity.PrimaryKey.PubKeyAlgo.CanSign() {
t.Fatal("PubKeyAlgo must be valid for signature and encryption")
}

/// Flags valid, but not set.
primarySelfSignature.FlagsValid = true
primarySelfSignature.FlagSign = false
primarySelfSignature.FlagCertify = false
primarySelfSignature.FlagEncryptCommunications = false

if isValidSigningKey(primarySelfSignature, entity.PrimaryKey.PubKeyAlgo, config) {
t.Error("isValidSigningKey must be false")
}

if isValidEncryptionKey(primarySelfSignature, entity.PrimaryKey.PubKeyAlgo, config) {
t.Error("isValidEncryptionKey must be false")
}

if isValidCertificationKey(primarySelfSignature, entity.PrimaryKey.PubKeyAlgo, config) {
t.Error("isValidCertificationKey must be false")
}

/// Flags not valid, but InsecureAllowAllKeyFlagsWhenMissing set.
primarySelfSignature.FlagsValid = false
config = &packet.Config{InsecureAllowAllKeyFlagsWhenMissing: true}

if !isValidSigningKey(primarySelfSignature, entity.PrimaryKey.PubKeyAlgo, config) {
t.Error("isValidSigningKey must be true when InsecureAllowAllKeyFlagsWhenMissing is true")
}

if !isValidEncryptionKey(primarySelfSignature, entity.PrimaryKey.PubKeyAlgo, config) {
t.Error("isValidEncryptionKey must be true when InsecureAllowAllKeyFlagsWhenMissing is true")
}

if !isValidCertificationKey(primarySelfSignature, entity.PrimaryKey.PubKeyAlgo, config) {
t.Error("isValidCertificationKey must be true when InsecureAllowAllKeyFlagsWhenMissing is true")
}
}