github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/gpgkeypairmgr_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package asserts_test
    21  
    22  import (
    23  	"bytes"
    24  	"crypto"
    25  	"crypto/rand"
    26  	"crypto/rsa"
    27  	"fmt"
    28  	"os"
    29  	"time"
    30  
    31  	. "gopkg.in/check.v1"
    32  
    33  	"golang.org/x/crypto/openpgp/packet"
    34  
    35  	"github.com/snapcore/snapd/asserts"
    36  	"github.com/snapcore/snapd/asserts/assertstest"
    37  	"github.com/snapcore/snapd/osutil"
    38  )
    39  
    40  type gpgKeypairMgrSuite struct {
    41  	homedir    string
    42  	keypairMgr asserts.KeypairManager
    43  }
    44  
    45  var _ = Suite(&gpgKeypairMgrSuite{})
    46  
    47  func (gkms *gpgKeypairMgrSuite) SetUpSuite(c *C) {
    48  	if !osutil.FileExists("/usr/bin/gpg1") && !osutil.FileExists("/usr/bin/gpg") {
    49  		c.Skip("gpg not installed")
    50  	}
    51  }
    52  
    53  func (gkms *gpgKeypairMgrSuite) importKey(key string) {
    54  	assertstest.GPGImportKey(gkms.homedir, key)
    55  }
    56  
    57  func (gkms *gpgKeypairMgrSuite) SetUpTest(c *C) {
    58  	gkms.homedir = c.MkDir()
    59  	os.Setenv("SNAP_GNUPG_HOME", gkms.homedir)
    60  	gkms.keypairMgr = asserts.NewGPGKeypairManager()
    61  	// import test key
    62  	gkms.importKey(assertstest.DevKey)
    63  }
    64  
    65  func (gkms *gpgKeypairMgrSuite) TearDownTest(c *C) {
    66  	os.Unsetenv("SNAP_GNUPG_HOME")
    67  }
    68  
    69  func (gkms *gpgKeypairMgrSuite) TestGetPublicKeyLooksGood(c *C) {
    70  	got, err := gkms.keypairMgr.Get(assertstest.DevKeyID)
    71  	c.Assert(err, IsNil)
    72  	keyID := got.PublicKey().ID()
    73  	c.Check(keyID, Equals, assertstest.DevKeyID)
    74  }
    75  
    76  func (gkms *gpgKeypairMgrSuite) TestGetNotFound(c *C) {
    77  	got, err := gkms.keypairMgr.Get("ffffffffffffffff")
    78  	c.Check(err, ErrorMatches, `cannot find key "ffffffffffffffff" in GPG keyring`)
    79  	c.Check(got, IsNil)
    80  }
    81  
    82  func (gkms *gpgKeypairMgrSuite) TestUseInSigning(c *C) {
    83  	store := assertstest.NewStoreStack("trusted", nil)
    84  
    85  	devKey, err := gkms.keypairMgr.Get(assertstest.DevKeyID)
    86  	c.Assert(err, IsNil)
    87  
    88  	devAcct := assertstest.NewAccount(store, "devel1", map[string]interface{}{
    89  		"account-id": "dev1-id",
    90  	}, "")
    91  	devAccKey := assertstest.NewAccountKey(store, devAcct, nil, devKey.PublicKey(), "")
    92  
    93  	signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
    94  		KeypairManager: gkms.keypairMgr,
    95  	})
    96  	c.Assert(err, IsNil)
    97  
    98  	checkDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
    99  		Backstore: asserts.NewMemoryBackstore(),
   100  		Trusted:   store.Trusted,
   101  	})
   102  	c.Assert(err, IsNil)
   103  	// add store key
   104  	err = checkDB.Add(store.StoreAccountKey(""))
   105  	c.Assert(err, IsNil)
   106  	// enable devel key
   107  	err = checkDB.Add(devAcct)
   108  	c.Assert(err, IsNil)
   109  	err = checkDB.Add(devAccKey)
   110  	c.Assert(err, IsNil)
   111  
   112  	headers := map[string]interface{}{
   113  		"authority-id":  "dev1-id",
   114  		"snap-sha3-384": blobSHA3_384,
   115  		"snap-id":       "snap-id-1",
   116  		"grade":         "devel",
   117  		"snap-size":     "1025",
   118  		"timestamp":     time.Now().Format(time.RFC3339),
   119  	}
   120  	snapBuild, err := signDB.Sign(asserts.SnapBuildType, headers, nil, assertstest.DevKeyID)
   121  	c.Assert(err, IsNil)
   122  
   123  	err = checkDB.Check(snapBuild)
   124  	c.Check(err, IsNil)
   125  }
   126  
   127  func (gkms *gpgKeypairMgrSuite) TestGetNotUnique(c *C) {
   128  	mockGPG := func(prev asserts.GPGRunner, input []byte, args ...string) ([]byte, error) {
   129  		if args[1] == "--list-secret-keys" {
   130  			return prev(input, args...)
   131  		}
   132  		c.Assert(args[1], Equals, "--export")
   133  
   134  		pk1, err := rsa.GenerateKey(rand.Reader, 512)
   135  		c.Assert(err, IsNil)
   136  		pk2, err := rsa.GenerateKey(rand.Reader, 512)
   137  		c.Assert(err, IsNil)
   138  
   139  		buf := new(bytes.Buffer)
   140  		err = packet.NewRSAPublicKey(time.Now(), &pk1.PublicKey).Serialize(buf)
   141  		c.Assert(err, IsNil)
   142  		err = packet.NewRSAPublicKey(time.Now(), &pk2.PublicKey).Serialize(buf)
   143  		c.Assert(err, IsNil)
   144  
   145  		return buf.Bytes(), nil
   146  	}
   147  	restore := asserts.MockRunGPG(mockGPG)
   148  	defer restore()
   149  
   150  	_, err := gkms.keypairMgr.Get(assertstest.DevKeyID)
   151  	c.Check(err, ErrorMatches, `cannot load GPG public key with fingerprint "[A-F0-9]+": cannot select exported public key, found many`)
   152  }
   153  
   154  func (gkms *gpgKeypairMgrSuite) TestUseInSigningBrokenSignature(c *C) {
   155  	_, rsaPrivKey := assertstest.ReadPrivKey(assertstest.DevKey)
   156  	pgpPrivKey := packet.NewRSAPrivateKey(time.Unix(1, 0), rsaPrivKey)
   157  
   158  	var breakSig func(sig *packet.Signature, cont []byte) []byte
   159  
   160  	mockGPG := func(prev asserts.GPGRunner, input []byte, args ...string) ([]byte, error) {
   161  		if args[1] == "--list-secret-keys" || args[1] == "--export" {
   162  			return prev(input, args...)
   163  		}
   164  		n := len(args)
   165  		c.Assert(args[n-1], Equals, "--detach-sign")
   166  
   167  		sig := new(packet.Signature)
   168  		sig.PubKeyAlgo = packet.PubKeyAlgoRSA
   169  		sig.Hash = crypto.SHA512
   170  		sig.CreationTime = time.Now()
   171  
   172  		// poking to break the signature
   173  		cont := breakSig(sig, input)
   174  
   175  		h := sig.Hash.New()
   176  		h.Write([]byte(cont))
   177  
   178  		err := sig.Sign(h, pgpPrivKey, nil)
   179  		c.Assert(err, IsNil)
   180  
   181  		buf := new(bytes.Buffer)
   182  		sig.Serialize(buf)
   183  		return buf.Bytes(), nil
   184  	}
   185  	restore := asserts.MockRunGPG(mockGPG)
   186  	defer restore()
   187  
   188  	signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   189  		KeypairManager: gkms.keypairMgr,
   190  	})
   191  	c.Assert(err, IsNil)
   192  
   193  	headers := map[string]interface{}{
   194  		"authority-id":  "dev1-id",
   195  		"snap-sha3-384": blobSHA3_384,
   196  		"snap-id":       "snap-id-1",
   197  		"grade":         "devel",
   198  		"snap-size":     "1025",
   199  		"timestamp":     time.Now().Format(time.RFC3339),
   200  	}
   201  
   202  	tests := []struct {
   203  		breakSig    func(*packet.Signature, []byte) []byte
   204  		expectedErr string
   205  	}{
   206  		{func(sig *packet.Signature, cont []byte) []byte {
   207  			sig.Hash = crypto.SHA1
   208  			return cont
   209  		}, "cannot sign assertion: bad GPG produced signature: expected SHA512 digest"},
   210  		{func(sig *packet.Signature, cont []byte) []byte {
   211  			return cont[:5]
   212  		}, "cannot sign assertion: bad GPG produced signature: it does not verify:.*"},
   213  	}
   214  
   215  	for _, t := range tests {
   216  		breakSig = t.breakSig
   217  
   218  		_, err = signDB.Sign(asserts.SnapBuildType, headers, nil, assertstest.DevKeyID)
   219  		c.Check(err, ErrorMatches, t.expectedErr)
   220  	}
   221  
   222  }
   223  
   224  func (gkms *gpgKeypairMgrSuite) TestUseInSigningFailure(c *C) {
   225  	mockGPG := func(prev asserts.GPGRunner, input []byte, args ...string) ([]byte, error) {
   226  		if args[1] == "--list-secret-keys" || args[1] == "--export" {
   227  			return prev(input, args...)
   228  		}
   229  		n := len(args)
   230  		c.Assert(args[n-1], Equals, "--detach-sign")
   231  		return nil, fmt.Errorf("boom")
   232  	}
   233  	restore := asserts.MockRunGPG(mockGPG)
   234  	defer restore()
   235  
   236  	signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   237  		KeypairManager: gkms.keypairMgr,
   238  	})
   239  	c.Assert(err, IsNil)
   240  
   241  	headers := map[string]interface{}{
   242  		"authority-id":  "dev1-id",
   243  		"snap-sha3-384": blobSHA3_384,
   244  		"snap-id":       "snap-id-1",
   245  		"grade":         "devel",
   246  		"snap-size":     "1025",
   247  		"timestamp":     time.Now().Format(time.RFC3339),
   248  	}
   249  
   250  	_, err = signDB.Sign(asserts.SnapBuildType, headers, nil, assertstest.DevKeyID)
   251  	c.Check(err, ErrorMatches, "cannot sign assertion: cannot sign using GPG: boom")
   252  }
   253  
   254  const shortPrivKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
   255  Version: GnuPG v1
   256  
   257  lQOYBFdGO7MBCADltsXglnDQdfBw0yOVpKZdkuvSnJKKn1H72PapgAr7ucLqNBCA
   258  js0kltDTa2LQP4vljiTyoMzOMnex4kXwRPlF+poZIEBHDLT0i/6sJ6mDukss1HBR
   259  GgNpU3y49WTXc8qxFY4clhbuqgQmy6bUmaVoo3Z4z7cqbsCepWfx5y+vJwMYqlo3
   260  Nb4q2+hTKS/o3yLiYB7/hkEhMZrFrOPR5SM7Tz5y7cpF6ObY+JZIp/MK+LsLWLji
   261  fEX/pcOtSjFdQqbcnhJJscXRERlFQDbc+gNmZYZ2RqdH5o46OliHkGhVDVTiW25A
   262  SqhGfnodypbZ9QAPSRvhLrN64AqEsvRb3I13ABEBAAEAB/9cQKg8Nz6sQUkkDm9C
   263  iCK1/qyNYwro9+3VXj9FOCJxEJuqMemUr4TMVnMcDQrchkC5GnpVJGXLw3HVcwFS
   264  amjPhUKAp7aYsg40DcrjuXP27oiFQvWuZGuNT5WNtCNg8WQr9POjIFWqWIYdTHk9
   265  9Ux79vW7s/Oj62GY9OWHPSilxpq1MjDKo9CSMbLeWxW+gbDxaD7cK7H/ONcz8bZ7
   266  pRfEhNIx3mEbWaZpWRrf+dSUx2OJbPGRkeFFMbCNapqftse173BZCwUKsW7RTp2S
   267  w8Vpo2Ky63Jlpz1DpoMDBz2vSH7pzaqAdnziI2r0IKiidajXFfpXJpJ3ICo/QhWj
   268  x1eRBADrI4I99zHeyy+12QMpkDrOu+ahF6/emdsm1FIy88TqeBmLkeXCXKZIpU3c
   269  USnxzm0nPNbOl7Nvf2VdAyeAftyag7t38Cud5MXldv/iY0e6oTKzxgha37yr6oRv
   270  PZ6VGwbkBvWti1HL4yx1QnkHFS6ailR9WiiHr3HaWAklZAsC0QQA+hgOi0V9fMZZ
   271  Y4/iFVRI9k1NK3pl0mP7pVTzbcjVYspLdIPQxPDsHJW0z48g23KOt0vL3yZvxdBx
   272  cfYGqIonAX19aMD5D4bNLx616pZs78DKGlOz6iXDcaib+n/uCNWxd5R/0m/zugrB
   273  qklpyIC/uxx+SmkJqqq378ytfvBMzccD/3Y6m3PM0ZnrIkr4Q7cKi9ao9rvM+J7o
   274  ziMgfnKWedNDxNa4tIVYYGPiXsjxY/ASUyxVjUPbkyCy3ubZrew0zQ9+kQbO/6vB
   275  WAg9ffT9M92QbSDjuxgUiC5GfvlCoDgJtuLRHd0YLDgUCS5nwb+teEsOpiNWEGXc
   276  Tr+5HZO+g6wxT6W0BiAoeHh4KYkBOAQTAQIAIgUCV0Y7swIbLwYLCQgHAwIGFQgC
   277  CQoLBBYCAwECHgECF4AACgkQEYacUJMr9p/i5wf/XbEiAe1+Y/ZNMO8PYnq1Nktk
   278  CbZEfQo+QH/9gJpt4p78YseWeUp14gsULLks3xRojlKNzYkqBpJcP7Ex+hQ3LEp7
   279  9IVbept5md4uuZcU0GFF42WAYXExd2cuxPv3lmWHOPuN63a/xpp0M2vYDfpt63qi
   280  Tly5/P4+NgpD6vAh8zwRHuBV/0mno/QX6cUCLVxq2v1aOqC9zq9B5sdYKQKjsQBP
   281  NOXCt1wPaINkqiW/8w2KhUl6mL6vhO0Onqu/F7M/YNXitv6Z2NFdFUVBh58UZW3C
   282  2jrc8JeRQ4Qlr1oeHh2loYOdZfxFPxRjhsRTnNKY8UHWLfbeI6lMqxR5G3DS+g==
   283  =kQRo
   284  -----END PGP PRIVATE KEY BLOCK-----
   285  `
   286  
   287  func (gkms *gpgKeypairMgrSuite) TestUseInSigningKeyTooShort(c *C) {
   288  	gkms.importKey(shortPrivKey)
   289  	privk, _ := assertstest.ReadPrivKey(shortPrivKey)
   290  
   291  	signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   292  		KeypairManager: gkms.keypairMgr,
   293  	})
   294  	c.Assert(err, IsNil)
   295  
   296  	headers := map[string]interface{}{
   297  		"authority-id":  "dev1-id",
   298  		"snap-sha3-384": blobSHA3_384,
   299  		"snap-id":       "snap-id-1",
   300  		"grade":         "devel",
   301  		"snap-size":     "1025",
   302  		"timestamp":     time.Now().Format(time.RFC3339),
   303  	}
   304  
   305  	_, err = signDB.Sign(asserts.SnapBuildType, headers, nil, privk.PublicKey().ID())
   306  	c.Check(err, ErrorMatches, `cannot sign assertion: signing needs at least a 4096 bits key, got 2048`)
   307  }
   308  
   309  func (gkms *gpgKeypairMgrSuite) TestParametersForGenerate(c *C) {
   310  	gpgKeypairMgr := gkms.keypairMgr.(*asserts.GPGKeypairManager)
   311  	baseParameters := `
   312  Key-Type: RSA
   313  Key-Length: 4096
   314  Name-Real: test-key
   315  Creation-Date: seconds=1451606400
   316  Preferences: SHA512
   317  `
   318  
   319  	tests := []struct {
   320  		passphrase      string
   321  		extraParameters string
   322  	}{
   323  		{"", ""},
   324  		{"secret", "Passphrase: secret\n"},
   325  	}
   326  
   327  	for _, test := range tests {
   328  		parameters := gpgKeypairMgr.ParametersForGenerate(test.passphrase, "test-key")
   329  		c.Check(parameters, Equals, baseParameters+test.extraParameters)
   330  	}
   331  }