github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/asserts/extkeypairmgr_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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  	"crypto/rand"
    24  	"crypto/rsa"
    25  	"crypto/x509"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"os"
    29  	"os/exec"
    30  	"path/filepath"
    31  	"time"
    32  
    33  	. "gopkg.in/check.v1"
    34  
    35  	"github.com/snapcore/snapd/asserts"
    36  	"github.com/snapcore/snapd/asserts/assertstest"
    37  	"github.com/snapcore/snapd/testutil"
    38  )
    39  
    40  type extKeypairMgrSuite struct {
    41  	pgm *testutil.MockCmd
    42  
    43  	defaultPub *rsa.PublicKey
    44  	modelsPub  *rsa.PublicKey
    45  }
    46  
    47  var _ = Suite(&extKeypairMgrSuite{})
    48  
    49  func (s *extKeypairMgrSuite) SetUpSuite(c *C) {
    50  	tmpdir := c.MkDir()
    51  	k1, err := rsa.GenerateKey(rand.Reader, 4096)
    52  	c.Assert(err, IsNil)
    53  	k2, err := rsa.GenerateKey(rand.Reader, 4096)
    54  	c.Assert(err, IsNil)
    55  
    56  	derPub1, err := x509.MarshalPKIXPublicKey(&k1.PublicKey)
    57  	c.Assert(err, IsNil)
    58  	err = ioutil.WriteFile(filepath.Join(tmpdir, "default.pub"), derPub1, 0644)
    59  	c.Assert(err, IsNil)
    60  	derPub2, err := x509.MarshalPKIXPublicKey(&k2.PublicKey)
    61  	c.Assert(err, IsNil)
    62  	err = ioutil.WriteFile(filepath.Join(tmpdir, "models.pub"), derPub2, 0644)
    63  	c.Assert(err, IsNil)
    64  
    65  	err = ioutil.WriteFile(filepath.Join(tmpdir, "default.key"), x509.MarshalPKCS1PrivateKey(k1), 0600)
    66  	c.Assert(err, IsNil)
    67  	err = ioutil.WriteFile(filepath.Join(tmpdir, "models.key"), x509.MarshalPKCS1PrivateKey(k2), 0600)
    68  	c.Assert(err, IsNil)
    69  
    70  	s.defaultPub = &k1.PublicKey
    71  	s.modelsPub = &k2.PublicKey
    72  
    73  	s.pgm = testutil.MockCommand(c, "keymgr", fmt.Sprintf(`
    74  keydir=%q
    75  case $1 in
    76    features)
    77      echo '{"signing":["RSA-PKCS"] , "public-keys":["DER"]}'
    78      ;;
    79    key-names)
    80      echo '{"key-names": ["default", "models"]}'
    81      ;;
    82    get-public-key)
    83      if [ "$5" = missing ]; then
    84         echo not found
    85         exit 1
    86      fi
    87      cat ${keydir}/"$5".pub
    88      ;;
    89    sign)
    90      openssl rsautl -sign -pkcs -keyform DER -inkey ${keydir}/"$5".key
    91      ;;
    92    *)
    93      exit 1
    94      ;;
    95  esac
    96  `, tmpdir))
    97  }
    98  
    99  func (s *extKeypairMgrSuite) TearDownSuite(c *C) {
   100  	s.pgm.Restore()
   101  }
   102  
   103  func (s *extKeypairMgrSuite) TestFeaturesErrors(c *C) {
   104  	pgm := testutil.MockCommand(c, "keymgr", `
   105  if [ "$1" != "features" ]; then
   106    exit 2
   107  fi
   108  if [ "${EXT_KEYMGR_FAIL}" = "exit-1" ]; then
   109    exit 1
   110  fi
   111  echo "${EXT_KEYMGR_FAIL}"
   112  `)
   113  	defer pgm.Restore()
   114  	defer os.Unsetenv("EXT_KEYMGR_FAIL")
   115  
   116  	tests := []struct {
   117  		outcome string
   118  		err     string
   119  	}{
   120  		{"exit-1", `.*exit status 1.*`},
   121  		{`{"signing":["RSA-PKCS"]}`, `external keypair manager "keymgr" missing support for public key DER output format`},
   122  		{"{}", `external keypair manager \"keymgr\" missing support for RSA-PKCS signing`},
   123  		{"{", `cannot decode external keypair manager "keymgr" \[features\] output.*`},
   124  		{"", `cannot decode external keypair manager "keymgr" \[features\] output.*`},
   125  	}
   126  
   127  	defer os.Unsetenv("EXT_KEYMGR_FAIL")
   128  	for _, t := range tests {
   129  		os.Setenv("EXT_KEYMGR_FAIL", t.outcome)
   130  
   131  		_, err := asserts.NewExternalKeypairManager("keymgr")
   132  		c.Check(err, ErrorMatches, t.err)
   133  		c.Check(pgm.Calls(), DeepEquals, [][]string{
   134  			{"keymgr", "features"},
   135  		})
   136  		pgm.ForgetCalls()
   137  	}
   138  }
   139  
   140  func (s *extKeypairMgrSuite) TestGetByName(c *C) {
   141  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   142  	c.Assert(err, IsNil)
   143  	s.pgm.ForgetCalls()
   144  
   145  	pk, err := kmgr.GetByName("default")
   146  	c.Assert(err, IsNil)
   147  
   148  	expPK := asserts.RSAPublicKey(s.defaultPub)
   149  
   150  	c.Check(pk.PublicKey().ID(), DeepEquals, expPK.ID())
   151  
   152  	c.Check(s.pgm.Calls(), DeepEquals, [][]string{
   153  		{"keymgr", "get-public-key", "-f", "DER", "-k", "default"},
   154  	})
   155  }
   156  
   157  func (s *extKeypairMgrSuite) TestGetByNameNotFound(c *C) {
   158  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   159  	c.Assert(err, IsNil)
   160  
   161  	_, err = kmgr.GetByName("missing")
   162  	c.Check(err, ErrorMatches, `cannot find external key:.*missing.*`)
   163  }
   164  
   165  func (s *extKeypairMgrSuite) TestGet(c *C) {
   166  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   167  	c.Assert(err, IsNil)
   168  	s.pgm.ForgetCalls()
   169  
   170  	defaultID := asserts.RSAPublicKey(s.defaultPub).ID()
   171  	modelsID := asserts.RSAPublicKey(s.modelsPub).ID()
   172  
   173  	pk1, err := kmgr.Get(defaultID)
   174  	c.Assert(err, IsNil)
   175  	c.Check(pk1.PublicKey().ID(), Equals, defaultID)
   176  
   177  	pk2, err := kmgr.Get(modelsID)
   178  	c.Assert(err, IsNil)
   179  	c.Check(pk2.PublicKey().ID(), Equals, modelsID)
   180  
   181  	c.Check(s.pgm.Calls(), DeepEquals, [][]string{
   182  		{"keymgr", "key-names"},
   183  		{"keymgr", "get-public-key", "-f", "DER", "-k", "default"},
   184  		{"keymgr", "get-public-key", "-f", "DER", "-k", "models"},
   185  	})
   186  
   187  	_, err = kmgr.Get("unknown-id")
   188  	c.Check(err, ErrorMatches, `cannot find external key with id "unknown-id"`)
   189  }
   190  
   191  func (s *extKeypairMgrSuite) TestSignFlow(c *C) {
   192  	// the signing uses openssl
   193  	_, err := exec.LookPath("openssl")
   194  	if err != nil {
   195  		c.Skip("cannot locate openssl on this system to test signing")
   196  	}
   197  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   198  	c.Assert(err, IsNil)
   199  	s.pgm.ForgetCalls()
   200  
   201  	pk, err := kmgr.GetByName("default")
   202  	c.Assert(err, IsNil)
   203  
   204  	store := assertstest.NewStoreStack("trusted", nil)
   205  
   206  	brandAcct := assertstest.NewAccount(store, "brand", map[string]interface{}{
   207  		"account-id": "brand-id",
   208  	}, "")
   209  	brandAccKey := assertstest.NewAccountKey(store, brandAcct, nil, pk.PublicKey(), "")
   210  
   211  	signDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   212  		KeypairManager: kmgr,
   213  	})
   214  	c.Assert(err, IsNil)
   215  
   216  	checkDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   217  		Backstore: asserts.NewMemoryBackstore(),
   218  		Trusted:   store.Trusted,
   219  	})
   220  	c.Assert(err, IsNil)
   221  	// add store key
   222  	err = checkDB.Add(store.StoreAccountKey(""))
   223  	c.Assert(err, IsNil)
   224  	// enable brand key
   225  	err = checkDB.Add(brandAcct)
   226  	c.Assert(err, IsNil)
   227  	err = checkDB.Add(brandAccKey)
   228  	c.Assert(err, IsNil)
   229  
   230  	modelHdsrs := map[string]interface{}{
   231  		"authority-id": "brand-id",
   232  		"brand-id":     "brand-id",
   233  		"model":        "model",
   234  		"series":       "16",
   235  		"architecture": "amd64",
   236  		"base":         "core18",
   237  		"gadget":       "gadget",
   238  		"kernel":       "pc-kernel",
   239  		"timestamp":    time.Now().Format(time.RFC3339),
   240  	}
   241  	a, err := signDB.Sign(asserts.ModelType, modelHdsrs, nil, pk.PublicKey().ID())
   242  	c.Assert(err, IsNil)
   243  
   244  	// valid
   245  	err = checkDB.Check(a)
   246  	c.Assert(err, IsNil)
   247  
   248  	c.Check(s.pgm.Calls(), DeepEquals, [][]string{
   249  		{"keymgr", "get-public-key", "-f", "DER", "-k", "default"},
   250  		{"keymgr", "sign", "-m", "RSA-PKCS", "-k", "default"},
   251  	})
   252  }
   253  
   254  func (s *extKeypairMgrSuite) TestExport(c *C) {
   255  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   256  	c.Assert(err, IsNil)
   257  
   258  	keys := []struct {
   259  		name string
   260  		pk   *rsa.PublicKey
   261  	}{
   262  		{name: "default", pk: s.defaultPub},
   263  		{name: "models", pk: s.modelsPub},
   264  	}
   265  
   266  	for _, tk := range keys {
   267  		exported, err := kmgr.Export(tk.name)
   268  		c.Assert(err, IsNil)
   269  
   270  		expected, err := asserts.EncodePublicKey(asserts.RSAPublicKey(tk.pk))
   271  		c.Assert(err, IsNil)
   272  		c.Check(exported, DeepEquals, expected)
   273  	}
   274  }
   275  
   276  func (s *extKeypairMgrSuite) TestList(c *C) {
   277  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   278  	c.Assert(err, IsNil)
   279  
   280  	keys, err := kmgr.List()
   281  	c.Assert(err, IsNil)
   282  
   283  	defaultID := asserts.RSAPublicKey(s.defaultPub).ID()
   284  	modelsID := asserts.RSAPublicKey(s.modelsPub).ID()
   285  
   286  	c.Check(keys, DeepEquals, []asserts.ExternalKeyInfo{
   287  		{Name: "default", ID: defaultID},
   288  		{Name: "models", ID: modelsID},
   289  	})
   290  }
   291  
   292  func (s *extKeypairMgrSuite) TestListError(c *C) {
   293  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   294  	c.Assert(err, IsNil)
   295  
   296  	pgm := testutil.MockCommand(c, "keymgr", `exit 1`)
   297  	defer pgm.Restore()
   298  
   299  	_, err = kmgr.List()
   300  	c.Check(err, ErrorMatches, `cannot get all external keypair manager key names:.*exit status 1.*`)
   301  }
   302  
   303  func (s *extKeypairMgrSuite) TestDeleteUnsupported(c *C) {
   304  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   305  	c.Assert(err, IsNil)
   306  
   307  	err = kmgr.Delete("key")
   308  	c.Check(err, ErrorMatches, `no support to delete external keypair manager keys`)
   309  	c.Check(err, FitsTypeOf, &asserts.ExternalUnsupportedOpError{})
   310  
   311  }
   312  
   313  func (s *extKeypairMgrSuite) TestGenerateUnsupported(c *C) {
   314  	kmgr, err := asserts.NewExternalKeypairManager("keymgr")
   315  	c.Assert(err, IsNil)
   316  
   317  	err = kmgr.Generate("key")
   318  	c.Check(err, ErrorMatches, `no support to mediate generating an external keypair manager key`)
   319  	c.Check(err, FitsTypeOf, &asserts.ExternalUnsupportedOpError{})
   320  }