mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-11-13 14:37:18 +00:00
Merge pull request #1629 from daniel-pinder/pgp-accept-multisigned-releases
This commit is contained in:
commit
28609a918e
19 files changed
+418
-19
No files matched your search
+98
-4
@@ -209,6 +209,40 @@ func (g *GpgVerifier) argsKeyrings() (args []string) {
|
||||
return
|
||||
}
|
||||
|
||||
type gpgvSignatureDisposition int
|
||||
|
||||
const (
|
||||
gpgvSignatureOther gpgvSignatureDisposition = iota
|
||||
gpgvSignatureGood
|
||||
gpgvSignatureUnknown
|
||||
gpgvSignatureDisqualified
|
||||
)
|
||||
|
||||
type gpgvSignatureStatus struct {
|
||||
goodSig bool
|
||||
validSig bool
|
||||
missingKeyErrSig bool
|
||||
noPubKey bool
|
||||
disqualified bool
|
||||
otherFailure bool
|
||||
}
|
||||
|
||||
func (status gpgvSignatureStatus) disposition() gpgvSignatureDisposition {
|
||||
if status.disqualified {
|
||||
return gpgvSignatureDisqualified
|
||||
}
|
||||
if status.otherFailure {
|
||||
return gpgvSignatureOther
|
||||
}
|
||||
if status.goodSig && status.validSig {
|
||||
return gpgvSignatureGood
|
||||
}
|
||||
if status.missingKeyErrSig && status.noPubKey && !status.goodSig && !status.validSig {
|
||||
return gpgvSignatureUnknown
|
||||
}
|
||||
return gpgvSignatureOther
|
||||
}
|
||||
|
||||
func (g *GpgVerifier) runGpgv(args []string, context string, showKeyTip bool) (*KeyInfo, error) {
|
||||
args = append([]string{"--status-fd", "3"}, args...)
|
||||
cmd := exec.Command(g.gpgv, args...)
|
||||
@@ -255,22 +289,82 @@ func (g *GpgVerifier) runGpgv(args []string, context string, showKeyTip bool) (*
|
||||
statusr := bufio.NewScanner(tempf)
|
||||
|
||||
result := &KeyInfo{}
|
||||
statuses := []gpgvSignatureStatus{}
|
||||
var status *gpgvSignatureStatus
|
||||
flushStatus := func() {
|
||||
if status != nil {
|
||||
statuses = append(statuses, *status)
|
||||
}
|
||||
}
|
||||
|
||||
for statusr.Scan() {
|
||||
line := strings.TrimSpace(statusr.Text())
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) >= 2 && fields[0] == "[GNUPG:]" && fields[1] == "NEWSIG" {
|
||||
flushStatus()
|
||||
status = &gpgvSignatureStatus{}
|
||||
continue
|
||||
}
|
||||
if status == nil {
|
||||
status = &gpgvSignatureStatus{otherFailure: true}
|
||||
}
|
||||
if len(fields) < 2 || fields[0] != "[GNUPG:]" {
|
||||
status.otherFailure = true
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "[GNUPG:] GOODSIG ") {
|
||||
result.GoodKeys = append(result.GoodKeys, Key(strings.Fields(line)[2]))
|
||||
} else if strings.HasPrefix(line, "[GNUPG:] NO_PUBKEY ") {
|
||||
result.MissingKeys = append(result.MissingKeys, Key(strings.Fields(line)[2]))
|
||||
switch fields[1] {
|
||||
case "GOODSIG":
|
||||
if len(fields) < 3 {
|
||||
status.otherFailure = true
|
||||
continue
|
||||
}
|
||||
status.goodSig = true
|
||||
result.GoodKeys = append(result.GoodKeys, Key(fields[2]))
|
||||
case "VALIDSIG":
|
||||
status.validSig = true
|
||||
case "NO_PUBKEY":
|
||||
if len(fields) < 3 {
|
||||
status.otherFailure = true
|
||||
continue
|
||||
}
|
||||
status.noPubKey = true
|
||||
result.MissingKeys = append(result.MissingKeys, Key(fields[2]))
|
||||
case "ERRSIG":
|
||||
// Field 7 is the gpg-error reason; 9 is GPG_ERR_NO_PUBKEY.
|
||||
if len(fields) > 7 && fields[7] == "9" {
|
||||
status.missingKeyErrSig = true
|
||||
} else {
|
||||
status.otherFailure = true
|
||||
}
|
||||
case "BADSIG", "EXPSIG", "EXPKEYSIG", "REVKEYSIG":
|
||||
status.disqualified = true
|
||||
}
|
||||
}
|
||||
flushStatus()
|
||||
|
||||
if err = statusr.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cmderr != nil {
|
||||
goodSignatures := 0
|
||||
unknownSignatures := 0
|
||||
failureExplained := cmd.ProcessState != nil && cmd.ProcessState.Exited()
|
||||
for _, signatureStatus := range statuses {
|
||||
switch signatureStatus.disposition() {
|
||||
case gpgvSignatureGood:
|
||||
goodSignatures++
|
||||
case gpgvSignatureUnknown:
|
||||
unknownSignatures++
|
||||
default:
|
||||
failureExplained = false
|
||||
}
|
||||
}
|
||||
if failureExplained && goodSignatures > 0 && unknownSignatures > 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if showKeyTip && len(g.keyRings) == 0 && len(result.MissingKeys) > 0 {
|
||||
fmt.Printf("\nLooks like some keys are missing in your trusted keyring, you may consider importing them from keyserver:\n\n")
|
||||
|
||||
|
||||
@@ -93,8 +93,14 @@ func (s *Gnupg1VerifierSuite) SetUpTest(c *C) {
|
||||
|
||||
s.verifier = NewGpgVerifier(finder)
|
||||
s.verifier.AddKeyring("./trusted.gpg")
|
||||
s.multisigOneVerifier = NewGpgVerifier(finder)
|
||||
s.multisigOneVerifier.AddKeyring("./multisig-one.gpg")
|
||||
s.multisigBothVerifier = NewGpgVerifier(finder)
|
||||
s.multisigBothVerifier.AddKeyring("./multisig-both.gpg")
|
||||
|
||||
c.Assert(s.verifier.InitKeyring(false), IsNil)
|
||||
c.Assert(s.multisigOneVerifier.InitKeyring(false), IsNil)
|
||||
c.Assert(s.multisigBothVerifier.InitKeyring(false), IsNil)
|
||||
}
|
||||
|
||||
type Gnupg1SignerSuite struct {
|
||||
@@ -142,8 +148,191 @@ func (s *Gnupg2VerifierSuite) SetUpTest(c *C) {
|
||||
|
||||
s.verifier = NewGpgVerifier(finder)
|
||||
s.verifier.AddKeyring("./trusted.gpg")
|
||||
s.multisigOneVerifier = NewGpgVerifier(finder)
|
||||
s.multisigOneVerifier.AddKeyring("./multisig-one.gpg")
|
||||
s.multisigBothVerifier = NewGpgVerifier(finder)
|
||||
s.multisigBothVerifier.AddKeyring("./multisig-both.gpg")
|
||||
|
||||
c.Assert(s.verifier.InitKeyring(false), IsNil)
|
||||
c.Assert(s.multisigOneVerifier.InitKeyring(false), IsNil)
|
||||
c.Assert(s.multisigBothVerifier.InitKeyring(false), IsNil)
|
||||
}
|
||||
|
||||
// The status fixtures were generated with GnuPG 2.4 at fixed times so the
|
||||
// expired key and subkey remain expired without making the other keys age out:
|
||||
//
|
||||
// export GNUPGHOME=$(mktemp -d); chmod 700 "$GNUPGHOME"
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-generate-key 'Aptly status good fixture <good@fixture.invalid>' rsa2048 sign 0
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-generate-key 'Aptly status expired fixture <expired@fixture.invalid>' rsa2048 sign seconds=5
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-generate-key 'Aptly status unknown fixture <unknown-status@fixture.invalid>' rsa2048 sign 0
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-generate-key 'Aptly status revoked fixture <revoked@fixture.invalid>' rsa2048 sign 0
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-generate-key 'Aptly unused expired subkey fixture <subkey@fixture.invalid>' rsa2048 cert 0
|
||||
// good=A35DB05543372EE999F6EF74EBC0B25127B9BF5A expired=DA1728275CB25D14DC8B6DC97D0DD4A295844522 unknown=A068D4E79AD454D37C0888F5B316843CAB67504E
|
||||
// revoked=CD11F407D4CEB092E66A83EC402BF908BCB202A9 primary=E1745C372D24AC29BD57FAB094ABAB0251F5E2FC active=724D68416C95AA282D8280DF22EE1E88523FD531
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-add-key "$primary" rsa2048 sign 0
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' --faked-system-time 20250101T000000 --quick-add-key "$primary" rsa2048 sign seconds=5
|
||||
// gpg --batch --yes --faked-system-time 20250101T000002 --local-user "$good!" --local-user "$expired!" --local-user "$unknown!" --detach-sign --output status-expired-held.signature multisig.text
|
||||
// gpg --batch --yes --export "$good" "$expired" > status-expired-held.gpg
|
||||
// gpg --batch --yes --faked-system-time 20250101T000002 --local-user "$good!" --local-user "$revoked!" --local-user "$unknown!" --detach-sign --output status-revoked-held.signature multisig.text
|
||||
// sed 's/^://' "$GNUPGHOME/openpgp-revocs.d/$revoked.rev" | gpg --batch --yes --import
|
||||
// gpg --batch --yes --export "$good" "$revoked" > status-revoked-held.gpg
|
||||
// gpg --batch --yes --faked-system-time 20260902T143800 --local-user "$active!" --local-user "$unknown!" --detach-sign --output status-unused-expired-subkey.signature multisig.text
|
||||
// gpg --batch --yes --export "$primary" > status-unused-expired-subkey.gpg
|
||||
|
||||
func verifyGpgStatusFixture(c *C, finder GPGFinder, keyring, signature string) error {
|
||||
verifier := NewGpgVerifier(finder)
|
||||
verifier.AddKeyring(keyring)
|
||||
c.Assert(verifier.InitKeyring(false), IsNil)
|
||||
|
||||
cleartext, err := os.Open("multisig.text")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = cleartext.Close() }()
|
||||
|
||||
signatureFile, err := os.Open(signature)
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = signatureFile.Close() }()
|
||||
|
||||
return verifier.VerifyDetachedSignature(signatureFile, cleartext, false)
|
||||
}
|
||||
|
||||
func (s *Gnupg2VerifierSuite) TestGpgVerifierRejectsExpiredHeldSignatureAlongsideGood(c *C) {
|
||||
err := verifyGpgStatusFixture(c, GPG2Finder(), "./status-expired-held.gpg", "status-expired-held.signature")
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *Gnupg2VerifierSuite) TestGpgVerifierRejectsRevokedHeldSignatureAlongsideGood(c *C) {
|
||||
err := verifyGpgStatusFixture(c, GPG2Finder(), "./status-revoked-held.gpg", "status-revoked-held.signature")
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *Gnupg2VerifierSuite) TestGpgVerifierIgnoresUnusedExpiredSubkey(c *C) {
|
||||
err := verifyGpgStatusFixture(c, GPG2Finder(), "./status-unused-expired-subkey.gpg", "status-unused-expired-subkey.signature")
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
type fixedGPGFinder struct {
|
||||
gpgv string
|
||||
}
|
||||
|
||||
func (f fixedGPGFinder) FindGPG() (string, GPGVersion, error) {
|
||||
return f.gpgv, GPG22xPlus, nil
|
||||
}
|
||||
|
||||
func (f fixedGPGFinder) FindGPGV() (string, GPGVersion, error) {
|
||||
return f.gpgv, GPG22xPlus, nil
|
||||
}
|
||||
|
||||
func (s *GnupgSuite) TestGpgVerifierRejectsUnexplainedFailure(c *C) {
|
||||
tests := []struct {
|
||||
name string
|
||||
body string
|
||||
wantError bool
|
||||
goodKeys []Key
|
||||
missingKeys []Key
|
||||
}{
|
||||
{
|
||||
name: "unexplained exit",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] GOODSIG 0123456789ABCDEF held' \
|
||||
'[GNUPG:] VALIDSIG 00000000000000000123456789ABCDEF 0 0 0 0 0 0 0 00 00000000000000000123456789ABCDEF' >&3
|
||||
exit 70
|
||||
`,
|
||||
wantError: true,
|
||||
goodKeys: []Key{"0123456789ABCDEF"},
|
||||
},
|
||||
{
|
||||
name: "unknown co-signer",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] GOODSIG 0123456789ABCDEF held' \
|
||||
'[GNUPG:] VALIDSIG 00000000000000000123456789ABCDEF 0 0 0 0 0 0 0 00 00000000000000000123456789ABCDEF' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] ERRSIG FEDCBA9876543210 1 10 00 0 9 0000000000000000FEDCBA9876543210' \
|
||||
'[GNUPG:] NO_PUBKEY FEDCBA9876543210' >&3
|
||||
exit 2
|
||||
`,
|
||||
goodKeys: []Key{"0123456789ABCDEF"},
|
||||
missingKeys: []Key{"FEDCBA9876543210"},
|
||||
},
|
||||
{
|
||||
name: "non-missing-key ERRSIG",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] GOODSIG 0123456789ABCDEF held' \
|
||||
'[GNUPG:] VALIDSIG 00000000000000000123456789ABCDEF 0 0 0 0 0 0 0 00 00000000000000000123456789ABCDEF' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] ERRSIG FEDCBA9876543210 1 10 00 0 1 0000000000000000FEDCBA9876543210' >&3
|
||||
exit 2
|
||||
`,
|
||||
wantError: true,
|
||||
goodKeys: []Key{"0123456789ABCDEF"},
|
||||
},
|
||||
{
|
||||
name: "empty signature block",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] GOODSIG 0123456789ABCDEF held' \
|
||||
'[GNUPG:] VALIDSIG 00000000000000000123456789ABCDEF 0 0 0 0 0 0 0 00 00000000000000000123456789ABCDEF' \
|
||||
'[GNUPG:] NEWSIG' >&3
|
||||
exit 2
|
||||
`,
|
||||
wantError: true,
|
||||
goodKeys: []Key{"0123456789ABCDEF"},
|
||||
},
|
||||
{
|
||||
name: "GOODSIG without VALIDSIG",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] GOODSIG 0123456789ABCDEF held' >&3
|
||||
exit 2
|
||||
`,
|
||||
wantError: true,
|
||||
goodKeys: []Key{"0123456789ABCDEF"},
|
||||
},
|
||||
{
|
||||
name: "signal death",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] GOODSIG 0123456789ABCDEF held' \
|
||||
'[GNUPG:] VALIDSIG 00000000000000000123456789ABCDEF 0 0 0 0 0 0 0 00 00000000000000000123456789ABCDEF' >&3
|
||||
kill -TERM "$$"
|
||||
`,
|
||||
wantError: true,
|
||||
goodKeys: []Key{"0123456789ABCDEF"},
|
||||
},
|
||||
{
|
||||
name: "exit zero preserves upstream behavior",
|
||||
body: `#!/bin/sh
|
||||
printf '%s\n' \
|
||||
'[GNUPG:] NEWSIG' \
|
||||
'[GNUPG:] BADSIG 0123456789ABCDEF held' >&3
|
||||
exit 0
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
directory := c.MkDir()
|
||||
gpgv := filepath.Join(directory, "gpgv")
|
||||
c.Assert(os.WriteFile(gpgv, []byte(test.body), 0o700), IsNil)
|
||||
|
||||
verifier := NewGpgVerifier(fixedGPGFinder{gpgv: gpgv})
|
||||
keyInfo, err := verifier.runGpgv(nil, "detached signature", false)
|
||||
if test.wantError {
|
||||
c.Check(err, NotNil, Commentf(test.name))
|
||||
} else {
|
||||
c.Check(err, IsNil, Commentf(test.name))
|
||||
}
|
||||
c.Check(keyInfo.GoodKeys, DeepEquals, test.goodKeys, Commentf(test.name))
|
||||
c.Check(keyInfo.MissingKeys, DeepEquals, test.missingKeys, Commentf(test.name))
|
||||
}
|
||||
}
|
||||
|
||||
type Gnupg2SignerSuite struct {
|
||||
|
||||
@@ -13,8 +13,14 @@ var _ = Suite(&GoVerifierSuite{})
|
||||
func (s *GoVerifierSuite) SetUpTest(c *C) {
|
||||
s.verifier = &GoVerifier{}
|
||||
s.verifier.AddKeyring("./trusted.gpg")
|
||||
s.multisigOneVerifier = &GoVerifier{}
|
||||
s.multisigOneVerifier.AddKeyring("./multisig-one.gpg")
|
||||
s.multisigBothVerifier = &GoVerifier{}
|
||||
s.multisigBothVerifier.AddKeyring("./multisig-both.gpg")
|
||||
|
||||
c.Assert(s.verifier.InitKeyring(false), IsNil)
|
||||
c.Assert(s.multisigOneVerifier.InitKeyring(false), IsNil)
|
||||
c.Assert(s.multisigBothVerifier.InitKeyring(false), IsNil)
|
||||
}
|
||||
|
||||
type GoSignerSuite struct {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
tampered aptly multi-signature verification fixture
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
aptly multi-signature verification fixture
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQEzBAEBCgAdFiEEqYoHYeLwl1cZL9VhhT6Gnff0SYYFAmrmaZIACgkQhT6Gnff0
|
||||
SYYKYAgAujy2QKb0Gjg9YSX16Ff5XCkd/XJSVwSHxVfX5TXN2daby+a3fns78Z9u
|
||||
gbNF8qB0n6P8Q8xpPvrvnx125yJidutBR77Y1fjBm5Ks6N/x1kWg9lIXwS5eYglA
|
||||
I7Ctm3vEdD96wyYnHiBOo9sufasrfX1CnCBd1q1MkDrNpaiYmvSMeK+f6Ozs7OK2
|
||||
IfYNB5dNsI/LI65bKbgGFiOXoqMlXYFDn9GA734jrk3CvOImks4Q9GEi/qhaPKHe
|
||||
sU9lZ9KU9Okj9Yy96c7Dk29iGaxhhKTiZ+2UGvyczVRenf2+v5t8/M11CgagApPP
|
||||
nq8MRgPBKk6BdMgMd0SyZZhPj0VEpokBMwQBAQoAHRYhBOyPYRMwLkNvKOuL0hPZ
|
||||
BG62qCWlBQJq5mmSAAoJEBPZBG62qCWlgv8H/1CEchjYZDgPWFu7cZE+abBPByTX
|
||||
VcYU62CQPBpkxJ00pviuKgYRzQX0FCxQxpQOQlHv3ToVzL3a62m1vrTRa3NhT0U8
|
||||
DYdRxcW8o/11hnY3VeiPavOvORhwn70FeBYtYw7BipHHxwOElh7Ey5dkzHS+Z2OL
|
||||
0kCNvltjKEFZM+q4w2yPbSpam7wCS5gYLl0d+99ornPtzDwBOreXFoC1MER3y1hB
|
||||
H3XbLqggE6gfumfiGR9Hljvh5CT/ebk7RwMWCcrp3/MdXwfpH+6JGg1AvF/TwmEi
|
||||
XAl8LljwTybylH+8wKAHYX4ed7GvJBOigJth+3o7sh8n7oqL0YJH0JoSjbM=
|
||||
=ThV5
|
||||
-----END PGP SIGNATURE-----
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
aptly multi-signature verification fixture
|
||||
+3
-1
@@ -63,7 +63,9 @@ func checkDetachedSignature(keyring openpgp.KeyRing, signed, signature io.Reader
|
||||
for {
|
||||
p, err = packets.Next()
|
||||
if err == io.EOF {
|
||||
if len(signers) == 0 || missingKeys > 0 {
|
||||
// Revocation and expiry of held keys deliberately remain unchanged
|
||||
// from upstream in this provider; only unknown co-signers are ignored.
|
||||
if len(signers) == missingKeys {
|
||||
err = errors.ErrUnknownIssuer
|
||||
} else {
|
||||
err = nil
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+99
-14
@@ -10,9 +10,33 @@ import (
|
||||
|
||||
// Common set of tests shared by internal & external GnuPG implementations
|
||||
type VerifierSuite struct {
|
||||
verifier Verifier
|
||||
verifier Verifier
|
||||
multisigOneVerifier Verifier
|
||||
multisigBothVerifier Verifier
|
||||
}
|
||||
|
||||
// The partial-keyring fixtures were generated with GnuPG 2.4 at a fixed time
|
||||
// so they remain valid under the CI faketime window (2025-01-02):
|
||||
//
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' \
|
||||
// --faked-system-time 20250101T000000 \
|
||||
// --quick-generate-key 'Aptly multisign known fixture <known@fixture.invalid>' rsa2048 sign 0
|
||||
// gpg --batch --pinentry-mode loopback --passphrase '' \
|
||||
// --faked-system-time 20250101T000000 \
|
||||
// --quick-generate-key 'Aptly multisign unknown fixture <unknown@fixture.invalid>' rsa2048 sign 0
|
||||
// gpg --export <FPR_known> <FPR_unknown> > multisig-both.gpg
|
||||
// gpg --export <FPR_known> > multisig-one.gpg
|
||||
// gpg --local-user <FPR_known> --local-user <FPR_unknown> \
|
||||
// --detach-sign --output multisig.signature multisig.text
|
||||
// gpg --local-user <FPR_known> --local-user <FPR_unknown> \
|
||||
// --clearsign --output multisig.clearsigned multisig.text
|
||||
// gpg --local-user <FPR_known> --detach-sign --output multisig-known-good.signature multisig.text
|
||||
// gpg --local-user <FPR_unknown> --detach-sign --output multisig-known-bad.signature multisig-bad.text
|
||||
//
|
||||
// Replace <FPR_known> and <FPR_unknown> with the actual fingerprints from step 2.
|
||||
//
|
||||
// multisig-bad.text was made by changing multisig.text after signing it.
|
||||
|
||||
func (s *VerifierSuite) TestVerifyDetached(c *C) {
|
||||
for _, test := range []struct {
|
||||
textName, signatureName string
|
||||
@@ -37,21 +61,82 @@ func (s *VerifierSuite) TestVerifyDetached(c *C) {
|
||||
}
|
||||
|
||||
func (s *VerifierSuite) TestVerifyClearsigned(c *C) {
|
||||
for _, test := range []struct {
|
||||
clearSignedName string
|
||||
}{
|
||||
{"1.clearsigned"},
|
||||
} {
|
||||
clearsigned, err := os.Open(test.clearSignedName)
|
||||
c.Assert(err, IsNil)
|
||||
clearsigned, err := os.Open("multisig.clearsigned")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = clearsigned.Close() }()
|
||||
|
||||
keyInfo, err := s.verifier.VerifyClearsigned(clearsigned, false)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"04EE7237B7D453EC", "648ACFD622F3D138", "DCC9EFBF77E11517"})
|
||||
c.Check(keyInfo.MissingKeys, DeepEquals, []Key(nil))
|
||||
keyInfo, err := s.multisigBothVerifier.VerifyClearsigned(clearsigned, false)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"853E869DF7F44986", "13D9046EB6A825A5"})
|
||||
c.Check(keyInfo.MissingKeys, DeepEquals, []Key(nil))
|
||||
}
|
||||
|
||||
_ = clearsigned.Close()
|
||||
}
|
||||
func (s *VerifierSuite) TestVerifyClearsignedPartialKeyring(c *C) {
|
||||
clearsigned, err := os.Open("multisig.clearsigned")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = clearsigned.Close() }()
|
||||
|
||||
keyInfo, err := s.multisigOneVerifier.VerifyClearsigned(clearsigned, false)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"853E869DF7F44986"})
|
||||
c.Check(keyInfo.MissingKeys, DeepEquals, []Key{"13D9046EB6A825A5"})
|
||||
}
|
||||
|
||||
func (s *VerifierSuite) TestVerifyDetachedPartialKeyring(c *C) {
|
||||
cleartext, err := os.Open("multisig.text")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = cleartext.Close() }()
|
||||
|
||||
signature, err := os.Open("multisig.signature")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = signature.Close() }()
|
||||
|
||||
err = s.multisigOneVerifier.VerifyDetachedSignature(signature, cleartext, false)
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *VerifierSuite) TestVerifyPartialKeyringRejectsBadSignature(c *C) {
|
||||
cleartext, err := os.Open("multisig-bad.text")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = cleartext.Close() }()
|
||||
|
||||
signature, err := os.Open("multisig.signature")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = signature.Close() }()
|
||||
|
||||
err = s.multisigOneVerifier.VerifyDetachedSignature(signature, cleartext, false)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *VerifierSuite) TestVerifyRejectsBadSignatureAlongsideGood(c *C) {
|
||||
cleartext, err := os.Open("multisig.text")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = cleartext.Close() }()
|
||||
|
||||
goodSignature, err := os.Open("multisig-known-good.signature")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = goodSignature.Close() }()
|
||||
|
||||
badSignature, err := os.Open("multisig-known-bad.signature")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = badSignature.Close() }()
|
||||
|
||||
signature := io.MultiReader(goodSignature, badSignature)
|
||||
err = s.multisigBothVerifier.VerifyDetachedSignature(signature, cleartext, false)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *VerifierSuite) TestVerifyNoKnownSignerStillFails(c *C) {
|
||||
cleartext, err := os.Open("multisig.text")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = cleartext.Close() }()
|
||||
|
||||
signature, err := os.Open("multisig.signature")
|
||||
c.Assert(err, IsNil)
|
||||
defer func() { _ = signature.Close() }()
|
||||
|
||||
err = s.verifier.VerifyDetachedSignature(signature, cleartext, false)
|
||||
c.Assert(err, NotNil)
|
||||
}
|
||||
|
||||
func (s *VerifierSuite) TestExtractClearsigned(c *C) {
|
||||
|
||||
Reference in new issue
Block a user