gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/gpgkeypairmgr_test.go (about)

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