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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-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  	"encoding/base64"
    24  	"fmt"
    25  	"path/filepath"
    26  	"strings"
    27  	"time"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/asserts"
    32  	"github.com/snapcore/snapd/asserts/assertstest"
    33  )
    34  
    35  type accountKeySuite struct {
    36  	privKey              asserts.PrivateKey
    37  	pubKeyBody           string
    38  	keyID                string
    39  	since, until         time.Time
    40  	sinceLine, untilLine string
    41  }
    42  
    43  var _ = Suite(&accountKeySuite{})
    44  
    45  func (aks *accountKeySuite) SetUpSuite(c *C) {
    46  	cfg1 := &asserts.DatabaseConfig{}
    47  	accDb, err := asserts.OpenDatabase(cfg1)
    48  	c.Assert(err, IsNil)
    49  	aks.privKey = testPrivKey1
    50  	err = accDb.ImportKey(aks.privKey)
    51  	c.Assert(err, IsNil)
    52  	aks.keyID = aks.privKey.PublicKey().ID()
    53  
    54  	pubKey, err := accDb.PublicKey(aks.keyID)
    55  	c.Assert(err, IsNil)
    56  	pubKeyEncoded, err := asserts.EncodePublicKey(pubKey)
    57  	c.Assert(err, IsNil)
    58  	aks.pubKeyBody = string(pubKeyEncoded)
    59  
    60  	aks.since, err = time.Parse(time.RFC822, "16 Nov 15 15:04 UTC")
    61  	c.Assert(err, IsNil)
    62  	aks.until = aks.since.AddDate(1, 0, 0)
    63  	aks.sinceLine = "since: " + aks.since.Format(time.RFC3339) + "\n"
    64  	aks.untilLine = "until: " + aks.until.Format(time.RFC3339) + "\n"
    65  }
    66  
    67  func (aks *accountKeySuite) TestDecodeOK(c *C) {
    68  	encoded := "type: account-key\n" +
    69  		"authority-id: canonical\n" +
    70  		"account-id: acc-id1\n" +
    71  		"name: default\n" +
    72  		"public-key-sha3-384: " + aks.keyID + "\n" +
    73  		aks.sinceLine +
    74  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
    75  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
    76  		aks.pubKeyBody + "\n\n" +
    77  		"AXNpZw=="
    78  	a, err := asserts.Decode([]byte(encoded))
    79  	c.Assert(err, IsNil)
    80  	c.Check(a.Type(), Equals, asserts.AccountKeyType)
    81  	accKey := a.(*asserts.AccountKey)
    82  	c.Check(accKey.AccountID(), Equals, "acc-id1")
    83  	c.Check(accKey.Name(), Equals, "default")
    84  	c.Check(accKey.PublicKeyID(), Equals, aks.keyID)
    85  	c.Check(accKey.Since(), Equals, aks.since)
    86  }
    87  
    88  func (aks *accountKeySuite) TestDecodeNoName(c *C) {
    89  	// XXX: remove this test once name is mandatory
    90  	encoded := "type: account-key\n" +
    91  		"authority-id: canonical\n" +
    92  		"account-id: acc-id1\n" +
    93  		"public-key-sha3-384: " + aks.keyID + "\n" +
    94  		aks.sinceLine +
    95  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
    96  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
    97  		aks.pubKeyBody + "\n\n" +
    98  		"AXNpZw=="
    99  	a, err := asserts.Decode([]byte(encoded))
   100  	c.Assert(err, IsNil)
   101  	c.Check(a.Type(), Equals, asserts.AccountKeyType)
   102  	accKey := a.(*asserts.AccountKey)
   103  	c.Check(accKey.AccountID(), Equals, "acc-id1")
   104  	c.Check(accKey.Name(), Equals, "")
   105  	c.Check(accKey.PublicKeyID(), Equals, aks.keyID)
   106  	c.Check(accKey.Since(), Equals, aks.since)
   107  }
   108  
   109  func (aks *accountKeySuite) TestUntil(c *C) {
   110  
   111  	untilSinceLine := "until: " + aks.since.Format(time.RFC3339) + "\n"
   112  
   113  	tests := []struct {
   114  		untilLine string
   115  		until     time.Time
   116  	}{
   117  		{"", time.Time{}},           // zero time default
   118  		{aks.untilLine, aks.until},  // in the future
   119  		{untilSinceLine, aks.since}, // same as since
   120  	}
   121  
   122  	for _, test := range tests {
   123  		c.Log(test)
   124  		encoded := "type: account-key\n" +
   125  			"authority-id: canonical\n" +
   126  			"account-id: acc-id1\n" +
   127  			"name: default\n" +
   128  			"public-key-sha3-384: " + aks.keyID + "\n" +
   129  			aks.sinceLine +
   130  			test.untilLine +
   131  			fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   132  			"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   133  			aks.pubKeyBody + "\n\n" +
   134  			"openpgp c2ln"
   135  		a, err := asserts.Decode([]byte(encoded))
   136  		c.Assert(err, IsNil)
   137  		accKey := a.(*asserts.AccountKey)
   138  		c.Check(accKey.Until(), Equals, test.until)
   139  	}
   140  }
   141  
   142  const (
   143  	accKeyErrPrefix    = "assertion account-key: "
   144  	accKeyReqErrPrefix = "assertion account-key-request: "
   145  )
   146  
   147  func (aks *accountKeySuite) TestDecodeInvalidHeaders(c *C) {
   148  
   149  	encoded := "type: account-key\n" +
   150  		"authority-id: canonical\n" +
   151  		"account-id: acc-id1\n" +
   152  		"name: default\n" +
   153  		"public-key-sha3-384: " + aks.keyID + "\n" +
   154  		aks.sinceLine +
   155  		aks.untilLine +
   156  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   157  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   158  		aks.pubKeyBody + "\n\n" +
   159  		"AXNpZw=="
   160  
   161  	untilPast := aks.since.AddDate(-1, 0, 0)
   162  	untilPastLine := "until: " + untilPast.Format(time.RFC3339) + "\n"
   163  
   164  	invalidHeaderTests := []struct{ original, invalid, expectedErr string }{
   165  		{"account-id: acc-id1\n", "", `"account-id" header is mandatory`},
   166  		{"account-id: acc-id1\n", "account-id: \n", `"account-id" header should not be empty`},
   167  		// XXX: enable this once name is mandatory
   168  		// {"name: default\n", "", `"name" header is mandatory`},
   169  		{"name: default\n", "name: \n", `"name" header should not be empty`},
   170  		{"name: default\n", "name: a b\n", `"name" header contains invalid characters: "a b"`},
   171  		{"name: default\n", "name: -default\n", `"name" header contains invalid characters: "-default"`},
   172  		{"name: default\n", "name: foo:bar\n", `"name" header contains invalid characters: "foo:bar"`},
   173  		{"name: default\n", "name: a--b\n", `"name" header contains invalid characters: "a--b"`},
   174  		{"name: default\n", "name: 42\n", `"name" header contains invalid characters: "42"`},
   175  		{"public-key-sha3-384: " + aks.keyID + "\n", "", `"public-key-sha3-384" header is mandatory`},
   176  		{"public-key-sha3-384: " + aks.keyID + "\n", "public-key-sha3-384: \n", `"public-key-sha3-384" header should not be empty`},
   177  		{aks.sinceLine, "", `"since" header is mandatory`},
   178  		{aks.sinceLine, "since: \n", `"since" header should not be empty`},
   179  		{aks.sinceLine, "since: 12:30\n", `"since" header is not a RFC3339 date: .*`},
   180  		{aks.sinceLine, "since: \n", `"since" header should not be empty`},
   181  		{aks.untilLine, "until: \n", `"until" header is not a RFC3339 date: .*`},
   182  		{aks.untilLine, "until: 12:30\n", `"until" header is not a RFC3339 date: .*`},
   183  		{aks.untilLine, untilPastLine, `'until' time cannot be before 'since' time`},
   184  	}
   185  
   186  	for _, test := range invalidHeaderTests {
   187  		invalid := strings.Replace(encoded, test.original, test.invalid, 1)
   188  		_, err := asserts.Decode([]byte(invalid))
   189  		c.Check(err, ErrorMatches, accKeyErrPrefix+test.expectedErr)
   190  	}
   191  }
   192  
   193  func (aks *accountKeySuite) TestDecodeInvalidPublicKey(c *C) {
   194  	headers := "type: account-key\n" +
   195  		"authority-id: canonical\n" +
   196  		"account-id: acc-id1\n" +
   197  		"name: default\n" +
   198  		"public-key-sha3-384: " + aks.keyID + "\n" +
   199  		aks.sinceLine +
   200  		aks.untilLine
   201  
   202  	raw, err := base64.StdEncoding.DecodeString(aks.pubKeyBody)
   203  	c.Assert(err, IsNil)
   204  	spurious := base64.StdEncoding.EncodeToString(append(raw, "gorp"...))
   205  
   206  	invalidPublicKeyTests := []struct{ body, expectedErr string }{
   207  		{"", "cannot decode public key: no data"},
   208  		{"==", "cannot decode public key: .*"},
   209  		{"stuff", "cannot decode public key: .*"},
   210  		{"AnNpZw==", "unsupported public key format version: 2"},
   211  		{"AUJST0tFTg==", "cannot decode public key: .*"},
   212  		{spurious, "public key has spurious trailing data"},
   213  	}
   214  
   215  	for _, test := range invalidPublicKeyTests {
   216  		invalid := headers +
   217  			fmt.Sprintf("body-length: %v", len(test.body)) + "\n" +
   218  			"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   219  			test.body + "\n\n" +
   220  			"AXNpZw=="
   221  
   222  		_, err := asserts.Decode([]byte(invalid))
   223  		c.Check(err, ErrorMatches, accKeyErrPrefix+test.expectedErr)
   224  	}
   225  }
   226  
   227  func (aks *accountKeySuite) TestDecodeKeyIDMismatch(c *C) {
   228  	invalid := "type: account-key\n" +
   229  		"authority-id: canonical\n" +
   230  		"account-id: acc-id1\n" +
   231  		"name: default\n" +
   232  		"public-key-sha3-384: aa\n" +
   233  		aks.sinceLine +
   234  		aks.untilLine +
   235  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   236  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   237  		aks.pubKeyBody + "\n\n" +
   238  		"AXNpZw=="
   239  
   240  	_, err := asserts.Decode([]byte(invalid))
   241  	c.Check(err, ErrorMatches, accKeyErrPrefix+"public key does not match provided key id")
   242  }
   243  
   244  func (aks *accountKeySuite) openDB(c *C) *asserts.Database {
   245  	trustedKey := testPrivKey0
   246  
   247  	topDir := filepath.Join(c.MkDir(), "asserts-db")
   248  	bs, err := asserts.OpenFSBackstore(topDir)
   249  	c.Assert(err, IsNil)
   250  	cfg := &asserts.DatabaseConfig{
   251  		Backstore: bs,
   252  		Trusted: []asserts.Assertion{
   253  			asserts.BootstrapAccountForTest("canonical"),
   254  			asserts.BootstrapAccountKeyForTest("canonical", trustedKey.PublicKey()),
   255  		},
   256  	}
   257  	db, err := asserts.OpenDatabase(cfg)
   258  	c.Assert(err, IsNil)
   259  	return db
   260  }
   261  
   262  func (aks *accountKeySuite) prereqAccount(c *C, db *asserts.Database) {
   263  	trustedKey := testPrivKey0
   264  
   265  	headers := map[string]interface{}{
   266  		"authority-id": "canonical",
   267  		"display-name": "Acct1",
   268  		"account-id":   "acc-id1",
   269  		"username":     "acc-id1",
   270  		"validation":   "unproven",
   271  		"timestamp":    aks.since.Format(time.RFC3339),
   272  	}
   273  	acct1, err := asserts.AssembleAndSignInTest(asserts.AccountType, headers, nil, trustedKey)
   274  	c.Assert(err, IsNil)
   275  
   276  	// prereq
   277  	db.Add(acct1)
   278  }
   279  
   280  func (aks *accountKeySuite) TestAccountKeyCheck(c *C) {
   281  	trustedKey := testPrivKey0
   282  
   283  	headers := map[string]interface{}{
   284  		"authority-id":        "canonical",
   285  		"account-id":          "acc-id1",
   286  		"name":                "default",
   287  		"public-key-sha3-384": aks.keyID,
   288  		"since":               aks.since.Format(time.RFC3339),
   289  		"until":               aks.until.Format(time.RFC3339),
   290  	}
   291  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   292  	c.Assert(err, IsNil)
   293  
   294  	db := aks.openDB(c)
   295  
   296  	aks.prereqAccount(c, db)
   297  
   298  	err = db.Check(accKey)
   299  	c.Assert(err, IsNil)
   300  }
   301  
   302  func (aks *accountKeySuite) TestAccountKeyCheckNoAccount(c *C) {
   303  	trustedKey := testPrivKey0
   304  
   305  	headers := map[string]interface{}{
   306  		"authority-id":        "canonical",
   307  		"account-id":          "acc-id1",
   308  		"name":                "default",
   309  		"public-key-sha3-384": aks.keyID,
   310  		"since":               aks.since.Format(time.RFC3339),
   311  		"until":               aks.until.Format(time.RFC3339),
   312  	}
   313  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   314  	c.Assert(err, IsNil)
   315  
   316  	db := aks.openDB(c)
   317  
   318  	err = db.Check(accKey)
   319  	c.Assert(err, ErrorMatches, `account-key assertion for "acc-id1" does not have a matching account assertion`)
   320  }
   321  
   322  func (aks *accountKeySuite) TestAccountKeyCheckUntrustedAuthority(c *C) {
   323  	trustedKey := testPrivKey0
   324  
   325  	db := aks.openDB(c)
   326  	storeDB := assertstest.NewSigningDB("canonical", trustedKey)
   327  	otherDB := setup3rdPartySigning(c, "other", storeDB, db)
   328  
   329  	headers := map[string]interface{}{
   330  		"account-id":          "acc-id1",
   331  		"name":                "default",
   332  		"public-key-sha3-384": aks.keyID,
   333  		"since":               aks.since.Format(time.RFC3339),
   334  		"until":               aks.until.Format(time.RFC3339),
   335  	}
   336  	accKey, err := otherDB.Sign(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), "")
   337  	c.Assert(err, IsNil)
   338  
   339  	err = db.Check(accKey)
   340  	c.Assert(err, ErrorMatches, `account-key assertion for "acc-id1" is not signed by a directly trusted authority:.*`)
   341  }
   342  
   343  func (aks *accountKeySuite) TestAccountKeyCheckSameNameAndNewRevision(c *C) {
   344  	trustedKey := testPrivKey0
   345  
   346  	headers := map[string]interface{}{
   347  		"authority-id":        "canonical",
   348  		"account-id":          "acc-id1",
   349  		"name":                "default",
   350  		"public-key-sha3-384": aks.keyID,
   351  		"since":               aks.since.Format(time.RFC3339),
   352  		"until":               aks.until.Format(time.RFC3339),
   353  	}
   354  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   355  	c.Assert(err, IsNil)
   356  
   357  	db := aks.openDB(c)
   358  	aks.prereqAccount(c, db)
   359  
   360  	err = db.Add(accKey)
   361  	c.Assert(err, IsNil)
   362  
   363  	headers["revision"] = "1"
   364  	newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   365  	c.Assert(err, IsNil)
   366  
   367  	err = db.Check(newAccKey)
   368  	c.Assert(err, IsNil)
   369  }
   370  
   371  func (aks *accountKeySuite) TestAccountKeyCheckSameAccountAndDifferentName(c *C) {
   372  	trustedKey := testPrivKey0
   373  
   374  	headers := map[string]interface{}{
   375  		"authority-id":        "canonical",
   376  		"account-id":          "acc-id1",
   377  		"name":                "default",
   378  		"public-key-sha3-384": aks.keyID,
   379  		"since":               aks.since.Format(time.RFC3339),
   380  		"until":               aks.until.Format(time.RFC3339),
   381  	}
   382  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   383  	c.Assert(err, IsNil)
   384  
   385  	db := aks.openDB(c)
   386  	aks.prereqAccount(c, db)
   387  
   388  	err = db.Add(accKey)
   389  	c.Assert(err, IsNil)
   390  
   391  	newPrivKey, _ := assertstest.GenerateKey(752)
   392  	err = db.ImportKey(newPrivKey)
   393  	c.Assert(err, IsNil)
   394  	newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID())
   395  	c.Assert(err, IsNil)
   396  	newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey)
   397  	c.Assert(err, IsNil)
   398  
   399  	headers["name"] = "another"
   400  	headers["public-key-sha3-384"] = newPubKey.ID()
   401  	newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, newPubKeyEncoded, trustedKey)
   402  	c.Assert(err, IsNil)
   403  
   404  	err = db.Check(newAccKey)
   405  	c.Assert(err, IsNil)
   406  }
   407  
   408  func (aks *accountKeySuite) TestAccountKeyCheckSameNameAndDifferentAccount(c *C) {
   409  	trustedKey := testPrivKey0
   410  
   411  	headers := map[string]interface{}{
   412  		"authority-id":        "canonical",
   413  		"account-id":          "acc-id1",
   414  		"name":                "default",
   415  		"public-key-sha3-384": aks.keyID,
   416  		"since":               aks.since.Format(time.RFC3339),
   417  		"until":               aks.until.Format(time.RFC3339),
   418  	}
   419  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   420  	c.Assert(err, IsNil)
   421  
   422  	db := aks.openDB(c)
   423  	err = db.ImportKey(trustedKey)
   424  	c.Assert(err, IsNil)
   425  	aks.prereqAccount(c, db)
   426  
   427  	err = db.Add(accKey)
   428  	c.Assert(err, IsNil)
   429  
   430  	newPrivKey, _ := assertstest.GenerateKey(752)
   431  	err = db.ImportKey(newPrivKey)
   432  	c.Assert(err, IsNil)
   433  	newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID())
   434  	c.Assert(err, IsNil)
   435  	newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey)
   436  	c.Assert(err, IsNil)
   437  
   438  	acct2 := assertstest.NewAccount(db, "acc-id2", map[string]interface{}{
   439  		"authority-id": "canonical",
   440  		"account-id":   "acc-id2",
   441  	}, trustedKey.PublicKey().ID())
   442  	db.Add(acct2)
   443  
   444  	headers["account-id"] = "acc-id2"
   445  	headers["public-key-sha3-384"] = newPubKey.ID()
   446  	headers["revision"] = "1"
   447  	newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, newPubKeyEncoded, trustedKey)
   448  	c.Assert(err, IsNil)
   449  
   450  	err = db.Check(newAccKey)
   451  	c.Assert(err, IsNil)
   452  }
   453  
   454  func (aks *accountKeySuite) TestAccountKeyCheckNameClash(c *C) {
   455  	trustedKey := testPrivKey0
   456  
   457  	headers := map[string]interface{}{
   458  		"authority-id":        "canonical",
   459  		"account-id":          "acc-id1",
   460  		"name":                "default",
   461  		"public-key-sha3-384": aks.keyID,
   462  		"since":               aks.since.Format(time.RFC3339),
   463  		"until":               aks.until.Format(time.RFC3339),
   464  	}
   465  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   466  	c.Assert(err, IsNil)
   467  
   468  	db := aks.openDB(c)
   469  	aks.prereqAccount(c, db)
   470  
   471  	err = db.Add(accKey)
   472  	c.Assert(err, IsNil)
   473  
   474  	newPrivKey, _ := assertstest.GenerateKey(752)
   475  	err = db.ImportKey(newPrivKey)
   476  	c.Assert(err, IsNil)
   477  	newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID())
   478  	c.Assert(err, IsNil)
   479  	newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey)
   480  	c.Assert(err, IsNil)
   481  
   482  	headers["public-key-sha3-384"] = newPubKey.ID()
   483  	headers["revision"] = "1"
   484  	newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, newPubKeyEncoded, trustedKey)
   485  	c.Assert(err, IsNil)
   486  
   487  	err = db.Check(newAccKey)
   488  	c.Assert(err, ErrorMatches, fmt.Sprintf(`account-key assertion for "acc-id1" with ID %q has the same name "default" as existing ID %q`, newPubKey.ID(), aks.keyID))
   489  }
   490  
   491  func (aks *accountKeySuite) TestAccountKeyAddAndFind(c *C) {
   492  	trustedKey := testPrivKey0
   493  
   494  	headers := map[string]interface{}{
   495  		"authority-id":        "canonical",
   496  		"account-id":          "acc-id1",
   497  		"name":                "default",
   498  		"public-key-sha3-384": aks.keyID,
   499  		"since":               aks.since.Format(time.RFC3339),
   500  		"until":               aks.until.Format(time.RFC3339),
   501  	}
   502  	accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey)
   503  	c.Assert(err, IsNil)
   504  
   505  	db := aks.openDB(c)
   506  
   507  	aks.prereqAccount(c, db)
   508  
   509  	err = db.Add(accKey)
   510  	c.Assert(err, IsNil)
   511  
   512  	found, err := db.Find(asserts.AccountKeyType, map[string]string{
   513  		"account-id":          "acc-id1",
   514  		"public-key-sha3-384": aks.keyID,
   515  	})
   516  	c.Assert(err, IsNil)
   517  	c.Assert(found, NotNil)
   518  	c.Check(found.Body(), DeepEquals, []byte(aks.pubKeyBody))
   519  }
   520  
   521  func (aks *accountKeySuite) TestPublicKeyIsValidAt(c *C) {
   522  	// With since and until, i.e. signing account-key expires.
   523  	encoded := "type: account-key\n" +
   524  		"authority-id: canonical\n" +
   525  		"account-id: acc-id1\n" +
   526  		"name: default\n" +
   527  		"public-key-sha3-384: " + aks.keyID + "\n" +
   528  		aks.sinceLine +
   529  		aks.untilLine +
   530  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   531  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   532  		aks.pubKeyBody + "\n\n" +
   533  		"AXNpZw=="
   534  	a, err := asserts.Decode([]byte(encoded))
   535  	c.Assert(err, IsNil)
   536  
   537  	accKey := a.(*asserts.AccountKey)
   538  
   539  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since), Equals, true)
   540  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, -1)), Equals, false)
   541  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, 1)), Equals, true)
   542  
   543  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until), Equals, false)
   544  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, -1, 0)), Equals, true)
   545  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, 1, 0)), Equals, false)
   546  
   547  	// With no until, i.e. signing account-key never expires.
   548  	encoded = "type: account-key\n" +
   549  		"authority-id: canonical\n" +
   550  		"account-id: acc-id1\n" +
   551  		"name: default\n" +
   552  		"public-key-sha3-384: " + aks.keyID + "\n" +
   553  		aks.sinceLine +
   554  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   555  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   556  		aks.pubKeyBody + "\n\n" +
   557  		"openpgp c2ln"
   558  	a, err = asserts.Decode([]byte(encoded))
   559  	c.Assert(err, IsNil)
   560  
   561  	accKey = a.(*asserts.AccountKey)
   562  
   563  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since), Equals, true)
   564  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, -1)), Equals, false)
   565  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, 1)), Equals, true)
   566  
   567  	// With since == until, i.e. signing account-key has been revoked.
   568  	encoded = "type: account-key\n" +
   569  		"authority-id: canonical\n" +
   570  		"account-id: acc-id1\n" +
   571  		"name: default\n" +
   572  		"public-key-sha3-384: " + aks.keyID + "\n" +
   573  		aks.sinceLine +
   574  		"until: " + aks.since.Format(time.RFC3339) + "\n" +
   575  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   576  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   577  		aks.pubKeyBody + "\n\n" +
   578  		"openpgp c2ln"
   579  	a, err = asserts.Decode([]byte(encoded))
   580  	c.Assert(err, IsNil)
   581  
   582  	accKey = a.(*asserts.AccountKey)
   583  
   584  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since), Equals, false)
   585  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, -1)), Equals, false)
   586  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, 1)), Equals, false)
   587  
   588  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until), Equals, false)
   589  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, -1, 0)), Equals, false)
   590  	c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, 1, 0)), Equals, false)
   591  }
   592  
   593  func (aks *accountKeySuite) TestPrerequisites(c *C) {
   594  	encoded := "type: account-key\n" +
   595  		"authority-id: canonical\n" +
   596  		"account-id: acc-id1\n" +
   597  		"name: default\n" +
   598  		"public-key-sha3-384: " + aks.keyID + "\n" +
   599  		aks.sinceLine +
   600  		aks.untilLine +
   601  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   602  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" +
   603  		aks.pubKeyBody + "\n\n" +
   604  		"AXNpZw=="
   605  	a, err := asserts.Decode([]byte(encoded))
   606  	c.Assert(err, IsNil)
   607  
   608  	prereqs := a.Prerequisites()
   609  	c.Assert(prereqs, HasLen, 1)
   610  	c.Check(prereqs[0], DeepEquals, &asserts.Ref{
   611  		Type:       asserts.AccountType,
   612  		PrimaryKey: []string{"acc-id1"},
   613  	})
   614  }
   615  
   616  func (aks *accountKeySuite) TestAccountKeyRequestHappy(c *C) {
   617  	akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType,
   618  		map[string]interface{}{
   619  			"account-id":          "acc-id1",
   620  			"name":                "default",
   621  			"public-key-sha3-384": aks.keyID,
   622  			"since":               aks.since.Format(time.RFC3339),
   623  		}, []byte(aks.pubKeyBody), aks.privKey)
   624  	c.Assert(err, IsNil)
   625  
   626  	// roundtrip
   627  	a, err := asserts.Decode(asserts.Encode(akr))
   628  	c.Assert(err, IsNil)
   629  
   630  	akr2, ok := a.(*asserts.AccountKeyRequest)
   631  	c.Assert(ok, Equals, true)
   632  
   633  	db := aks.openDB(c)
   634  	aks.prereqAccount(c, db)
   635  
   636  	err = db.Check(akr2)
   637  	c.Check(err, IsNil)
   638  
   639  	c.Check(akr2.AccountID(), Equals, "acc-id1")
   640  	c.Check(akr2.Name(), Equals, "default")
   641  	c.Check(akr2.PublicKeyID(), Equals, aks.keyID)
   642  	c.Check(akr2.Since(), Equals, aks.since)
   643  }
   644  
   645  func (aks *accountKeySuite) TestAccountKeyRequestUntil(c *C) {
   646  	db := aks.openDB(c)
   647  	aks.prereqAccount(c, db)
   648  
   649  	tests := []struct {
   650  		untilHeader string
   651  		until       time.Time
   652  	}{
   653  		{"", time.Time{}},                           // zero time default
   654  		{aks.until.Format(time.RFC3339), aks.until}, // in the future
   655  		{aks.since.Format(time.RFC3339), aks.since}, // same as since
   656  	}
   657  
   658  	for _, test := range tests {
   659  		c.Log(test)
   660  		headers := map[string]interface{}{
   661  			"account-id":          "acc-id1",
   662  			"name":                "default",
   663  			"public-key-sha3-384": aks.keyID,
   664  			"since":               aks.since.Format(time.RFC3339),
   665  		}
   666  		if test.untilHeader != "" {
   667  			headers["until"] = test.untilHeader
   668  		}
   669  		akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType, headers, []byte(aks.pubKeyBody), aks.privKey)
   670  		c.Assert(err, IsNil)
   671  		a, err := asserts.Decode(asserts.Encode(akr))
   672  		c.Assert(err, IsNil)
   673  		akr2 := a.(*asserts.AccountKeyRequest)
   674  		c.Check(akr2.Until(), Equals, test.until)
   675  		err = db.Check(akr2)
   676  		c.Check(err, IsNil)
   677  	}
   678  }
   679  
   680  func (aks *accountKeySuite) TestAccountKeyRequestAddAndFind(c *C) {
   681  	akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType,
   682  		map[string]interface{}{
   683  			"account-id":          "acc-id1",
   684  			"name":                "default",
   685  			"public-key-sha3-384": aks.keyID,
   686  			"since":               aks.since.Format(time.RFC3339),
   687  		}, []byte(aks.pubKeyBody), aks.privKey)
   688  	c.Assert(err, IsNil)
   689  
   690  	db := aks.openDB(c)
   691  	aks.prereqAccount(c, db)
   692  
   693  	err = db.Add(akr)
   694  	c.Assert(err, IsNil)
   695  
   696  	found, err := db.Find(asserts.AccountKeyRequestType, map[string]string{
   697  		"account-id":          "acc-id1",
   698  		"public-key-sha3-384": aks.keyID,
   699  	})
   700  	c.Assert(err, IsNil)
   701  	c.Assert(found, NotNil)
   702  	c.Check(found.Body(), DeepEquals, []byte(aks.pubKeyBody))
   703  }
   704  
   705  func (aks *accountKeySuite) TestAccountKeyRequestDecodeInvalid(c *C) {
   706  	encoded := "type: account-key-request\n" +
   707  		"account-id: acc-id1\n" +
   708  		"name: default\n" +
   709  		"public-key-sha3-384: " + aks.keyID + "\n" +
   710  		aks.sinceLine +
   711  		aks.untilLine +
   712  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   713  		"sign-key-sha3-384: " + aks.privKey.PublicKey().ID() + "\n\n" +
   714  		aks.pubKeyBody + "\n\n" +
   715  		"AXNpZw=="
   716  
   717  	untilPast := aks.since.AddDate(-1, 0, 0)
   718  	untilPastLine := "until: " + untilPast.Format(time.RFC3339) + "\n"
   719  
   720  	invalidTests := []struct{ original, invalid, expectedErr string }{
   721  		{"account-id: acc-id1\n", "", `"account-id" header is mandatory`},
   722  		{"account-id: acc-id1\n", "account-id: \n", `"account-id" header should not be empty`},
   723  		{"name: default\n", "", `"name" header is mandatory`},
   724  		{"name: default\n", "name: \n", `"name" header should not be empty`},
   725  		{"name: default\n", "name: a b\n", `"name" header contains invalid characters: "a b"`},
   726  		{"name: default\n", "name: -default\n", `"name" header contains invalid characters: "-default"`},
   727  		{"name: default\n", "name: foo:bar\n", `"name" header contains invalid characters: "foo:bar"`},
   728  		{"public-key-sha3-384: " + aks.keyID + "\n", "", `"public-key-sha3-384" header is mandatory`},
   729  		{"public-key-sha3-384: " + aks.keyID + "\n", "public-key-sha3-384: \n", `"public-key-sha3-384" header should not be empty`},
   730  		{aks.sinceLine, "", `"since" header is mandatory`},
   731  		{aks.sinceLine, "since: \n", `"since" header should not be empty`},
   732  		{aks.sinceLine, "since: 12:30\n", `"since" header is not a RFC3339 date: .*`},
   733  		{aks.sinceLine, "since: \n", `"since" header should not be empty`},
   734  		{aks.untilLine, "until: \n", `"until" header is not a RFC3339 date: .*`},
   735  		{aks.untilLine, "until: 12:30\n", `"until" header is not a RFC3339 date: .*`},
   736  		{aks.untilLine, untilPastLine, `'until' time cannot be before 'since' time`},
   737  	}
   738  
   739  	for _, test := range invalidTests {
   740  		invalid := strings.Replace(encoded, test.original, test.invalid, 1)
   741  		_, err := asserts.Decode([]byte(invalid))
   742  		c.Check(err, ErrorMatches, accKeyReqErrPrefix+test.expectedErr)
   743  	}
   744  }
   745  
   746  func (aks *accountKeySuite) TestAccountKeyRequestDecodeInvalidPublicKey(c *C) {
   747  	headers := "type: account-key-request\n" +
   748  		"account-id: acc-id1\n" +
   749  		"name: default\n" +
   750  		"public-key-sha3-384: " + aks.keyID + "\n" +
   751  		aks.sinceLine +
   752  		aks.untilLine
   753  
   754  	raw, err := base64.StdEncoding.DecodeString(aks.pubKeyBody)
   755  	c.Assert(err, IsNil)
   756  	spurious := base64.StdEncoding.EncodeToString(append(raw, "gorp"...))
   757  
   758  	invalidPublicKeyTests := []struct{ body, expectedErr string }{
   759  		{"", "cannot decode public key: no data"},
   760  		{"==", "cannot decode public key: .*"},
   761  		{"stuff", "cannot decode public key: .*"},
   762  		{"AnNpZw==", "unsupported public key format version: 2"},
   763  		{"AUJST0tFTg==", "cannot decode public key: .*"},
   764  		{spurious, "public key has spurious trailing data"},
   765  	}
   766  
   767  	for _, test := range invalidPublicKeyTests {
   768  		invalid := headers +
   769  			fmt.Sprintf("body-length: %v", len(test.body)) + "\n" +
   770  			"sign-key-sha3-384: " + aks.privKey.PublicKey().ID() + "\n\n" +
   771  			test.body + "\n\n" +
   772  			"AXNpZw=="
   773  
   774  		_, err := asserts.Decode([]byte(invalid))
   775  		c.Check(err, ErrorMatches, accKeyReqErrPrefix+test.expectedErr)
   776  	}
   777  }
   778  
   779  func (aks *accountKeySuite) TestAccountKeyRequestDecodeKeyIDMismatch(c *C) {
   780  	invalid := "type: account-key-request\n" +
   781  		"account-id: acc-id1\n" +
   782  		"name: default\n" +
   783  		"public-key-sha3-384: aa\n" +
   784  		aks.sinceLine +
   785  		aks.untilLine +
   786  		fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" +
   787  		"sign-key-sha3-384: " + aks.privKey.PublicKey().ID() + "\n\n" +
   788  		aks.pubKeyBody + "\n\n" +
   789  		"AXNpZw=="
   790  
   791  	_, err := asserts.Decode([]byte(invalid))
   792  	c.Check(err, ErrorMatches, "assertion account-key-request: public key does not match provided key id")
   793  }
   794  
   795  func (aks *accountKeySuite) TestAccountKeyRequestNoAccount(c *C) {
   796  	headers := map[string]interface{}{
   797  		"account-id":          "acc-id1",
   798  		"name":                "default",
   799  		"public-key-sha3-384": aks.keyID,
   800  		"since":               aks.since.Format(time.RFC3339),
   801  	}
   802  	akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType, headers, []byte(aks.pubKeyBody), aks.privKey)
   803  	c.Assert(err, IsNil)
   804  
   805  	db := aks.openDB(c)
   806  
   807  	err = db.Check(akr)
   808  	c.Assert(err, ErrorMatches, `account-key-request assertion for "acc-id1" does not have a matching account assertion`)
   809  }