diff --git a/pgp/gnupg.go b/pgp/gnupg.go index 5d416281..ba5f24b3 100644 --- a/pgp/gnupg.go +++ b/pgp/gnupg.go @@ -174,7 +174,7 @@ type GpgVerifier struct { keyRings []string } -// NewGpgVerifier creates a new gpg signer +// NewGpgVerifier creates a new gpg verifier func NewGpgVerifier() *GpgVerifier { gpg, err := findGPG1() if err != nil { @@ -191,18 +191,6 @@ func NewGpgVerifier() *GpgVerifier { // InitKeyring verifies that gpg is installed and some keys are trusted func (g *GpgVerifier) InitKeyring() error { - cmd, err := findGPG1() - if err != nil { - return err - } - g.gpg = cmd - - cmd, err = findGPGV1() - if err != nil { - return err - } - g.gpgv = cmd - if len(g.keyRings) == 0 { // using default keyring output, err := exec.Command(g.gpg, "--no-default-keyring", "--no-auto-check-trustdb", "--keyring", "trustedkeys.gpg", "--list-keys").Output() diff --git a/pgp/gnupg_test.go b/pgp/gnupg_test.go index 670ab114..30e93ca3 100644 --- a/pgp/gnupg_test.go +++ b/pgp/gnupg_test.go @@ -77,3 +77,35 @@ func (s *GnupgSuite) TestGPGVNothing(c *C) { c.Assert(func() { NewGpgVerifier() }, PanicMatches, `Couldn't find a suitable gpgv executable.+`) } + +type Gnupg1VerifierSuite struct { + VerifierSuite +} + +var _ = Suite(&Gnupg1VerifierSuite{}) + +func (s *Gnupg1VerifierSuite) SetUpTest(c *C) { + s.verifier = NewGpgVerifier() + s.verifier.AddKeyring("./trusted.gpg") + + c.Assert(s.verifier.InitKeyring(), IsNil) +} + +type Gnupg1SignerSuite struct { + SignerSuite +} + +var _ = Suite(&Gnupg1SignerSuite{}) + +func (s *Gnupg1SignerSuite) SetUpTest(c *C) { + s.signer = NewGpgSigner() + s.signer.SetBatch(true) + + s.verifier = &GoVerifier{} + s.verifier.AddKeyring("./keyrings/aptly.pub") + s.verifier.AddKeyring("./keyrings/aptly_passphrase.pub") + + c.Assert(s.verifier.InitKeyring(), IsNil) + + s.SignerSuite.SetUpTest(c) +} diff --git a/pgp/internal_test.go b/pgp/internal_test.go index 772e52bd..79fd60d5 100644 --- a/pgp/internal_test.go +++ b/pgp/internal_test.go @@ -1,14 +1,11 @@ package pgp import ( - "io/ioutil" - "os" - . "gopkg.in/check.v1" ) type GoVerifierSuite struct { - verifier Verifier + VerifierSuite } var _ = Suite(&GoVerifierSuite{}) @@ -20,77 +17,21 @@ func (s *GoVerifierSuite) SetUpTest(c *C) { c.Assert(s.verifier.InitKeyring(), IsNil) } -func (s *GoVerifierSuite) TestVerifyDetached(c *C) { - for _, test := range []struct { - textName, signatureName string - }{ - {"1.text", "1.signature"}, - {"2.text", "2.signature"}, - {"3.text", "3.signature"}, - } { - cleartext, err := os.Open(test.textName) - c.Assert(err, IsNil) - - signature, err := os.Open(test.signatureName) - c.Assert(err, IsNil) - - err = s.verifier.VerifyDetachedSignature(signature, cleartext, false) - c.Assert(err, IsNil) - - signature.Close() - cleartext.Close() - } +type GoSignerSuite struct { + SignerSuite } -func (s *GoVerifierSuite) TestVerifyClearsigned(c *C) { - for _, test := range []struct { - clearSignedName string - }{ - {"1.clearsigned"}, - } { - clearsigned, err := os.Open(test.clearSignedName) - c.Assert(err, IsNil) +var _ = Suite(&GoSignerSuite{}) - keyInfo, err := s.verifier.VerifyClearsigned(clearsigned, false) - c.Assert(err, IsNil) - c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"8B48AD6246925553", "7638D0442B90D010"}) - c.Check(keyInfo.MissingKeys, DeepEquals, []Key(nil)) +func (s *GoSignerSuite) SetUpTest(c *C) { + s.signer = &GoSigner{} + s.signer.SetBatch(true) - clearsigned.Close() - } -} - -func (s *GoVerifierSuite) TestExtractClearsigned(c *C) { - for _, test := range []struct { - clearSignedName, clearTextName string - }{ - {"1.clearsigned", "1.cleartext"}, - } { - clearsigned, err := os.Open(test.clearSignedName) - c.Assert(err, IsNil) - - cleartext, err := os.Open(test.clearTextName) - c.Assert(err, IsNil) - - is, err := s.verifier.IsClearSigned(clearsigned) - c.Assert(err, IsNil) - c.Check(is, Equals, true) - - clearsigned.Seek(0, 0) - - extractedF, err := s.verifier.ExtractClearsigned(clearsigned) - c.Assert(err, IsNil) - - expected, err := ioutil.ReadAll(cleartext) - c.Assert(err, IsNil) - - extracted, err := ioutil.ReadAll(extractedF) - c.Assert(err, IsNil) - - c.Check(expected, DeepEquals, extracted) - - extractedF.Close() - clearsigned.Close() - cleartext.Close() - } + s.verifier = &GoVerifier{} + s.verifier.AddKeyring("./keyrings/aptly.pub") + s.verifier.AddKeyring("./keyrings/aptly_passphrase.pub") + + c.Assert(s.verifier.InitKeyring(), IsNil) + + s.SignerSuite.SetUpTest(c) } diff --git a/pgp/keyrings/aptly.pub b/pgp/keyrings/aptly.pub new file mode 100644 index 00000000..08758e43 Binary files /dev/null and b/pgp/keyrings/aptly.pub differ diff --git a/pgp/keyrings/aptly.sec b/pgp/keyrings/aptly.sec new file mode 100644 index 00000000..f90e1c91 Binary files /dev/null and b/pgp/keyrings/aptly.sec differ diff --git a/pgp/keyrings/aptly_passphrase.pub b/pgp/keyrings/aptly_passphrase.pub new file mode 100644 index 00000000..ec24aa32 Binary files /dev/null and b/pgp/keyrings/aptly_passphrase.pub differ diff --git a/pgp/keyrings/aptly_passphrase.sec b/pgp/keyrings/aptly_passphrase.sec new file mode 100644 index 00000000..2ffe24ef Binary files /dev/null and b/pgp/keyrings/aptly_passphrase.sec differ diff --git a/pgp/sign_test.go b/pgp/sign_test.go new file mode 100644 index 00000000..583329e7 --- /dev/null +++ b/pgp/sign_test.go @@ -0,0 +1,134 @@ +package pgp + +import ( + "crypto/rand" + "io" + "io/ioutil" + "os" + "path" + + . "gopkg.in/check.v1" +) + +// Common set of tests shared by internal & external GnuPG implementations +type SignerSuite struct { + signer Signer + verifier Verifier + + clearF *os.File + signedF *os.File + cleartext []byte + + passwordFile string +} + +func (s *SignerSuite) SetUpTest(c *C) { + tempDir := c.MkDir() + + var err error + s.clearF, err = os.Create(path.Join(tempDir, "cleartext")) + c.Assert(err, IsNil) + + s.cleartext = make([]byte, 0, 1024) + _, err = rand.Read(s.cleartext) + c.Assert(err, IsNil) + + _, err = s.clearF.Write(s.cleartext) + c.Assert(err, IsNil) + + _, err = s.clearF.Seek(0, io.SeekStart) + c.Assert(err, IsNil) + + s.signedF, err = os.Create(path.Join(tempDir, "signed")) + c.Assert(err, IsNil) + + s.passwordFile = path.Join(tempDir, "password") + f, err := os.OpenFile(s.passwordFile, os.O_CREATE|os.O_WRONLY, 0600) + c.Assert(err, IsNil) + + _, err = f.Write([]byte("verysecret")) + c.Assert(err, IsNil) + + f.Close() + + s.signer.SetBatch(true) +} + +func (s *SignerSuite) TearDownTest(c *C) { + s.clearF.Close() + s.signedF.Close() +} + +func (s *SignerSuite) testSignDetached(c *C) { + c.Assert(s.signer.Init(), IsNil) + + err := s.signer.DetachedSign(s.clearF.Name(), s.signedF.Name()) + c.Assert(err, IsNil) + + err = s.verifier.VerifyDetachedSignature(s.signedF, s.clearF, false) + c.Assert(err, IsNil) +} + +func (s *SignerSuite) TestSignDetachedNoPassphrase(c *C) { + s.signer.SetKeyRing("keyrings/aptly.pub", "keyrings/aptly.sec") + + s.testSignDetached(c) +} + +func (s *SignerSuite) TestSignDetachedPassphrase(c *C) { + s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetPassphrase("verysecret", "") + + s.testSignDetached(c) +} + +func (s *SignerSuite) TestSignDetachedPassphraseFile(c *C) { + s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetPassphrase("", s.passwordFile) + + s.testSignDetached(c) +} + +func (s *SignerSuite) testClearSign(c *C, expectedKey Key) { + c.Assert(s.signer.Init(), IsNil) + + err := s.signer.ClearSign(s.clearF.Name(), s.signedF.Name()) + c.Assert(err, IsNil) + + keyInfo, err := s.verifier.VerifyClearsigned(s.signedF, false) + c.Assert(err, IsNil) + + c.Assert(keyInfo.GoodKeys, DeepEquals, []Key{expectedKey}) + c.Assert(keyInfo.MissingKeys, DeepEquals, []Key(nil)) + + _, err = s.signedF.Seek(0, io.SeekStart) + c.Assert(err, IsNil) + extractedF, err := s.verifier.ExtractClearsigned(s.signedF) + c.Assert(err, IsNil) + defer extractedF.Close() + + extracted, err := ioutil.ReadAll(extractedF) + c.Assert(err, IsNil) + + c.Assert(extracted, DeepEquals, s.cleartext) +} + +func (s *SignerSuite) TestClearSignNoPassphrase(c *C) { + s.signer.SetKeyRing("keyrings/aptly.pub", "keyrings/aptly.sec") + + s.testClearSign(c, "21DBB89C16DB3E6D") +} + +func (s *SignerSuite) TestClearSignPassphrase(c *C) { + s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetPassphrase("verysecret", "") + + s.testClearSign(c, "F30E8CB9CDDE2AF8") +} + +func (s *SignerSuite) TestClearSignPassphraseFile(c *C) { + s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetPassphrase("", s.passwordFile) + + s.testClearSign(c, "F30E8CB9CDDE2AF8") +} diff --git a/pgp/verify_test.go b/pgp/verify_test.go new file mode 100644 index 00000000..a5071534 --- /dev/null +++ b/pgp/verify_test.go @@ -0,0 +1,93 @@ +package pgp + +import ( + "bytes" + "io/ioutil" + "os" + + . "gopkg.in/check.v1" +) + +// Common set of tests shared by internal & external GnuPG implementations +type VerifierSuite struct { + verifier Verifier +} + +func (s *VerifierSuite) TestVerifyDetached(c *C) { + for _, test := range []struct { + textName, signatureName string + }{ + {"1.text", "1.signature"}, + {"2.text", "2.signature"}, + {"3.text", "3.signature"}, + } { + cleartext, err := os.Open(test.textName) + c.Assert(err, IsNil) + + signature, err := os.Open(test.signatureName) + c.Assert(err, IsNil) + + err = s.verifier.VerifyDetachedSignature(signature, cleartext, false) + c.Assert(err, IsNil) + + signature.Close() + cleartext.Close() + } +} + +func (s *VerifierSuite) TestVerifyClearsigned(c *C) { + for _, test := range []struct { + clearSignedName string + }{ + {"1.clearsigned"}, + } { + clearsigned, err := os.Open(test.clearSignedName) + c.Assert(err, IsNil) + + keyInfo, err := s.verifier.VerifyClearsigned(clearsigned, false) + c.Assert(err, IsNil) + c.Check(keyInfo.GoodKeys, DeepEquals, []Key{"8B48AD6246925553", "7638D0442B90D010"}) + c.Check(keyInfo.MissingKeys, DeepEquals, []Key(nil)) + + clearsigned.Close() + } +} + +func (s *VerifierSuite) TestExtractClearsigned(c *C) { + for _, test := range []struct { + clearSignedName, clearTextName string + }{ + {"1.clearsigned", "1.cleartext"}, + } { + clearsigned, err := os.Open(test.clearSignedName) + c.Assert(err, IsNil) + + cleartext, err := os.Open(test.clearTextName) + c.Assert(err, IsNil) + + is, err := s.verifier.IsClearSigned(clearsigned) + c.Assert(err, IsNil) + c.Check(is, Equals, true) + + clearsigned.Seek(0, 0) + + extractedF, err := s.verifier.ExtractClearsigned(clearsigned) + c.Assert(err, IsNil) + + expected, err := ioutil.ReadAll(cleartext) + c.Assert(err, IsNil) + + extracted, err := ioutil.ReadAll(extractedF) + c.Assert(err, IsNil) + + // normalize newlines + extracted = bytes.TrimRight(bytes.Replace(extracted, []byte("\r\n"), []byte("\n"), -1), "\n") + expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) + + c.Check(extracted, DeepEquals, expected) + + extractedF.Close() + clearsigned.Close() + cleartext.Close() + } +}