Files
snapd/asserts/database_test.go
Miguel Pires 79c5ac14b2 many: remove usages of deprecated io/ioutil package (#13768)
* many: remove usages of deprecated io/ioutil package

Signed-off-by: Miguel Pires <miguel.pires@canonical.com>

* .golangci.yml: remove errcheck ignore rule for io/ioutil

Signed-off-by: Miguel Pires <miguel.pires@canonical.com>

* run-checks: prevent new usages of io/ioutil

Signed-off-by: Miguel Pires <miguel.pires@canonical.com>

---------

Signed-off-by: Miguel Pires <miguel.pires@canonical.com>
2024-04-03 23:23:24 +02:00

1701 lines
51 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2015-2022 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package asserts_test
import (
"bytes"
"crypto"
"encoding/base64"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"testing"
"time"
"golang.org/x/crypto/openpgp/packet"
"golang.org/x/crypto/sha3"
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/assertstest"
"github.com/snapcore/snapd/testutil"
)
func Test(t *testing.T) { TestingT(t) }
var _ = Suite(&openSuite{})
var _ = Suite(&revisionErrorSuite{})
var _ = Suite(&isUnacceptedUpdateSuite{})
type openSuite struct{}
func (opens *openSuite) TestOpenDatabaseOK(c *C) {
cfg := &asserts.DatabaseConfig{
Backstore: asserts.NewMemoryBackstore(),
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
c.Assert(db, NotNil)
}
func (opens *openSuite) TestOpenDatabaseTrustedAccount(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"account-id": "trusted",
"display-name": "Trusted",
"validation": "verified",
"timestamp": "2015-01-01T14:00:00Z",
}
acct, err := asserts.AssembleAndSignInTest(asserts.AccountType, headers, nil, testPrivKey0)
c.Assert(err, IsNil)
cfg := &asserts.DatabaseConfig{
Backstore: asserts.NewMemoryBackstore(),
Trusted: []asserts.Assertion{acct},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
a, err := db.Find(asserts.AccountType, map[string]string{
"account-id": "trusted",
})
c.Assert(err, IsNil)
acct1 := a.(*asserts.Account)
c.Check(acct1.AccountID(), Equals, "trusted")
c.Check(acct1.DisplayName(), Equals, "Trusted")
c.Check(db.IsTrustedAccount("trusted"), Equals, true)
// empty account id (invalid) is not trusted
c.Check(db.IsTrustedAccount(""), Equals, false)
}
func (opens *openSuite) TestOpenDatabaseTrustedWrongType(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "0",
}
a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey0)
c.Assert(err, IsNil)
cfg := &asserts.DatabaseConfig{
Trusted: []asserts.Assertion{a},
}
_, err = asserts.OpenDatabase(cfg)
c.Assert(err, ErrorMatches, "cannot predefine trusted assertions that are not account-key or account: test-only")
}
type databaseSuite struct {
topDir string
db *asserts.Database
}
var _ = Suite(&databaseSuite{})
func (dbs *databaseSuite) SetUpTest(c *C) {
dbs.topDir = filepath.Join(c.MkDir(), "asserts-db")
fsKeypairMgr, err := asserts.OpenFSKeypairManager(dbs.topDir)
c.Assert(err, IsNil)
cfg := &asserts.DatabaseConfig{
KeypairManager: fsKeypairMgr,
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
dbs.db = db
}
func (dbs *databaseSuite) TestImportKey(c *C) {
err := dbs.db.ImportKey(testPrivKey1)
c.Assert(err, IsNil)
keyPath := filepath.Join(dbs.topDir, "private-keys-v1", testPrivKey1SHA3_384)
info, err := os.Stat(keyPath)
c.Assert(err, IsNil)
c.Check(info.Mode().Perm(), Equals, os.FileMode(0600)) // secret
// too much "clear box" testing? ok at least until we have
// more functionality
privKey, err := os.ReadFile(keyPath)
c.Assert(err, IsNil)
privKeyFromDisk, err := asserts.DecodePrivateKeyInTest(privKey)
c.Assert(err, IsNil)
c.Check(privKeyFromDisk.PublicKey().ID(), Equals, testPrivKey1SHA3_384)
}
func (dbs *databaseSuite) TestImportKeyAlreadyExists(c *C) {
err := dbs.db.ImportKey(testPrivKey1)
c.Assert(err, IsNil)
err = dbs.db.ImportKey(testPrivKey1)
c.Check(err, ErrorMatches, "key pair with given key id already exists")
}
func (dbs *databaseSuite) TestPublicKey(c *C) {
pk := testPrivKey1
keyID := pk.PublicKey().ID()
err := dbs.db.ImportKey(pk)
c.Assert(err, IsNil)
pubk, err := dbs.db.PublicKey(keyID)
c.Assert(err, IsNil)
c.Check(pubk.ID(), Equals, keyID)
// usual pattern is to then encode it
encoded, err := asserts.EncodePublicKey(pubk)
c.Assert(err, IsNil)
data, err := base64.StdEncoding.DecodeString(string(encoded))
c.Assert(err, IsNil)
c.Check(data[0], Equals, uint8(1)) // v1
// check details of packet
const newHeaderBits = 0x80 | 0x40
c.Check(data[1]&newHeaderBits, Equals, uint8(newHeaderBits))
c.Check(data[2] < 192, Equals, true) // small packet, 1 byte length
c.Check(data[3], Equals, uint8(4)) // openpgp v4
pkt, err := packet.Read(bytes.NewBuffer(data[1:]))
c.Assert(err, IsNil)
pubKey, ok := pkt.(*packet.PublicKey)
c.Assert(ok, Equals, true)
c.Check(pubKey.PubKeyAlgo, Equals, packet.PubKeyAlgoRSA)
c.Check(pubKey.IsSubkey, Equals, false)
fixedTimestamp := time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC)
c.Check(pubKey.CreationTime.Equal(fixedTimestamp), Equals, true)
// hash of blob content == hash of key
h384 := sha3.Sum384(data)
encHash := base64.RawURLEncoding.EncodeToString(h384[:])
c.Check(encHash, DeepEquals, testPrivKey1SHA3_384)
}
func (dbs *databaseSuite) TestPublicKeyNotFound(c *C) {
pk := testPrivKey1
keyID := pk.PublicKey().ID()
_, err := dbs.db.PublicKey(keyID)
c.Check(err, ErrorMatches, "cannot find key pair")
err = dbs.db.ImportKey(pk)
c.Assert(err, IsNil)
_, err = dbs.db.PublicKey("ff" + keyID)
c.Check(err, ErrorMatches, "cannot find key pair")
}
func (dbs *databaseSuite) TestNotFoundErrorIs(c *C) {
this := &asserts.NotFoundError{
Headers: map[string]string{"a": "a"},
Type: asserts.ValidationSetType,
}
that := &asserts.NotFoundError{
Headers: map[string]string{"b": "b"},
Type: asserts.RepairType,
}
c.Check(this, testutil.ErrorIs, that)
}
type checkSuite struct {
bs asserts.Backstore
a asserts.Assertion
}
var _ = Suite(&checkSuite{})
func (chks *checkSuite) SetUpTest(c *C) {
var err error
topDir := filepath.Join(c.MkDir(), "asserts-db")
chks.bs, err = asserts.OpenFSBackstore(topDir)
c.Assert(err, IsNil)
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "0",
}
chks.a, err = asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey0)
c.Assert(err, IsNil)
}
func (chks *checkSuite) TestCheckNoPubKey(c *C) {
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
err = db.Check(chks.a)
c.Assert(err, ErrorMatches, `no matching public key "[[:alnum:]_-]+" for signature by "canonical"`)
}
func (chks *checkSuite) TestCheckExpiredPubKey(c *C) {
fixedTimeStr := "0003-01-01T00:00:00Z"
fixedTime, err := time.Parse(time.RFC3339, fixedTimeStr)
c.Assert(err, IsNil)
restore := asserts.MockTimeNow(fixedTime)
defer restore()
trustedKey := testPrivKey0
expiredAccKey := asserts.ExpiredAccountKeyForTest("canonical", trustedKey.PublicKey())
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
Trusted: []asserts.Assertion{expiredAccKey},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
expSince := regexp.QuoteMeta(expiredAccKey.Since().Format(time.RFC3339))
expUntil := regexp.QuoteMeta(expiredAccKey.Until().Format(time.RFC3339))
curTime := regexp.QuoteMeta(fixedTimeStr)
err = db.Check(chks.a)
c.Assert(err, ErrorMatches, fmt.Sprintf(`assertion is signed with expired public key "[[:alnum:]_-]+" from "canonical": current time is %s but key is valid during \[%s, %s\)`, curTime, expSince, expUntil))
}
func (chks *checkSuite) TestCheckExpiredPubKeyNoUntil(c *C) {
curTimeStr := "0002-01-01T00:00:00Z"
curTime, err := time.Parse(time.RFC3339, curTimeStr)
c.Assert(err, IsNil)
restore := asserts.MockTimeNow(curTime)
defer restore()
trustedKey := testPrivKey0
keyTimeStr := "0003-01-01T00:00:00Z"
keyTime, err := time.Parse(time.RFC3339, keyTimeStr)
c.Assert(err, IsNil)
expiredAccKey := asserts.MakeAccountKeyForTestWithUntil("canonical", trustedKey.PublicKey(), keyTime, time.Time{}, 1)
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
Trusted: []asserts.Assertion{expiredAccKey},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
err = db.Check(chks.a)
c.Assert(err, ErrorMatches, fmt.Sprintf(`assertion is signed with expired public key "[[:alnum:]_-]+" from "canonical": current time is %s but key is valid from %s`, regexp.QuoteMeta(curTimeStr), regexp.QuoteMeta(keyTimeStr)))
}
func (chks *checkSuite) TestCheckForgery(c *C) {
trustedKey := testPrivKey0
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
Trusted: []asserts.Assertion{asserts.BootstrapAccountKeyForTest("canonical", trustedKey.PublicKey())},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
encoded := asserts.Encode(chks.a)
content, encodedSig := chks.a.Signature()
// forgery
forgedSig := new(packet.Signature)
forgedSig.PubKeyAlgo = packet.PubKeyAlgoRSA
forgedSig.Hash = crypto.SHA512
forgedSig.CreationTime = time.Now()
h := crypto.SHA512.New()
h.Write(content)
pk1 := packet.NewRSAPrivateKey(time.Unix(1, 0), testPrivKey1RSA)
err = forgedSig.Sign(h, pk1, &packet.Config{DefaultHash: crypto.SHA512})
c.Assert(err, IsNil)
buf := new(bytes.Buffer)
forgedSig.Serialize(buf)
b := append([]byte{0x1}, buf.Bytes()...)
forgedSigEncoded := base64.StdEncoding.EncodeToString(b)
forgedEncoded := bytes.Replace(encoded, encodedSig, []byte(forgedSigEncoded), 1)
c.Assert(forgedEncoded, Not(DeepEquals), encoded)
forgedAssert, err := asserts.Decode(forgedEncoded)
c.Assert(err, IsNil)
err = db.Check(forgedAssert)
c.Assert(err, ErrorMatches, "failed signature verification: .*")
}
func (chks *checkSuite) TestCheckUnsupportedFormat(c *C) {
trustedKey := testPrivKey0
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
Trusted: []asserts.Assertion{asserts.BootstrapAccountKeyForTest("canonical", trustedKey.PublicKey())},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
var a asserts.Assertion
(func() {
restore := asserts.MockMaxSupportedFormat(asserts.TestOnlyType, 77)
defer restore()
var err error
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "0",
"format": "77",
}
a, err = asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, trustedKey)
c.Assert(err, IsNil)
})()
err = db.Check(a)
c.Assert(err, FitsTypeOf, &asserts.UnsupportedFormatError{})
c.Check(err, ErrorMatches, `proposed "test-only" assertion has format 77 but 1 is latest supported`)
}
func (chks *checkSuite) TestCheckMismatchedAccountIDandKey(c *C) {
trustedKey := testPrivKey0
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
Trusted: []asserts.Assertion{asserts.BootstrapAccountKeyForTest("canonical", trustedKey.PublicKey())},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
headers := map[string]interface{}{
"authority-id": "random",
"primary-key": "0",
}
a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, trustedKey)
c.Assert(err, IsNil)
err = db.Check(a)
c.Check(err, ErrorMatches, `error finding matching public key for signature: found public key ".*" from "canonical" but expected it from: random`)
err = asserts.CheckSignature(a, cfg.Trusted[0].(*asserts.AccountKey), db, time.Time{}, time.Time{})
c.Check(err, ErrorMatches, `assertion authority "random" does not match public key from "canonical"`)
}
func (chks *checkSuite) TestCheckAndSetEarliestTime(c *C) {
trustedKey := testPrivKey0
ak := asserts.MakeAccountKeyForTest("canonical", trustedKey.PublicKey(), time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC), 2)
cfg := &asserts.DatabaseConfig{
Backstore: chks.bs,
Trusted: []asserts.Assertion{ak},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "0",
}
a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, trustedKey)
c.Assert(err, IsNil)
// now is since + 1 year, key is valid
r := asserts.MockTimeNow(ak.Since().AddDate(1, 0, 0))
defer r()
err = db.Check(a)
c.Check(err, IsNil)
// now is since - 1 year, key is invalid
pastTime := ak.Since().AddDate(-1, 0, 0)
asserts.MockTimeNow(pastTime)
err = db.Check(a)
c.Check(err, ErrorMatches, `assertion is signed with expired public key .*`)
// now is ignored but known to be at least >= pastTime
// key is considered valid
db.SetEarliestTime(pastTime)
err = db.Check(a)
c.Check(err, IsNil)
// move earliest after until
db.SetEarliestTime(ak.Until().AddDate(0, 0, 1))
err = db.Check(a)
c.Check(err, ErrorMatches, `assertion is signed with expired public key .*`)
// check using now = since - 1 year again
db.SetEarliestTime(time.Time{})
err = db.Check(a)
c.Check(err, ErrorMatches, `assertion is signed with expired public key .*`)
// now is since + 1 month, key is valid
asserts.MockTimeNow(ak.Since().AddDate(0, 1, 0))
err = db.Check(a)
c.Check(err, IsNil)
}
type signAddFindSuite struct {
signingDB *asserts.Database
signingKeyID string
db *asserts.Database
}
var _ = Suite(&signAddFindSuite{})
func (safs *signAddFindSuite) SetUpTest(c *C) {
cfg0 := &asserts.DatabaseConfig{}
db0, err := asserts.OpenDatabase(cfg0)
c.Assert(err, IsNil)
safs.signingDB = db0
pk := testPrivKey0
err = db0.ImportKey(pk)
c.Assert(err, IsNil)
safs.signingKeyID = pk.PublicKey().ID()
topDir := filepath.Join(c.MkDir(), "asserts-db")
bs, err := asserts.OpenFSBackstore(topDir)
c.Assert(err, IsNil)
headers := map[string]interface{}{
"type": "account",
"authority-id": "canonical",
"account-id": "predefined",
"validation": "verified",
"display-name": "Predef",
"timestamp": time.Now().Format(time.RFC3339),
}
predefAcct, err := safs.signingDB.Sign(asserts.AccountType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
trustedKey := testPrivKey0
cfg := &asserts.DatabaseConfig{
Backstore: bs,
Trusted: []asserts.Assertion{
asserts.BootstrapAccountForTest("canonical"),
asserts.BootstrapAccountKeyForTest("canonical", trustedKey.PublicKey()),
},
OtherPredefined: []asserts.Assertion{
predefAcct,
},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
safs.db = db
}
func (safs *signAddFindSuite) TestSign(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Check(a1)
c.Check(err, IsNil)
}
func (safs *signAddFindSuite) TestSignEmptyKeyID(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, "")
c.Assert(err, ErrorMatches, "key id is empty")
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignMissingAuthorityId(c *C) {
headers := map[string]interface{}{
"primary-key": "a",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `"authority-id" header is mandatory`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignMissingPrimaryKey(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `"primary-key" header is mandatory`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignPrimaryKeyWithSlash(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "baz/9000",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `"primary-key" primary key header cannot contain '/'`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignNoPrivateKey(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, "abcd")
c.Assert(err, ErrorMatches, "cannot find key pair")
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignUnknownType(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
}
a1, err := safs.signingDB.Sign(&asserts.AssertionType{Name: "xyz", PrimaryKey: nil}, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `internal error: unknown assertion type: "xyz"`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignNonPredefinedType(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
}
a1, err := safs.signingDB.Sign(&asserts.AssertionType{Name: "test-only", PrimaryKey: nil}, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `internal error: unpredefined assertion type for name "test-only" used.*`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignBadRevision(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"revision": "zzz",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `"revision" header is not an integer: zzz`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignBadFormat(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"format": "zzz",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `"format" header is not an integer: zzz`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignHeadersCheck(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"extra": []interface{}{1, 2},
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Check(err, ErrorMatches, `header "extra": header values must be strings or nested lists or maps with strings as the only scalars: 1`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignHeadersCheckMap(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"extra": map[string]interface{}{"a": "a", "b": 1},
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Check(err, ErrorMatches, `header "extra": header values must be strings or nested lists or maps with strings as the only scalars: 1`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignAssemblerError(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"count": "zzz",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `cannot assemble assertion test-only: "count" header is not an integer: zzz`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignUnsupportedFormat(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"format": "77",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `cannot sign "test-only" assertion with format 77 higher than max supported format 1`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestSignInadequateFormat(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"format-1-feature": "true",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, ErrorMatches, `cannot sign "test-only" assertion with format set to 0 lower than min format 1 covering included features`)
c.Check(a1, IsNil)
}
func (safs *signAddFindSuite) TestAddRefusesSelfSignedKey(c *C) {
aKey := testPrivKey2
aKeyEncoded, err := asserts.EncodePublicKey(aKey.PublicKey())
c.Assert(err, IsNil)
now := time.Now().UTC()
headers := map[string]interface{}{
"authority-id": "canonical",
"account-id": "canonical",
"public-key-sha3-384": aKey.PublicKey().ID(),
"name": "default",
"since": now.Format(time.RFC3339),
}
acctKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, aKeyEncoded, aKey)
c.Assert(err, IsNil)
// this must fail
err = safs.db.Add(acctKey)
c.Check(err, ErrorMatches, `no matching public key.*`)
}
func (safs *signAddFindSuite) TestAddSuperseding(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a1)
c.Assert(err, IsNil)
retrieved1, err := safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "a",
})
c.Assert(err, IsNil)
c.Check(retrieved1, NotNil)
c.Check(retrieved1.Revision(), Equals, 0)
headers["revision"] = "1"
a2, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a2)
c.Assert(err, IsNil)
retrieved2, err := safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "a",
})
c.Assert(err, IsNil)
c.Check(retrieved2, NotNil)
c.Check(retrieved2.Revision(), Equals, 1)
err = safs.db.Add(a1)
c.Check(err, ErrorMatches, "revision 0 is older than current revision 1")
c.Check(asserts.IsUnaccceptedUpdate(err), Equals, true)
}
func (safs *signAddFindSuite) TestAddNoAuthorityNoPrimaryKey(c *C) {
headers := map[string]interface{}{
"hdr": "FOO",
}
a, err := asserts.SignWithoutAuthority(asserts.TestOnlyNoAuthorityType, headers, nil, testPrivKey0)
c.Assert(err, IsNil)
err = safs.db.Add(a)
c.Assert(err, ErrorMatches, `internal error: assertion type "test-only-no-authority" has no primary key`)
}
func (safs *signAddFindSuite) TestAddNoAuthorityButPrimaryKey(c *C) {
headers := map[string]interface{}{
"pk": "primary",
}
a, err := asserts.SignWithoutAuthority(asserts.TestOnlyNoAuthorityPKType, headers, nil, testPrivKey0)
c.Assert(err, IsNil)
err = safs.db.Add(a)
c.Assert(err, ErrorMatches, `cannot check no-authority assertion type "test-only-no-authority-pk"`)
}
func (safs *signAddFindSuite) TestAddUnsupportedFormat(c *C) {
const unsupported = "type: test-only\n" +
"format: 77\n" +
"authority-id: canonical\n" +
"primary-key: a\n" +
"payload: unsupported\n" +
"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
"\n\n" +
"AXNpZw=="
aUnsupp, err := asserts.Decode([]byte(unsupported))
c.Assert(err, IsNil)
c.Assert(aUnsupp.SupportedFormat(), Equals, false)
err = safs.db.Add(aUnsupp)
c.Assert(err, FitsTypeOf, &asserts.UnsupportedFormatError{})
c.Check(err.(*asserts.UnsupportedFormatError).Update, Equals, false)
c.Check(err, ErrorMatches, `proposed "test-only" assertion has format 77 but 1 is latest supported`)
c.Check(asserts.IsUnaccceptedUpdate(err), Equals, false)
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"format": "1",
"payload": "supported",
}
aSupp, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey0)
c.Assert(err, IsNil)
err = safs.db.Add(aSupp)
c.Assert(err, IsNil)
err = safs.db.Add(aUnsupp)
c.Assert(err, FitsTypeOf, &asserts.UnsupportedFormatError{})
c.Check(err.(*asserts.UnsupportedFormatError).Update, Equals, true)
c.Check(err, ErrorMatches, `proposed "test-only" assertion has format 77 but 1 is latest supported \(current not updated\)`)
c.Check(asserts.IsUnaccceptedUpdate(err), Equals, true)
}
func (safs *signAddFindSuite) TestNotFoundError(c *C) {
err1 := &asserts.NotFoundError{
Type: asserts.SnapDeclarationType,
Headers: map[string]string{
"series": "16",
"snap-id": "snap-id",
},
}
c.Check(errors.Is(err1, &asserts.NotFoundError{}), Equals, true)
c.Check(err1.Error(), Equals, "snap-declaration (snap-id; series:16) not found")
err2 := &asserts.NotFoundError{
Type: asserts.SnapRevisionType,
}
c.Check(errors.Is(err2, &asserts.NotFoundError{}), Equals, true)
c.Check(err2.Error(), Equals, "snap-revision assertion not found")
}
func (safs *signAddFindSuite) TestFindNotFound(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a1)
c.Assert(err, IsNil)
hdrs := map[string]string{
"primary-key": "b",
}
retrieved1, err := safs.db.Find(asserts.TestOnlyType, hdrs)
c.Assert(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: hdrs,
})
c.Check(retrieved1, IsNil)
// checking also extra headers
hdrs = map[string]string{
"primary-key": "a",
"authority-id": "other-auth-id",
}
retrieved1, err = safs.db.Find(asserts.TestOnlyType, hdrs)
c.Assert(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: hdrs,
})
c.Check(retrieved1, IsNil)
}
func (safs *signAddFindSuite) TestFindPrimaryLeftOut(c *C) {
retrieved1, err := safs.db.Find(asserts.TestOnlyType, map[string]string{})
c.Assert(err, ErrorMatches, "must provide primary key: primary-key")
c.Check(retrieved1, IsNil)
}
func (safs *signAddFindSuite) TestFindMany(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "a",
"other": "other-x",
}
aa, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(aa)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "b",
"other": "other-y",
}
ab, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(ab)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "c",
"other": "other-x",
}
ac, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(ac)
c.Assert(err, IsNil)
res, err := safs.db.FindMany(asserts.TestOnlyType, map[string]string{
"other": "other-x",
})
c.Assert(err, IsNil)
c.Assert(res, HasLen, 2)
primKeys := []string{res[0].HeaderString("primary-key"), res[1].HeaderString("primary-key")}
sort.Strings(primKeys)
c.Check(primKeys, DeepEquals, []string{"a", "c"})
res, err = safs.db.FindMany(asserts.TestOnlyType, map[string]string{
"other": "other-y",
})
c.Assert(err, IsNil)
c.Assert(res, HasLen, 1)
c.Check(res[0].Header("primary-key"), Equals, "b")
res, err = safs.db.FindMany(asserts.TestOnlyType, map[string]string{})
c.Assert(err, IsNil)
c.Assert(res, HasLen, 3)
res, err = safs.db.FindMany(asserts.TestOnlyType, map[string]string{
"primary-key": "b",
"other": "other-y",
})
c.Assert(err, IsNil)
c.Assert(res, HasLen, 1)
hdrs := map[string]string{
"primary-key": "b",
"other": "other-x",
}
res, err = safs.db.FindMany(asserts.TestOnlyType, hdrs)
c.Assert(res, HasLen, 0)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: hdrs,
})
}
func (safs *signAddFindSuite) TestFindFindsPredefined(c *C) {
pk1 := testPrivKey1
acct1 := assertstest.NewAccount(safs.signingDB, "acc-id1", map[string]interface{}{
"authority-id": "canonical",
}, safs.signingKeyID)
acct1Key := assertstest.NewAccountKey(safs.signingDB, acct1, map[string]interface{}{
"authority-id": "canonical",
}, pk1.PublicKey(), safs.signingKeyID)
err := safs.db.Add(acct1)
c.Assert(err, IsNil)
err = safs.db.Add(acct1Key)
c.Assert(err, IsNil)
// find the trusted key as well
tKey, err := safs.db.Find(asserts.AccountKeyType, map[string]string{
"account-id": "canonical",
"public-key-sha3-384": safs.signingKeyID,
})
c.Assert(err, IsNil)
c.Assert(tKey.(*asserts.AccountKey).AccountID(), Equals, "canonical")
c.Assert(tKey.(*asserts.AccountKey).PublicKeyID(), Equals, safs.signingKeyID)
// find predefined account as well
predefAcct, err := safs.db.Find(asserts.AccountType, map[string]string{
"account-id": "predefined",
})
c.Assert(err, IsNil)
c.Assert(predefAcct.(*asserts.Account).AccountID(), Equals, "predefined")
c.Assert(predefAcct.(*asserts.Account).DisplayName(), Equals, "Predef")
// find trusted and indirectly trusted
accKeys, err := safs.db.FindMany(asserts.AccountKeyType, nil)
c.Assert(err, IsNil)
c.Check(accKeys, HasLen, 2)
accts, err := safs.db.FindMany(asserts.AccountType, nil)
c.Assert(err, IsNil)
c.Check(accts, HasLen, 3)
}
func (safs *signAddFindSuite) TestFindTrusted(c *C) {
pk1 := testPrivKey1
acct1 := assertstest.NewAccount(safs.signingDB, "acc-id1", map[string]interface{}{
"authority-id": "canonical",
}, safs.signingKeyID)
acct1Key := assertstest.NewAccountKey(safs.signingDB, acct1, map[string]interface{}{
"authority-id": "canonical",
}, pk1.PublicKey(), safs.signingKeyID)
err := safs.db.Add(acct1)
c.Assert(err, IsNil)
err = safs.db.Add(acct1Key)
c.Assert(err, IsNil)
// find the trusted account
tAcct, err := safs.db.FindTrusted(asserts.AccountType, map[string]string{
"account-id": "canonical",
})
c.Assert(err, IsNil)
c.Assert(tAcct.(*asserts.Account).AccountID(), Equals, "canonical")
// find the trusted key
tKey, err := safs.db.FindTrusted(asserts.AccountKeyType, map[string]string{
"account-id": "canonical",
"public-key-sha3-384": safs.signingKeyID,
})
c.Assert(err, IsNil)
c.Assert(tKey.(*asserts.AccountKey).AccountID(), Equals, "canonical")
c.Assert(tKey.(*asserts.AccountKey).PublicKeyID(), Equals, safs.signingKeyID)
// doesn't find not trusted assertions
hdrs := map[string]string{
"account-id": acct1.AccountID(),
}
_, err = safs.db.FindTrusted(asserts.AccountType, hdrs)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.AccountType,
Headers: hdrs,
})
hdrs = map[string]string{
"account-id": acct1.AccountID(),
"public-key-sha3-384": acct1Key.PublicKeyID(),
}
_, err = safs.db.FindTrusted(asserts.AccountKeyType, hdrs)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.AccountKeyType,
Headers: hdrs,
})
_, err = safs.db.FindTrusted(asserts.AccountType, map[string]string{
"account-id": "predefined",
})
c.Check(errors.Is(err, &asserts.NotFoundError{}), Equals, true)
}
func (safs *signAddFindSuite) TestFindPredefined(c *C) {
pk1 := testPrivKey1
acct1 := assertstest.NewAccount(safs.signingDB, "acc-id1", map[string]interface{}{
"authority-id": "canonical",
}, safs.signingKeyID)
acct1Key := assertstest.NewAccountKey(safs.signingDB, acct1, map[string]interface{}{
"authority-id": "canonical",
}, pk1.PublicKey(), safs.signingKeyID)
err := safs.db.Add(acct1)
c.Assert(err, IsNil)
err = safs.db.Add(acct1Key)
c.Assert(err, IsNil)
// find the trusted account
tAcct, err := safs.db.FindPredefined(asserts.AccountType, map[string]string{
"account-id": "canonical",
})
c.Assert(err, IsNil)
c.Assert(tAcct.(*asserts.Account).AccountID(), Equals, "canonical")
// find the trusted key
tKey, err := safs.db.FindPredefined(asserts.AccountKeyType, map[string]string{
"account-id": "canonical",
"public-key-sha3-384": safs.signingKeyID,
})
c.Assert(err, IsNil)
c.Assert(tKey.(*asserts.AccountKey).AccountID(), Equals, "canonical")
c.Assert(tKey.(*asserts.AccountKey).PublicKeyID(), Equals, safs.signingKeyID)
// find predefined account as well
predefAcct, err := safs.db.FindPredefined(asserts.AccountType, map[string]string{
"account-id": "predefined",
})
c.Assert(err, IsNil)
c.Assert(predefAcct.(*asserts.Account).AccountID(), Equals, "predefined")
c.Assert(predefAcct.(*asserts.Account).DisplayName(), Equals, "Predef")
// doesn't find not trusted or predefined assertions
hdrs := map[string]string{
"account-id": acct1.AccountID(),
}
_, err = safs.db.FindPredefined(asserts.AccountType, hdrs)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.AccountType,
Headers: hdrs,
})
hdrs = map[string]string{
"account-id": acct1.AccountID(),
"public-key-sha3-384": acct1Key.PublicKeyID(),
}
_, err = safs.db.FindPredefined(asserts.AccountKeyType, hdrs)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.AccountKeyType,
Headers: hdrs,
})
}
func (safs *signAddFindSuite) TestFindManyPredefined(c *C) {
headers := map[string]interface{}{
"type": "account",
"authority-id": "canonical",
"account-id": "predefined",
"validation": "verified",
"display-name": "Predef",
"timestamp": time.Now().Format(time.RFC3339),
}
predefAcct, err := safs.signingDB.Sign(asserts.AccountType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
trustedKey0 := testPrivKey0
trustedKey1 := testPrivKey1
cfg := &asserts.DatabaseConfig{
Backstore: asserts.NewMemoryBackstore(),
Trusted: []asserts.Assertion{
asserts.BootstrapAccountForTest("canonical"),
asserts.BootstrapAccountKeyForTest("canonical", trustedKey0.PublicKey()),
asserts.BootstrapAccountKeyForTest("canonical", trustedKey1.PublicKey()),
},
OtherPredefined: []asserts.Assertion{
predefAcct,
},
}
db, err := asserts.OpenDatabase(cfg)
c.Assert(err, IsNil)
pk1 := testPrivKey2
acct1 := assertstest.NewAccount(safs.signingDB, "acc-id1", map[string]interface{}{
"authority-id": "canonical",
}, safs.signingKeyID)
acct1Key := assertstest.NewAccountKey(safs.signingDB, acct1, map[string]interface{}{
"authority-id": "canonical",
}, pk1.PublicKey(), safs.signingKeyID)
err = db.Add(acct1)
c.Assert(err, IsNil)
err = db.Add(acct1Key)
c.Assert(err, IsNil)
// find the trusted account
tAccts, err := db.FindManyPredefined(asserts.AccountType, map[string]string{
"account-id": "canonical",
})
c.Assert(err, IsNil)
c.Assert(tAccts, HasLen, 1)
c.Assert(tAccts[0].(*asserts.Account).AccountID(), Equals, "canonical")
// find the predefined account
pAccts, err := db.FindManyPredefined(asserts.AccountType, map[string]string{
"account-id": "predefined",
})
c.Assert(err, IsNil)
c.Assert(pAccts, HasLen, 1)
c.Assert(pAccts[0].(*asserts.Account).AccountID(), Equals, "predefined")
// find the multiple trusted keys
tKeys, err := db.FindManyPredefined(asserts.AccountKeyType, map[string]string{
"account-id": "canonical",
})
c.Assert(err, IsNil)
c.Assert(tKeys, HasLen, 2)
got := make(map[string]string)
for _, a := range tKeys {
acctKey := a.(*asserts.AccountKey)
got[acctKey.PublicKeyID()] = acctKey.AccountID()
}
c.Check(got, DeepEquals, map[string]string{
trustedKey0.PublicKey().ID(): "canonical",
trustedKey1.PublicKey().ID(): "canonical",
})
// doesn't find not predefined assertions
hdrs := map[string]string{
"account-id": acct1.AccountID(),
}
_, err = db.FindManyPredefined(asserts.AccountType, hdrs)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.AccountType,
Headers: hdrs,
})
_, err = db.FindManyPredefined(asserts.AccountKeyType, map[string]string{
"account-id": acct1.AccountID(),
"public-key-sha3-384": acct1Key.PublicKeyID(),
})
c.Check(errors.Is(err, &asserts.NotFoundError{}), Equals, true)
}
func (safs *signAddFindSuite) TestDontLetAddConfusinglyAssertionClashingWithTrustedOnes(c *C) {
// trusted
pubKey0, err := safs.signingDB.PublicKey(safs.signingKeyID)
c.Assert(err, IsNil)
pubKey0Encoded, err := asserts.EncodePublicKey(pubKey0)
c.Assert(err, IsNil)
now := time.Now().UTC()
headers := map[string]interface{}{
"authority-id": "canonical",
"account-id": "canonical",
"public-key-sha3-384": safs.signingKeyID,
"name": "default",
"since": now.Format(time.RFC3339),
"until": now.AddDate(1, 0, 0).Format(time.RFC3339),
}
tKey, err := safs.signingDB.Sign(asserts.AccountKeyType, headers, []byte(pubKey0Encoded), safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(tKey)
c.Check(err, ErrorMatches, `cannot add "account-key" assertion with primary key clashing with a trusted assertion: .*`)
}
func (safs *signAddFindSuite) TestDontLetAddConfusinglyAssertionClashingWithPredefinedOnes(c *C) {
headers := map[string]interface{}{
"type": "account",
"authority-id": "canonical",
"account-id": "predefined",
"validation": "verified",
"display-name": "Predef",
"timestamp": time.Now().Format(time.RFC3339),
}
predefAcct, err := safs.signingDB.Sign(asserts.AccountType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(predefAcct)
c.Check(err, ErrorMatches, `cannot add "account" assertion with primary key clashing with a predefined assertion: .*`)
}
func (safs *signAddFindSuite) TestFindAndRefResolve(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"pk1": "ka",
"pk2": "kb",
}
a1, err := safs.signingDB.Sign(asserts.TestOnly2Type, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a1)
c.Assert(err, IsNil)
ref := &asserts.Ref{
Type: asserts.TestOnly2Type,
PrimaryKey: []string{"ka", "kb"},
}
resolved, err := ref.Resolve(safs.db.Find)
c.Assert(err, IsNil)
c.Check(resolved.Headers(), DeepEquals, map[string]interface{}{
"type": "test-only-2",
"authority-id": "canonical",
"pk1": "ka",
"pk2": "kb",
"sign-key-sha3-384": resolved.SignKeyID(),
})
ref = &asserts.Ref{
Type: asserts.TestOnly2Type,
PrimaryKey: []string{"kb", "ka"},
}
_, err = ref.Resolve(safs.db.Find)
c.Assert(err, DeepEquals, &asserts.NotFoundError{
Type: ref.Type,
Headers: map[string]string{
"pk1": "kb",
"pk2": "ka",
},
})
}
func (safs *signAddFindSuite) TestFindMaxFormat(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "foo",
}
af0, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(af0)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "foo",
"format": "1",
"revision": "1",
}
af1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(af1)
c.Assert(err, IsNil)
a, err := safs.db.FindMaxFormat(asserts.TestOnlyType, map[string]string{
"primary-key": "foo",
}, 1)
c.Assert(err, IsNil)
c.Check(a.Revision(), Equals, 1)
a, err = safs.db.FindMaxFormat(asserts.TestOnlyType, map[string]string{
"primary-key": "foo",
}, 0)
c.Assert(err, IsNil)
c.Check(a.Revision(), Equals, 0)
a, err = safs.db.FindMaxFormat(asserts.TestOnlyType, map[string]string{
"primary-key": "foo",
}, 3)
c.Check(err, ErrorMatches, `cannot find "test-only" assertions for format 3 higher than supported format 1`)
c.Check(a, IsNil)
}
func (safs *signAddFindSuite) TestFindOptionalPrimaryKeys(c *C) {
r := asserts.MockOptionalPrimaryKey(asserts.TestOnlyType, "opt1", "o1-defl")
defer r()
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "k1",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a1)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "k2",
"opt1": "A",
}
a2, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a2)
c.Assert(err, IsNil)
a, err := safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k1",
})
c.Assert(err, IsNil)
c.Check(a.HeaderString("primary-key"), Equals, "k1")
c.Check(a.HeaderString("opt1"), Equals, "o1-defl")
a, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k1",
"opt1": "o1-defl",
})
c.Assert(err, IsNil)
c.Check(a.HeaderString("primary-key"), Equals, "k1")
c.Check(a.HeaderString("opt1"), Equals, "o1-defl")
a, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k2",
"opt1": "A",
})
c.Assert(err, IsNil)
c.Check(a.HeaderString("primary-key"), Equals, "k2")
c.Check(a.HeaderString("opt1"), Equals, "A")
_, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k3",
})
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: map[string]string{
"primary-key": "k3",
},
})
_, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k2",
})
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: map[string]string{
"primary-key": "k2",
},
})
_, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k2",
"opt1": "B",
})
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: map[string]string{
"primary-key": "k2",
"opt1": "B",
},
})
_, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "k1",
"opt1": "B",
})
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlyType,
Headers: map[string]string{
"primary-key": "k1",
"opt1": "B",
},
})
}
func (safs *signAddFindSuite) TestWithStackedBackstore(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"primary-key": "one",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a1)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "two",
}
a2, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
bs := asserts.NewMemoryBackstore()
stacked := safs.db.WithStackedBackstore(bs)
err = stacked.Add(a2)
c.Assert(err, IsNil)
_, err = stacked.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "one",
})
c.Check(err, IsNil)
_, err = stacked.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "two",
})
c.Check(err, IsNil)
_, err = safs.db.Find(asserts.TestOnlyType, map[string]string{
"primary-key": "two",
})
c.Check(errors.Is(err, &asserts.NotFoundError{}), Equals, true)
_, err = stacked.Find(asserts.AccountKeyType, map[string]string{
"public-key-sha3-384": safs.signingKeyID,
})
c.Check(err, IsNil)
// stored in backstore
_, err = bs.Get(asserts.TestOnlyType, []string{"two"}, 0)
c.Check(err, IsNil)
}
func (safs *signAddFindSuite) TestWithStackedBackstoreSafety(c *C) {
stacked := safs.db.WithStackedBackstore(asserts.NewMemoryBackstore())
// usual add safety
pubKey0, err := safs.signingDB.PublicKey(safs.signingKeyID)
c.Assert(err, IsNil)
pubKey0Encoded, err := asserts.EncodePublicKey(pubKey0)
c.Assert(err, IsNil)
now := time.Now().UTC()
headers := map[string]interface{}{
"authority-id": "canonical",
"account-id": "canonical",
"public-key-sha3-384": safs.signingKeyID,
"name": "default",
"since": now.Format(time.RFC3339),
"until": now.AddDate(1, 0, 0).Format(time.RFC3339),
}
tKey, err := safs.signingDB.Sign(asserts.AccountKeyType, headers, []byte(pubKey0Encoded), safs.signingKeyID)
c.Assert(err, IsNil)
err = stacked.Add(tKey)
c.Check(err, ErrorMatches, `cannot add "account-key" assertion with primary key clashing with a trusted assertion: .*`)
// cannot go back to old revisions
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "one",
}
a0, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"primary-key": "one",
"revision": "1",
}
a1, err := safs.signingDB.Sign(asserts.TestOnlyType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(a1)
c.Assert(err, IsNil)
err = stacked.Add(a0)
c.Assert(err, DeepEquals, &asserts.RevisionError{
Used: 0,
Current: 1,
})
}
func (safs *signAddFindSuite) TestFindSequence(c *C) {
headers := map[string]interface{}{
"authority-id": "canonical",
"n": "s1",
"sequence": "1",
}
sq1f0, err := safs.signingDB.Sign(asserts.TestOnlySeqType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"n": "s1",
"sequence": "2",
}
sq2f0, err := safs.signingDB.Sign(asserts.TestOnlySeqType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"format": "1",
"n": "s1",
"sequence": "2",
"revision": "1",
}
sq2f1, err := safs.signingDB.Sign(asserts.TestOnlySeqType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"format": "1",
"n": "s1",
"sequence": "3",
}
sq3f1, err := safs.signingDB.Sign(asserts.TestOnlySeqType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
headers = map[string]interface{}{
"authority-id": "canonical",
"format": "2",
"n": "s1",
"sequence": "3",
"revision": "1",
}
sq3f2, err := safs.signingDB.Sign(asserts.TestOnlySeqType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
for _, a := range []asserts.Assertion{sq1f0, sq2f0, sq2f1, sq3f1} {
err = safs.db.Add(a)
c.Assert(err, IsNil)
}
// stack a backstore, for test completeness, this is an unlikely
// scenario atm
bs := asserts.NewMemoryBackstore()
db := safs.db.WithStackedBackstore(bs)
err = db.Add(sq3f2)
c.Assert(err, IsNil)
seqHeaders := map[string]string{
"n": "s1",
}
tests := []struct {
after int
maxFormat int
sequence int
format int
revision int
}{
{after: 0, maxFormat: 0, sequence: 1, format: 0, revision: 0},
{after: 0, maxFormat: 2, sequence: 1, format: 0, revision: 0},
{after: 1, maxFormat: 0, sequence: 2, format: 0, revision: 0},
{after: 1, maxFormat: 1, sequence: 2, format: 1, revision: 1},
{after: 1, maxFormat: 2, sequence: 2, format: 1, revision: 1},
{after: 2, maxFormat: 0, sequence: -1},
{after: 2, maxFormat: 1, sequence: 3, format: 1, revision: 0},
{after: 2, maxFormat: 2, sequence: 3, format: 2, revision: 1},
{after: 3, maxFormat: 0, sequence: -1},
{after: 3, maxFormat: 2, sequence: -1},
{after: 4, maxFormat: 2, sequence: -1},
{after: -1, maxFormat: 0, sequence: 2, format: 0, revision: 0},
{after: -1, maxFormat: 1, sequence: 3, format: 1, revision: 0},
{after: -1, maxFormat: 2, sequence: 3, format: 2, revision: 1},
}
for _, t := range tests {
a, err := db.FindSequence(asserts.TestOnlySeqType, seqHeaders, t.after, t.maxFormat)
if t.sequence == -1 {
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlySeqType,
Headers: seqHeaders,
})
} else {
c.Assert(err, IsNil)
c.Assert(a.HeaderString("n"), Equals, "s1")
c.Check(a.Sequence(), Equals, t.sequence)
c.Check(a.Format(), Equals, t.format)
c.Check(a.Revision(), Equals, t.revision)
}
}
seqHeaders = map[string]string{
"n": "s2",
}
_, err = db.FindSequence(asserts.TestOnlySeqType, seqHeaders, -1, 2)
c.Check(err, DeepEquals, &asserts.NotFoundError{
Type: asserts.TestOnlySeqType, Headers: seqHeaders,
})
}
func (safs *signAddFindSuite) TestCheckConstraints(c *C) {
headers := map[string]interface{}{
"type": "account",
"authority-id": "canonical",
"account-id": "my-brand",
"display-name": "My Brand",
"validation": "verified",
"timestamp": time.Now().Format(time.RFC3339),
}
acct, err := safs.signingDB.Sign(asserts.AccountType, headers, nil, safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(acct)
c.Check(err, IsNil)
pubKey1 := testPrivKey1.PublicKey()
pubKey1Encoded, err := asserts.EncodePublicKey(pubKey1)
c.Assert(err, IsNil)
now := time.Now().UTC()
headers = map[string]interface{}{
"authority-id": "canonical",
"format": "1",
"account-id": "my-brand",
"public-key-sha3-384": pubKey1.ID(),
"name": "default",
"since": now.Format(time.RFC3339),
"until": now.AddDate(1, 0, 0).Format(time.RFC3339),
"constraints": []interface{}{
map[string]interface{}{
"headers": map[string]interface{}{
"type": "model",
"model": "foo-.*",
},
},
},
}
accKey, err := safs.signingDB.Sign(asserts.AccountKeyType, headers, []byte(pubKey1Encoded), safs.signingKeyID)
c.Assert(err, IsNil)
err = safs.db.Add(accKey)
c.Check(err, IsNil)
headers = map[string]interface{}{
"type": "model",
"authority-id": "my-brand",
"brand-id": "my-brand",
"series": "16",
"model": "foo-200",
"classic": "true",
"timestamp": now.Format(time.RFC3339),
}
mfoo, err := asserts.AssembleAndSignInTest(asserts.ModelType, headers, nil, testPrivKey1)
c.Assert(err, IsNil)
err = safs.db.Add(mfoo)
c.Check(err, IsNil)
headers = map[string]interface{}{
"type": "model",
"authority-id": "my-brand",
"brand-id": "my-brand",
"series": "16",
"model": "goo-200",
"classic": "true",
"timestamp": now.Format(time.RFC3339),
}
mnotfoo, err := asserts.AssembleAndSignInTest(asserts.ModelType, headers, nil, testPrivKey1)
c.Assert(err, IsNil)
err = safs.db.Add(mnotfoo)
c.Check(err, ErrorMatches, `assertion does not match signing constraints for public key ".*" from "my-brand"`)
}
type revisionErrorSuite struct{}
func (res *revisionErrorSuite) TestErrorText(c *C) {
tests := []struct {
err error
expected string
}{
// Invalid revisions.
{&asserts.RevisionError{Used: -1}, "assertion revision is unknown"},
{&asserts.RevisionError{Used: -100}, "assertion revision is unknown"},
{&asserts.RevisionError{Current: -1}, "assertion revision is unknown"},
{&asserts.RevisionError{Current: -100}, "assertion revision is unknown"},
{&asserts.RevisionError{Used: -1, Current: -1}, "assertion revision is unknown"},
// Used == Current.
{&asserts.RevisionError{}, "revision 0 is already the current revision"},
{&asserts.RevisionError{Used: 100, Current: 100}, "revision 100 is already the current revision"},
// Used < Current.
{&asserts.RevisionError{Used: 1, Current: 2}, "revision 1 is older than current revision 2"},
{&asserts.RevisionError{Used: 2, Current: 100}, "revision 2 is older than current revision 100"},
// Used > Current.
{&asserts.RevisionError{Current: 1, Used: 2}, "revision 2 is more recent than current revision 1"},
{&asserts.RevisionError{Current: 2, Used: 100}, "revision 100 is more recent than current revision 2"},
}
for _, test := range tests {
c.Check(test.err, ErrorMatches, test.expected)
}
}
type isUnacceptedUpdateSuite struct{}
func (s *isUnacceptedUpdateSuite) TestIsUnacceptedUpdate(c *C) {
tests := []struct {
err error
keptCurrent bool
}{
{&asserts.UnsupportedFormatError{}, false},
{&asserts.UnsupportedFormatError{Update: true}, true},
{&asserts.RevisionError{Used: 1, Current: 1}, true},
{&asserts.RevisionError{Used: 1, Current: 5}, true},
{&asserts.RevisionError{Used: 3, Current: 1}, false},
{errors.New("other error"), false},
{&asserts.NotFoundError{Type: asserts.TestOnlyType}, false},
}
for _, t := range tests {
c.Check(asserts.IsUnaccceptedUpdate(t.err), Equals, t.keptCurrent, Commentf("%v", t.err))
}
}