Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
293521eeab |
No files matched your search
+3
-2
@@ -153,8 +153,9 @@ that key (or one of its subkeys), and `signer` is that key's fingerprint. The
|
|||||||
reference implementation refuses to load a manifest that fails these checks;
|
reference implementation refuses to load a manifest that fails these checks;
|
||||||
`check` and `fetch` given `--require-signature` then compare the required
|
`check` and `fetch` given `--require-signature` then compare the required
|
||||||
fingerprint with `signer`. It also refuses a manifest whose `signingPubKey`
|
fingerprint with `signer`. It also refuses a manifest whose `signingPubKey`
|
||||||
holds a DSA key or subkey, since checking the self-signatures of a DSA key with
|
holds a DSA key or subkey, or any secret key or subkey, since checking the
|
||||||
very large numbers can take hours.
|
self-signatures of a DSA key or the numbers of a secret key can take hours when
|
||||||
|
those numbers are very large.
|
||||||
|
|
||||||
## Deterministic Serialization
|
## Deterministic Serialization
|
||||||
|
|
||||||
|
|||||||
+20
-7
@@ -46,6 +46,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
errKeyCount = errors.New("must hold exactly one key")
|
errKeyCount = errors.New("must hold exactly one key")
|
||||||
errDSAKey = errors.New("must not hold a DSA key")
|
errDSAKey = errors.New("must not hold a DSA key")
|
||||||
|
errSecretKey = errors.New("must not hold a secret key")
|
||||||
errNoSecretKey = errors.New("signing key file holds no secret key")
|
errNoSecretKey = errors.New("signing key file holds no secret key")
|
||||||
errNotV4Key = errors.New("signing key must be an OpenPGP version 4 key, " +
|
errNotV4Key = errors.New("signing key must be an OpenPGP version 4 key, " +
|
||||||
"the only kind whose fingerprint --require-signature takes")
|
"the only kind whose fingerprint --require-signature takes")
|
||||||
@@ -126,7 +127,7 @@ func readSigningKey(opts *SigningOptions) (*openpgp.Entity, error) {
|
|||||||
// readSecretKey returns the one key in secretKey, armored or binary,
|
// readSecretKey returns the one key in secretKey, armored or binary,
|
||||||
// which must be a version 4 key and include its secret key.
|
// which must be a version 4 key and include its secret key.
|
||||||
func readSecretKey(secretKey []byte) (*openpgp.Entity, error) {
|
func readSecretKey(secretKey []byte) (*openpgp.Entity, error) {
|
||||||
key, err := readOneKey(secretKey, "signing key file")
|
key, err := readOneKey(secretKey, "signing key file", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -144,9 +145,9 @@ func readSecretKey(secretKey []byte) (*openpgp.Entity, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readOneKey returns the key in data, armored or binary, which must hold
|
// readOneKey returns the key in data, armored or binary, which must hold
|
||||||
// exactly one primary key and no DSA key or subkey. what names data in
|
// exactly one primary key and no DSA key or subkey, and when publicOnly no
|
||||||
// errors.
|
// secret key or subkey either. what names data in errors.
|
||||||
func readOneKey(data []byte, what string) (*openpgp.Entity, error) {
|
func readOneKey(data []byte, what string, publicOnly bool) (*openpgp.Entity, error) {
|
||||||
packets, err := dearmor(data)
|
packets, err := dearmor(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read %s: %w", what, err)
|
return nil, fmt.Errorf("read %s: %w", what, err)
|
||||||
@@ -161,8 +162,20 @@ func readOneKey(data []byte, what string) (*openpgp.Entity, error) {
|
|||||||
return nil, fmt.Errorf("%s %w, found %d", what, errKeyCount, keys)
|
return nil, fmt.Errorf("%s %w, found %d", what, errKeyCount, keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
// openpgp.ReadKeyRing checks every self-signature, and a DSA key with
|
// openpgp.ReadKeyRing checks the numbers of every secret key it reads,
|
||||||
// very large numbers makes each check take seconds to minutes.
|
// and an ElGamal secret subkey with a very large prime makes that take
|
||||||
|
// minutes.
|
||||||
|
secretKeys, err := countPackets(packets, secretKeyPacketTag, secretSubkeyPacketTag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read %s: %w", what, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if publicOnly && secretKeys != 0 {
|
||||||
|
return nil, fmt.Errorf("%s %w", what, errSecretKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// openpgp.ReadKeyRing also checks every self-signature, and a DSA key
|
||||||
|
// with very large numbers makes each check take seconds to minutes.
|
||||||
dsa, err := holdsDSAKey(packets)
|
dsa, err := holdsDSAKey(packets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("read %s: %w", what, err)
|
return nil, fmt.Errorf("read %s: %w", what, err)
|
||||||
@@ -234,7 +247,7 @@ func fingerprint(key *openpgp.Entity) string {
|
|||||||
// and returns that primary key's fingerprint. signature and pubKey may each
|
// and returns that primary key's fingerprint. signature and pubKey may each
|
||||||
// be armored or binary.
|
// be armored or binary.
|
||||||
func verifySignature(data, signature, pubKey []byte) (string, error) {
|
func verifySignature(data, signature, pubKey []byte) (string, error) {
|
||||||
key, err := readOneKey(pubKey, "embedded public key block")
|
key, err := readOneKey(pubKey, "embedded public key block", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ProtonMail/go-crypto/openpgp"
|
"github.com/ProtonMail/go-crypto/openpgp"
|
||||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||||
|
"github.com/ProtonMail/go-crypto/openpgp/elgamal"
|
||||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -607,6 +608,54 @@ func TestManifestRefusesDSAKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// elGamalSecretSubkeyPacket returns a secret subkey packet holding an
|
||||||
|
// ElGamal key. Its numbers are not a working key: loading must refuse the
|
||||||
|
// packet before it uses them.
|
||||||
|
func elGamalSecretSubkeyPacket(t *testing.T) []byte {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
key := packet.NewElGamalPrivateKey(time.Now(), &elgamal.PrivateKey{
|
||||||
|
P: big.NewInt(23), G: big.NewInt(4), Y: big.NewInt(8), X: big.NewInt(3),
|
||||||
|
})
|
||||||
|
key.IsSubkey = true
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
require.NoError(t, key.Serialize(&buf))
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestManifestRefusesSecretKey loads manifests whose embedded public key
|
||||||
|
// block holds a secret key: the key that signed the manifest with its
|
||||||
|
// secret key, or its public key followed by an ElGamal secret subkey.
|
||||||
|
// openpgp.ReadKeyRing checks the numbers of every secret key it reads,
|
||||||
|
// which for an ElGamal key with a very large prime takes minutes. Loading
|
||||||
|
// must refuse each block before that.
|
||||||
|
func TestManifestRefusesSecretKey(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
signer := newTestKey(t, nil)
|
||||||
|
manifest := signedTestManifest(t,
|
||||||
|
&SigningOptions{SecretKey: armoredSecretKeys(t, signer)})
|
||||||
|
|
||||||
|
var signerKey bytes.Buffer
|
||||||
|
|
||||||
|
require.NoError(t, signer.Serialize(&signerKey))
|
||||||
|
|
||||||
|
for _, block := range [][]byte{
|
||||||
|
armoredSecretKeys(t, signer),
|
||||||
|
slices.Concat(signerKey.Bytes(), elGamalSecretSubkeyPacket(t)),
|
||||||
|
} {
|
||||||
|
embedded := rewriteOuter(t, manifest, func(outer *MFFileOuter) {
|
||||||
|
outer.SigningPubKey = block
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := NewManifestFromReader(bytes.NewReader(embedded))
|
||||||
|
require.ErrorIs(t, err, errSecretKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestManifestRefusesTwoSignatures loads a manifest whose signature field
|
// TestManifestRefusesTwoSignatures loads a manifest whose signature field
|
||||||
// holds its good signature twice, not armored. Loading must refuse it.
|
// holds its good signature twice, not armored. Loading must refuse it.
|
||||||
func TestManifestRefusesTwoSignatures(t *testing.T) {
|
func TestManifestRefusesTwoSignatures(t *testing.T) {
|
||||||
|
|||||||
Reference in new issue
Block a user