Skip to content

Commit

Permalink
Allows Key Flags override, fixes #205
Browse files Browse the repository at this point in the history
Added InsecureAllowAllKeyFlagsWhenMissing to packet.Config.
Allows usage of keys without key flags.
  • Loading branch information
davrux authored and twiss committed Feb 20, 2025
1 parent ef10ec5 commit 68c5c41
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 22 deletions.
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", "[email protected]", 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", "[email protected]", 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")
}
}

0 comments on commit 68c5c41

Please sign in to comment.