diff --git a/deb/remote.go b/deb/remote.go index a1a79d689..5b0386ca0 100644 --- a/deb/remote.go +++ b/deb/remote.go @@ -417,7 +417,7 @@ func (repo *RemoteRepo) Fetch(d aptly.Downloader, verifier pgp.Verifier, ignoreS return err } - err = verifier.VerifyDetachedSignature(releasesig, release, true) + _, err = verifier.VerifyDetachedSignature(releasesig, release, true) if err != nil { return err } @@ -600,7 +600,7 @@ func (repo *RemoteRepo) DownloadPackageIndexes(progress aptly.Progress, d aptly. return err } - err = verifier.VerifyDetachedSignature(filesig, packagesFile, false) + _, err = verifier.VerifyDetachedSignature(filesig, packagesFile, false) if err != nil { return err } diff --git a/deb/remote_test.go b/deb/remote_test.go index 4154e0b8a..14f9086f4 100644 --- a/deb/remote_test.go +++ b/deb/remote_test.go @@ -29,8 +29,8 @@ func (n *NullVerifier) InitKeyring(_ bool) error { func (n *NullVerifier) AddKeyring(keyring string) { } -func (n *NullVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, hint bool) error { - return nil +func (n *NullVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, hint bool) (*pgp.KeyInfo, error) { + return &pgp.KeyInfo{}, nil } func (n *NullVerifier) VerifyClearsigned(clearsigned io.Reader, hint bool) (*pgp.KeyInfo, error) { diff --git a/pgp/gnupg.go b/pgp/gnupg.go index 59e405f77..27a294a51 100644 --- a/pgp/gnupg.go +++ b/pgp/gnupg.go @@ -292,12 +292,12 @@ func (g *GpgVerifier) runGpgv(args []string, context string, showKeyTip bool) (* } // VerifyDetachedSignature verifies combination of signature and cleartext using gpgv -func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, showKeyTip bool) error { +func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, showKeyTip bool) (*KeyInfo, error) { args := g.argsKeyrings() sigf, err := os.CreateTemp("", "aptly-gpg") if err != nil { - return err + return nil, err } defer func() { _ = os.Remove(sigf.Name()) @@ -306,12 +306,12 @@ func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, sh _, err = io.Copy(sigf, signature) if err != nil { - return err + return nil, err } clearf, err := os.CreateTemp("", "aptly-gpg") if err != nil { - return err + return nil, err } defer func() { _ = os.Remove(clearf.Name()) @@ -320,12 +320,16 @@ func (g *GpgVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, sh _, err = io.Copy(clearf, cleartext) if err != nil { - return err + return nil, err } args = append(args, sigf.Name(), clearf.Name()) _, err = g.runGpgv(args, "detached signature", showKeyTip) - return err + if err != nil { + return nil, err + } + + return &KeyInfo{}, nil } // IsClearSigned returns true if file contains signature diff --git a/pgp/internal.go b/pgp/internal.go index 8bf214c44..b23bea969 100644 --- a/pgp/internal.go +++ b/pgp/internal.go @@ -392,7 +392,7 @@ func (g *GoVerifier) printLog(signers []signatureResult) { } // VerifyDetachedSignature verifies combination of signature and cleartext using gpgv -func (g *GoVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, showKeyTip bool) error { +func (g *GoVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, showKeyTip bool) (*KeyInfo, error) { var signatureBuf bytes.Buffer signers, missingKeys, err := checkArmoredDetachedSignature(g.trustedKeyring, cleartext, io.TeeReader(signature, &signatureBuf)) @@ -409,10 +409,24 @@ func (g *GoVerifier) VerifyDetachedSignature(signature, cleartext io.Reader, sho } if err != nil { - return errors.Wrap(err, "failed to verify detached signature") + return nil, errors.Wrap(err, "failed to verify detached signature") } - return nil + result := &KeyInfo{} + + for _, signer := range signers { + if signer.Entity != nil { + if signer.IsExpired { + result.ExpiredKeys = append(result.ExpiredKeys, KeyFromUint64(signer.IssuerKeyID)) + } else { + result.GoodKeys = append(result.GoodKeys, KeyFromUint64(signer.IssuerKeyID)) + } + } else { + result.MissingKeys = append(result.MissingKeys, KeyFromUint64(signer.IssuerKeyID)) + } + } + + return result, nil } // IsClearSigned returns true if file contains signature @@ -455,10 +469,13 @@ func (g *GoVerifier) VerifyClearsigned(clearsigned io.Reader, showKeyTip bool) ( for _, signer := range signers { if signer.Entity != nil { - result.GoodKeys = append(result.GoodKeys, KeyFromUint64(signer.IssuerKeyID)) + if signer.IsExpired { + result.ExpiredKeys = append(result.ExpiredKeys, KeyFromUint64(signer.IssuerKeyID)) + } else { + result.GoodKeys = append(result.GoodKeys, KeyFromUint64(signer.IssuerKeyID)) + } } else { result.MissingKeys = append(result.MissingKeys, KeyFromUint64(signer.IssuerKeyID)) - } } diff --git a/pgp/openpgp.go b/pgp/openpgp.go index 335a9ad98..9d7d868cb 100644 --- a/pgp/openpgp.go +++ b/pgp/openpgp.go @@ -40,6 +40,7 @@ func hashForSignature(hashID crypto.Hash, sigType packet.SignatureType) (hash.Ha type signatureResult struct { CreationTime time.Time + IsExpired bool IssuerKeyID uint64 PubKeyAlgo packet.PublicKeyAlgorithm Entity *openpgp.Entity @@ -59,6 +60,8 @@ func checkDetachedSignature(keyring openpgp.KeyRing, signed, signature io.Reader return nil, 0, e } + var now = time.Now() + packets := packet.NewReader(signature) for { p, err = packets.Next() @@ -82,11 +85,14 @@ func checkDetachedSignature(keyring openpgp.KeyRing, signed, signature io.Reader var pubKeyAlgo packet.PublicKeyAlgorithm var keys []openpgp.Key + var sigExpired bool + switch sig := p.(type) { case *packet.Signature: if sig.IssuerKeyId == nil { return nil, 0, errors.StructuralError("signature doesn't have an issuer") } + sigExpired = sig.SigExpired(now) issuerKeyID = *sig.IssuerKeyId hashFunc = sig.Hash sigType = sig.SigType @@ -128,6 +134,7 @@ func checkDetachedSignature(keyring openpgp.KeyRing, signed, signature io.Reader if err == nil { signers = append(signers, signatureResult{ CreationTime: creationTime, + IsExpired: sigExpired || key.PublicKey.KeyExpired(key.SelfSignature, now), IssuerKeyID: issuerKeyID, PubKeyAlgo: pubKeyAlgo, Entity: key.Entity, diff --git a/pgp/pgp.go b/pgp/pgp.go index 228dbaaa0..28a0b4ffe 100644 --- a/pgp/pgp.go +++ b/pgp/pgp.go @@ -36,6 +36,7 @@ func KeyFromUint64(key uint64) Key { type KeyInfo struct { GoodKeys []Key MissingKeys []Key + ExpiredKeys []Key } // Signer interface describes facility implementing signing of files @@ -53,7 +54,7 @@ type Signer interface { type Verifier interface { InitKeyring(verbose bool) error AddKeyring(keyring string) - VerifyDetachedSignature(signature, cleartext io.Reader, showKeyTip bool) error + VerifyDetachedSignature(signature, cleartext io.Reader, showKeyTip bool) (*KeyInfo, error) IsClearSigned(clearsigned io.Reader) (bool, error) VerifyClearsigned(clearsigned io.Reader, showKeyTip bool) (*KeyInfo, error) ExtractClearsigned(clearsigned io.Reader) (text *os.File, err error) diff --git a/pgp/sign_test.go b/pgp/sign_test.go index e51893cc8..cf3f70e35 100644 --- a/pgp/sign_test.go +++ b/pgp/sign_test.go @@ -72,7 +72,7 @@ func (s *SignerSuite) testSignDetached(c *C) { err := s.signer.DetachedSign(s.clearF.Name(), s.signedF.Name()) c.Assert(err, IsNil) - err = s.verifier.VerifyDetachedSignature(s.signedF, s.clearF, false) + _, err = s.verifier.VerifyDetachedSignature(s.signedF, s.clearF, false) c.Assert(err, IsNil) } diff --git a/pgp/verify_test.go b/pgp/verify_test.go index 89fdbc147..e4d77f2d2 100644 --- a/pgp/verify_test.go +++ b/pgp/verify_test.go @@ -16,11 +16,13 @@ type VerifierSuite struct { func (s *VerifierSuite) TestVerifyDetached(c *C) { for _, test := range []struct { textName, signatureName string + expiredKeys []Key }{ - {"1.text", "1.signature"}, - {"2.text", "2.signature"}, - {"3.text", "3.signature"}, - {"4.text", "4.signature"}, + {"1.text", "1.signature", nil}, + {"2.text", "2.signature", nil}, + {"3.text", "3.signature", nil}, + // 4.signature is signed by an expired key (wheezy, 8B48AD6246925553) + {"4.text", "4.signature", []Key{"8B48AD6246925553"}}, } { cleartext, err := os.Open(test.textName) c.Assert(err, IsNil) @@ -28,8 +30,11 @@ func (s *VerifierSuite) TestVerifyDetached(c *C) { signature, err := os.Open(test.signatureName) c.Assert(err, IsNil) - err = s.verifier.VerifyDetachedSignature(signature, cleartext, false) + keyInfo, err := s.verifier.VerifyDetachedSignature(signature, cleartext, false) c.Assert(err, IsNil) + if test.expiredKeys != nil { + c.Check(keyInfo.ExpiredKeys, DeepEquals, test.expiredKeys) + } _ = signature.Close() _ = cleartext.Close() @@ -47,8 +52,10 @@ func (s *VerifierSuite) TestVerifyClearsigned(c *C) { keyInfo, err := s.verifier.VerifyClearsigned(clearsigned, false) c.Assert(err, IsNil) - c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"04EE7237B7D453EC", "648ACFD622F3D138", "DCC9EFBF77E11517"}) + // 04EE7237B7D453EC (stretch) is expired and must appear in ExpiredKeys, not GoodKeys + c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"648ACFD622F3D138", "DCC9EFBF77E11517"}) c.Check(keyInfo.MissingKeys, DeepEquals, []Key(nil)) + c.Check(keyInfo.ExpiredKeys, DeepEquals, []Key{"04EE7237B7D453EC"}) _ = clearsigned.Close() }