github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/asserts/assertstest/assertstest.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  // Package assertstest provides helpers for testing code that involves assertions.
    21  package assertstest
    22  
    23  import (
    24  	"bytes"
    25  	"crypto/rand"
    26  	"crypto/rsa"
    27  	"encoding/base64"
    28  	"fmt"
    29  	"io"
    30  	"os/exec"
    31  	"strings"
    32  	"time"
    33  
    34  	"golang.org/x/crypto/openpgp/armor"
    35  	"golang.org/x/crypto/openpgp/packet"
    36  
    37  	"github.com/snapcore/snapd/asserts"
    38  	"github.com/snapcore/snapd/randutil"
    39  )
    40  
    41  // GenerateKey generates a private/public key pair of the given bits. It panics on error.
    42  func GenerateKey(bits int) (asserts.PrivateKey, *rsa.PrivateKey) {
    43  	priv, err := rsa.GenerateKey(rand.Reader, bits)
    44  	if err != nil {
    45  		panic(fmt.Errorf("failed to create private key: %v", err))
    46  	}
    47  	return asserts.RSAPrivateKey(priv), priv
    48  }
    49  
    50  // ReadPrivKey reads a PGP private key (either armored or simply base64 encoded). It panics on error.
    51  func ReadPrivKey(pk string) (asserts.PrivateKey, *rsa.PrivateKey) {
    52  	rd := bytes.NewReader([]byte(pk))
    53  	blk, err := armor.Decode(rd)
    54  	var body io.Reader
    55  	if err == nil {
    56  		body = blk.Body
    57  	} else {
    58  		rd.Seek(0, 0)
    59  		// try unarmored
    60  		body = base64.NewDecoder(base64.StdEncoding, rd)
    61  	}
    62  	pkt, err := packet.Read(body)
    63  	if err != nil {
    64  		panic(err)
    65  	}
    66  
    67  	pkPkt := pkt.(*packet.PrivateKey)
    68  	rsaPrivKey, ok := pkPkt.PrivateKey.(*rsa.PrivateKey)
    69  	if !ok {
    70  		panic("not a RSA key")
    71  	}
    72  
    73  	return asserts.RSAPrivateKey(rsaPrivKey), rsaPrivKey
    74  }
    75  
    76  // A sample developer key.
    77  // See systestkeys for a prebuilt set of trusted keys and assertions.
    78  const (
    79  	DevKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
    80  Version: GnuPG v1
    81  
    82  lQcYBFaFwYABEAC0kYiC4rsWFLJHEv/qO93LTMCAYKMLXFU0XN4XvqnkbwFc0QQd
    83  lQcr7PwavYmKdWum+EmGWV/k5vZ0gwfZhBsL2MTWSNvO+5q5AYOqTq01CbSLcoN4
    84  cJI+BU348Vc/AoiIuuHro+gALs59HWsVSAKq7SNyHQfo257TKe8Q+Jjh095eruYJ
    85  2kOvlAgAzjUv7eGDQ53O87wcwgZlCl0XqM/t+SRUxE5i8dQ4nySSekoTsWJo02kf
    86  uMrWo3E5iEt6KKhfQtit2ZO91NYetIplzzZmaUOOkpziFTFW1NcwDKzDsLMh1EQ+
    87  ib+8mSWcou9m35aTkAQXlXlgqe5Pelj5+NUxnnoa1MR478Sv+guT+fbFQrl8PkMD
    88  Jb/3PTKDPBNtjki5ZfIN9id4vidfBY4SCDftnj7yZMf5+1PPZ2XXHUoiUhHbGjST
    89  F/23wr6OWvXe/AXX5BF4wJJTJxSxnYR6nleGMj4sbsbVsxIaqh1lMg5cuQjLr7eI
    90  nxn994geUnQQsEPIVuVjLThJ/0sjXjy8kyxh6eieShZ6NZ0yLyIJRN5pnJ0ckRQF
    91  T9Fs0UuMJZro0hR71t9mAuI45mSmznj78AvTvyuL+0aOj/lQa97NKbCsShYnKqsm
    92  3Yzr03ahUMslwd6jZtRg+0ULYp9vaN7nwmsn6WWJ92CsCzFucdeJfJWKZQARAQAB
    93  AA/9GSda3mzKRhms+hSt/MnJLFxtRpTvsZHztp8nOySO0ykZhf4B9kL/5EEXn3v+
    94  0IBp9jEJQQNrRd5cv79PFSB/igdw6C7vG+bV12bcGhnqrARFl9Vkdh8saCJiCcdI
    95  8ZifP3jVJvfGxlu+3RP/ik/lOz1cnjVoGCqb9euWB4Wx+meCxyrTFdVHb4qOENqo
    96  8xvOufPt5Fn0vwbSUDoA3N5h1NNLmdlc2BC7EQYuWI9biWHBBTxKHSanbv4GtE6F
    97  wScvyVFtEM7J83xWNaHN07/pYpvQUuienSn5nRB6R5HEcWBIm/JPbWzP/mxRHoBe
    98  HDUSa0z5HPXwGiSh84VmJrBgtlQosxk3jOHjynlU194S2cVLcSrFSf4hp6WZVAa1
    99  Nlkv6v62eU3nDxabkF92Lcv40s1cBqYCvhOtMzgoXL0TuaVJIdUwbJRHoBi8Bh5f
   100  bNYJqyMqJNHcT9ylAWw130ljPTtqzbTMRtitxnJPbf60hpsJ4jcp2bJP9pg9XyuR
   101  ZyIKtLfGQfxvFLsXzNssnVv7ZenK5AgUFTMvmyKCQQeYluheKc0KtRKSYE3iaVAs
   102  Efw5Pd0GD82UGef9WahtnemodTlD3nkzlD50XBsd8xdNBQ7N2TFsP5Ldvfp1Wf2F
   103  qg+rTaS0OID9vDQuekOcDI8lA9E4FYlIkJ6AqIb7hD5hlBMIAMRVXLlPLgzmrY5k
   104  pIPMbgyN0wm3f4qAWIaMtg79x9gTylsGF7lkqNLqFDFYfoUHb+iXINYc51kHV7Ka
   105  JifHhdy8TaBTBrIrsFLJpv06lRex/fdyvswev3W1g3wRJ86eTCqwr1DjB+q2kYX8
   106  u1qDPFRzK4WF+wOF/FwCBLDpESmHSapXuzL5i6pJfOCFIJqT/Q/yp9tyTcxs82tu
   107  kSlNKoXrZi4xHsDpPBuNjMl3eIz3ogesUG60MMa6xovVGV3ICJcwYwycvvQcjuxS
   108  XtJlHK+/G3kB87BXzNCMyUGfDNy7mcTrXAXoUH8nCu4ipyaT/jEyvi95w/7RJcFU
   109  qs6taH8IAOtxqnBZGDQuYPF0ZmZQ7e1/FXq/LBQryYZgNtyLUdR7ycXGCTXlEIGw
   110  X3r7Qf4+a3MlriP5thKxci+npcIj4e31aS6cpO2IcGJzmNOHzLCl3b4XmO/APBSA
   111  FZpQE3k+lg45tn/vgcPMKKLAAv6TbpVVgLrFXGtX3Gtkd66fPPOcINXi6+MqXfp5
   112  rl8OJIq5O5ygbbglwcqmeI29RLZ58b0ktCa5ZZNzeSV+T5jHwRNnWm0EJgjx8Lwn
   113  LEWFS/vQjGwaoRJi06jpmM+66sefyTQ3qvyzQLBqlenZf16GGz28cOSjNJ9FDth1
   114  iKnyk7d8nqhmbSHeW08QUwTF6NGp+xsIAJDa3ouxSjTEB1P7z1VLJp6nSglBQ74n
   115  XAprk2WpggLNrWwyFJsgFh07LxShb/O3t1TanU+Ld/ryyWHztTxag2auAHuVQ4+S
   116  EkjKqkUaSOQML9a9AvZ2rQr84f5ohc/vCOQhpNVLSyw55EW6WhnntNWVwgZxMiAj
   117  oREMJMrBb6LL9b7kHtfYqLNfe3fkUx+tuTsm96Wi1cdkh0qyut0+J+eieZVn7kiM
   118  UP5IZuz9TSjDOrA5qu5NGlbXNaN0cdJ2UUSNekQiysqDpdf00wIwr1XqH+KLUjZv
   119  pO5Mub6NdnVXJRZunpbNXbuxj49NXnZEEi71WQm9KLR8KQ1oQ+RlnHx/XLQHICh0
   120  ZXN0KYkCOAQTAQIAIgUCVoXBgAIbLwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA
   121  CgkQSkI9KKrqS0/YEhAAgJALHrx4kFRcgDJE+khK/CdoaLvi0N40eAE+RzQgcxhh
   122  S4Aeks8n1cL6oAwDfCL+ohyWvPzF2DzsBkEIC3l+JS2tn0JJ+qexY+qhdGkEze/o
   123  SIvH9sfR5LJuKb3OAt2mQlY+sxjlkzU9rTGKsVZxgApNM4665dlagF9tipMQTHnd
   124  eFZRlvNTWKkweW0jbJCpRKlQnjEZ6S/wlPBgH69Ek3bnDcgp6eaAU92Ke9Fa2wMV
   125  LBMaXpUIvddKFjoGtvShDOpcQRE99Z8tK4YSAOg+zbSUeD7HGH00EQERItoJsAv1
   126  7Du8+jcKSeOhz7PPxOA7mEnYNdoMcrg/2AP+FVI6zGYcKN7Hq3C6Z+bQ4X1VkKmv
   127  NCFomU2AyPVxpJRYw7/EkoRWp/iq6sEb7bsmhmDEiz1MiroAV+efmWyUjxueSzrW
   128  24OxHTWi2GuHBF+FKUD3UxfaWMjH+tuWYPIHzYsT+TfsN0vAEFyhRi8Ncelu1RV4
   129  x2O3wmjxoaX/2FmyuU5WhcVkcpRFgceyf1/86NP9gT5MKbWtJC85YYpxibnvPdGd
   130  +sqtEEqgX3dSsHT+rkBk7kf3ghDwsLtnliFPOeAaIHGZl754EpK+qPUTnYZK022H
   131  2crhYlApO9+06kBeybSO6joMUR007883I9GELYhzmuEjpVGquJQ3+S5QtW1to0w=
   132  =5Myf
   133  -----END PGP PRIVATE KEY BLOCK-----
   134  `
   135  
   136  	DevKeyID = "EAD4DbLxK_kn0gzNCXOs3kd6DeMU3f-L6BEsSEuJGBqCORR0gXkdDxMbOm11mRFu"
   137  
   138  	DevKeyPGPFingerprint = "966e70f4b9f257a2772f8f354a423d28aaea4b4f"
   139  )
   140  
   141  // GPGImportKey imports the given PGP armored key into the GnuPG setup at homedir. It panics on error.
   142  func GPGImportKey(homedir, armoredKey string) {
   143  	path, err := exec.LookPath("gpg1")
   144  	if err != nil {
   145  		path, err = exec.LookPath("gpg")
   146  	}
   147  	if err != nil {
   148  		panic(err)
   149  	}
   150  	gpg := exec.Command(path, "--homedir", homedir, "-q", "--batch", "--import", "--armor")
   151  	gpg.Stdin = bytes.NewBufferString(armoredKey)
   152  	out, err := gpg.CombinedOutput()
   153  	if err != nil {
   154  		panic(fmt.Errorf("cannot import test key into GPG setup at %q: %v (%q)", homedir, err, out))
   155  	}
   156  }
   157  
   158  // A SignerDB can sign assertions using its key pairs.
   159  type SignerDB interface {
   160  	Sign(assertType *asserts.AssertionType, headers map[string]interface{}, body []byte, keyID string) (asserts.Assertion, error)
   161  }
   162  
   163  // NewAccount creates an account assertion for username, it fills in values for other missing headers as needed. It panics on error.
   164  func NewAccount(db SignerDB, username string, otherHeaders map[string]interface{}, keyID string) *asserts.Account {
   165  	if otherHeaders == nil {
   166  		otherHeaders = make(map[string]interface{})
   167  	}
   168  	otherHeaders["username"] = username
   169  	if otherHeaders["account-id"] == nil {
   170  		otherHeaders["account-id"] = randutil.RandomString(32)
   171  	}
   172  	if otherHeaders["display-name"] == nil {
   173  		otherHeaders["display-name"] = strings.ToTitle(username[:1]) + username[1:]
   174  	}
   175  	if otherHeaders["validation"] == nil {
   176  		otherHeaders["validation"] = "unproven"
   177  	}
   178  	if otherHeaders["timestamp"] == nil {
   179  		otherHeaders["timestamp"] = time.Now().Format(time.RFC3339)
   180  	}
   181  	a, err := db.Sign(asserts.AccountType, otherHeaders, nil, keyID)
   182  	if err != nil {
   183  		panic(err)
   184  	}
   185  	return a.(*asserts.Account)
   186  }
   187  
   188  // NewAccountKey creates an account-key assertion for the account, it fills in values for missing headers as needed. In panics on error.
   189  func NewAccountKey(db SignerDB, acct *asserts.Account, otherHeaders map[string]interface{}, pubKey asserts.PublicKey, keyID string) *asserts.AccountKey {
   190  	if otherHeaders == nil {
   191  		otherHeaders = make(map[string]interface{})
   192  	}
   193  	otherHeaders["account-id"] = acct.AccountID()
   194  	otherHeaders["public-key-sha3-384"] = pubKey.ID()
   195  	if otherHeaders["name"] == nil {
   196  		otherHeaders["name"] = "default"
   197  	}
   198  	if otherHeaders["since"] == nil {
   199  		otherHeaders["since"] = time.Now().Format(time.RFC3339)
   200  	}
   201  	encodedPubKey, err := asserts.EncodePublicKey(pubKey)
   202  	if err != nil {
   203  		panic(err)
   204  	}
   205  	a, err := db.Sign(asserts.AccountKeyType, otherHeaders, encodedPubKey, keyID)
   206  	if err != nil {
   207  		panic(err)
   208  	}
   209  	return a.(*asserts.AccountKey)
   210  }
   211  
   212  // SigningDB embeds a signing assertion database with a default private key and assigned authority id.
   213  // Sign will use the assigned authority id.
   214  // "" can be passed for keyID to Sign and PublicKey to use the default key.
   215  type SigningDB struct {
   216  	AuthorityID string
   217  	KeyID       string
   218  
   219  	*asserts.Database
   220  }
   221  
   222  // NewSigningDB creates a test signing assertion db with the given defaults. It panics on error.
   223  func NewSigningDB(authorityID string, privKey asserts.PrivateKey) *SigningDB {
   224  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{})
   225  	if err != nil {
   226  		panic(err)
   227  	}
   228  	err = db.ImportKey(privKey)
   229  	if err != nil {
   230  		panic(err)
   231  	}
   232  	return &SigningDB{
   233  		AuthorityID: authorityID,
   234  		KeyID:       privKey.PublicKey().ID(),
   235  		Database:    db,
   236  	}
   237  }
   238  
   239  func (db *SigningDB) Sign(assertType *asserts.AssertionType, headers map[string]interface{}, body []byte, keyID string) (asserts.Assertion, error) {
   240  	if _, ok := headers["authority-id"]; !ok {
   241  		// copy before modifying
   242  		headers2 := make(map[string]interface{}, len(headers)+1)
   243  		for h, v := range headers {
   244  			headers2[h] = v
   245  		}
   246  		headers = headers2
   247  		headers["authority-id"] = db.AuthorityID
   248  	}
   249  	if keyID == "" {
   250  		keyID = db.KeyID
   251  	}
   252  	return db.Database.Sign(assertType, headers, body, keyID)
   253  }
   254  
   255  func (db *SigningDB) PublicKey(keyID string) (asserts.PublicKey, error) {
   256  	if keyID == "" {
   257  		keyID = db.KeyID
   258  	}
   259  	return db.Database.PublicKey(keyID)
   260  }
   261  
   262  // StoreStack realises a store-like set of founding trusted assertions and signing setup.
   263  type StoreStack struct {
   264  	// Trusted authority assertions.
   265  	TrustedAccount *asserts.Account
   266  	TrustedKey     *asserts.AccountKey
   267  	Trusted        []asserts.Assertion
   268  
   269  	// Generic authority assertions.
   270  	GenericAccount      *asserts.Account
   271  	GenericKey          *asserts.AccountKey
   272  	GenericModelsKey    *asserts.AccountKey
   273  	Generic             []asserts.Assertion
   274  	GenericClassicModel *asserts.Model
   275  
   276  	// Signing assertion db that signs with the root private key.
   277  	RootSigning *SigningDB
   278  
   279  	// The store-like signing functionality that signs with a store key, setup to also store assertions if desired. It stores a default account-key for the store private key, see also the StoreStack.Key method.
   280  	*SigningDB
   281  }
   282  
   283  // StoreKeys holds a set of store private keys.
   284  type StoreKeys struct {
   285  	Root          asserts.PrivateKey
   286  	Store         asserts.PrivateKey
   287  	Generic       asserts.PrivateKey
   288  	GenericModels asserts.PrivateKey
   289  }
   290  
   291  var (
   292  	rootPrivKey, _          = GenerateKey(1024)
   293  	storePrivKey, _         = GenerateKey(752)
   294  	genericPrivKey, _       = GenerateKey(752)
   295  	genericModelsPrivKey, _ = GenerateKey(752)
   296  
   297  	pregenKeys = StoreKeys{
   298  		Root:          rootPrivKey,
   299  		Store:         storePrivKey,
   300  		Generic:       genericPrivKey,
   301  		GenericModels: genericModelsPrivKey,
   302  	}
   303  )
   304  
   305  // NewStoreStack creates a new store assertion stack. It panics on error.
   306  // Optional keys specify private keys to use for the various roles.
   307  func NewStoreStack(authorityID string, keys *StoreKeys) *StoreStack {
   308  	if keys == nil {
   309  		keys = &pregenKeys
   310  	}
   311  
   312  	rootSigning := NewSigningDB(authorityID, keys.Root)
   313  	ts := time.Now().Format(time.RFC3339)
   314  	trustedAcct := NewAccount(rootSigning, authorityID, map[string]interface{}{
   315  		"account-id": authorityID,
   316  		"validation": "verified",
   317  		"timestamp":  ts,
   318  	}, "")
   319  	trustedKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{
   320  		"name":  "root",
   321  		"since": ts,
   322  	}, keys.Root.PublicKey(), "")
   323  	trusted := []asserts.Assertion{trustedAcct, trustedKey}
   324  
   325  	genericAcct := NewAccount(rootSigning, "generic", map[string]interface{}{
   326  		"account-id": "generic",
   327  		"validation": "verified",
   328  		"timestamp":  ts,
   329  	}, "")
   330  
   331  	err := rootSigning.ImportKey(keys.GenericModels)
   332  	if err != nil {
   333  		panic(err)
   334  	}
   335  	genericModelsKey := NewAccountKey(rootSigning, genericAcct, map[string]interface{}{
   336  		"name":  "models",
   337  		"since": ts,
   338  	}, keys.GenericModels.PublicKey(), "")
   339  	generic := []asserts.Assertion{genericAcct, genericModelsKey}
   340  
   341  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   342  		Backstore:       asserts.NewMemoryBackstore(),
   343  		Trusted:         trusted,
   344  		OtherPredefined: generic,
   345  	})
   346  	if err != nil {
   347  		panic(err)
   348  	}
   349  	err = db.ImportKey(keys.Store)
   350  	if err != nil {
   351  		panic(err)
   352  	}
   353  	storeKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{
   354  		"name": "store",
   355  	}, keys.Store.PublicKey(), "")
   356  	err = db.Add(storeKey)
   357  	if err != nil {
   358  		panic(err)
   359  	}
   360  
   361  	err = db.ImportKey(keys.Generic)
   362  	if err != nil {
   363  		panic(err)
   364  	}
   365  	genericKey := NewAccountKey(rootSigning, genericAcct, map[string]interface{}{
   366  		"name":  "serials",
   367  		"since": ts,
   368  	}, keys.Generic.PublicKey(), "")
   369  	err = db.Add(genericKey)
   370  	if err != nil {
   371  		panic(err)
   372  	}
   373  
   374  	a, err := rootSigning.Sign(asserts.ModelType, map[string]interface{}{
   375  		"authority-id": "generic",
   376  		"series":       "16",
   377  		"brand-id":     "generic",
   378  		"model":        "generic-classic",
   379  		"classic":      "true",
   380  		"timestamp":    ts,
   381  	}, nil, genericModelsKey.PublicKeyID())
   382  	if err != nil {
   383  		panic(err)
   384  	}
   385  	genericClassicMod := a.(*asserts.Model)
   386  
   387  	return &StoreStack{
   388  		TrustedAccount: trustedAcct,
   389  		TrustedKey:     trustedKey,
   390  		Trusted:        trusted,
   391  
   392  		GenericAccount:      genericAcct,
   393  		GenericKey:          genericKey,
   394  		GenericModelsKey:    genericModelsKey,
   395  		Generic:             generic,
   396  		GenericClassicModel: genericClassicMod,
   397  
   398  		RootSigning: rootSigning,
   399  
   400  		SigningDB: &SigningDB{
   401  			AuthorityID: authorityID,
   402  			KeyID:       storeKey.PublicKeyID(),
   403  			Database:    db,
   404  		},
   405  	}
   406  }
   407  
   408  // StoreAccountKey retrieves one of the account-key assertions for the signing keys of the simulated store signing database.
   409  // "" for keyID means the default one. It panics on error.
   410  func (ss *StoreStack) StoreAccountKey(keyID string) *asserts.AccountKey {
   411  	if keyID == "" {
   412  		keyID = ss.KeyID
   413  	}
   414  	key, err := ss.Find(asserts.AccountKeyType, map[string]string{
   415  		"account-id":          ss.AuthorityID,
   416  		"public-key-sha3-384": keyID,
   417  	})
   418  	if asserts.IsNotFound(err) {
   419  		return nil
   420  	}
   421  	if err != nil {
   422  		panic(err)
   423  	}
   424  	return key.(*asserts.AccountKey)
   425  }
   426  
   427  // MockBuiltinBaseDeclaration mocks the builtin base-declaration exposed by asserts.BuiltinBaseDeclaration.
   428  func MockBuiltinBaseDeclaration(headers []byte) (restore func()) {
   429  	var prevHeaders []byte
   430  	decl := asserts.BuiltinBaseDeclaration()
   431  	if decl != nil {
   432  		prevHeaders, _ = decl.Signature()
   433  	}
   434  
   435  	err := asserts.InitBuiltinBaseDeclaration(headers)
   436  	if err != nil {
   437  		panic(err)
   438  	}
   439  
   440  	return func() {
   441  		err := asserts.InitBuiltinBaseDeclaration(prevHeaders)
   442  		if err != nil {
   443  			panic(err)
   444  		}
   445  	}
   446  }
   447  
   448  // FakeAssertionWithBody builds a fake assertion with the given body
   449  // and layered headers. A fake assertion cannot be verified or added
   450  // to a database or properly encoded. It can still be useful for unit
   451  // tests but shouldn't be used in integration tests.
   452  func FakeAssertionWithBody(body []byte, headerLayers ...map[string]interface{}) asserts.Assertion {
   453  	headers := map[string]interface{}{
   454  		"sign-key-sha3-384": "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
   455  	}
   456  	for _, h := range headerLayers {
   457  		for k, v := range h {
   458  			headers[k] = v
   459  		}
   460  	}
   461  
   462  	_, hasTimestamp := headers["timestamp"]
   463  	_, hasSince := headers["since"]
   464  	if !(hasTimestamp || hasSince) {
   465  		headers["timestamp"] = time.Now().Format(time.RFC3339)
   466  	}
   467  
   468  	a, err := asserts.Assemble(headers, body, nil, []byte("AXNpZw=="))
   469  	if err != nil {
   470  		panic(fmt.Sprintf("cannot build fake assertion: %v", err))
   471  	}
   472  	return a
   473  }
   474  
   475  // FakeAssertion builds a fake assertion with given layered headers
   476  // and an empty body. A fake assertion cannot be verified or added to
   477  // a database or properly encoded. It can still be useful for unit
   478  // tests but shouldn't be used in integration tests.
   479  func FakeAssertion(headerLayers ...map[string]interface{}) asserts.Assertion {
   480  	return FakeAssertionWithBody(nil, headerLayers...)
   481  }
   482  
   483  type accuDB interface {
   484  	Add(asserts.Assertion) error
   485  }
   486  
   487  // AddMany conveniently adds the given assertions to the db.
   488  // It is idempotent but otherwise panics on error.
   489  func AddMany(db accuDB, assertions ...asserts.Assertion) {
   490  	for _, a := range assertions {
   491  		err := db.Add(a)
   492  		if _, ok := err.(*asserts.RevisionError); !ok {
   493  			if err != nil {
   494  				panic(fmt.Sprintf("cannot add test assertions: %v", err))
   495  			}
   496  		}
   497  	}
   498  }
   499  
   500  // SigningAccounts manages a set of brand or user accounts,
   501  // with their keys that can sign models etc.
   502  type SigningAccounts struct {
   503  	store *StoreStack
   504  
   505  	signing map[string]*SigningDB
   506  
   507  	accts    map[string]*asserts.Account
   508  	acctKeys map[string]*asserts.AccountKey
   509  }
   510  
   511  // NewSigningAccounts creates a new SigningAccounts instance.
   512  func NewSigningAccounts(store *StoreStack) *SigningAccounts {
   513  	return &SigningAccounts{
   514  		store:    store,
   515  		signing:  make(map[string]*SigningDB),
   516  		accts:    make(map[string]*asserts.Account),
   517  		acctKeys: make(map[string]*asserts.AccountKey),
   518  	}
   519  }
   520  
   521  func (sa *SigningAccounts) Register(accountID string, brandPrivKey asserts.PrivateKey, extra map[string]interface{}) *SigningDB {
   522  	brandSigning := NewSigningDB(accountID, brandPrivKey)
   523  	sa.signing[accountID] = brandSigning
   524  
   525  	acctHeaders := map[string]interface{}{
   526  		"account-id": accountID,
   527  	}
   528  	for k, v := range extra {
   529  		acctHeaders[k] = v
   530  	}
   531  
   532  	brandAcct := NewAccount(sa.store, accountID, acctHeaders, "")
   533  	sa.accts[accountID] = brandAcct
   534  
   535  	brandPubKey, err := brandSigning.PublicKey("")
   536  	if err != nil {
   537  		panic(err)
   538  	}
   539  	brandAcctKey := NewAccountKey(sa.store, brandAcct, nil, brandPubKey, "")
   540  	sa.acctKeys[accountID] = brandAcctKey
   541  
   542  	return brandSigning
   543  }
   544  
   545  func (sa *SigningAccounts) Account(accountID string) *asserts.Account {
   546  	if acct := sa.accts[accountID]; acct != nil {
   547  		return acct
   548  	}
   549  	panic(fmt.Sprintf("unknown test account-id: %s", accountID))
   550  }
   551  
   552  func (sa *SigningAccounts) AccountKey(accountID string) *asserts.AccountKey {
   553  	if acctKey := sa.acctKeys[accountID]; acctKey != nil {
   554  		return acctKey
   555  	}
   556  	panic(fmt.Sprintf("unknown test account-id: %s", accountID))
   557  }
   558  
   559  func (sa *SigningAccounts) PublicKey(accountID string) asserts.PublicKey {
   560  	pubKey, err := sa.Signing(accountID).PublicKey("")
   561  	if err != nil {
   562  		panic(err)
   563  	}
   564  	return pubKey
   565  }
   566  
   567  func (sa *SigningAccounts) Signing(accountID string) *SigningDB {
   568  	// convenience
   569  	if accountID == sa.store.RootSigning.AuthorityID {
   570  		return sa.store.RootSigning
   571  	}
   572  	if signer := sa.signing[accountID]; signer != nil {
   573  		return signer
   574  	}
   575  	panic(fmt.Sprintf("unknown test account-id: %s", accountID))
   576  }
   577  
   578  // Model creates a new model for accountID. accountID can also be the account-id of the underlying store stack.
   579  func (sa *SigningAccounts) Model(accountID, model string, extras ...map[string]interface{}) *asserts.Model {
   580  	headers := map[string]interface{}{
   581  		"series":    "16",
   582  		"brand-id":  accountID,
   583  		"model":     model,
   584  		"timestamp": time.Now().Format(time.RFC3339),
   585  	}
   586  	for _, extra := range extras {
   587  		for k, v := range extra {
   588  			headers[k] = v
   589  		}
   590  	}
   591  
   592  	signer := sa.Signing(accountID)
   593  
   594  	modelAs, err := signer.Sign(asserts.ModelType, headers, nil, "")
   595  	if err != nil {
   596  		panic(err)
   597  	}
   598  	return modelAs.(*asserts.Model)
   599  }
   600  
   601  // AccountsAndKeys returns the account and account-key for each given
   602  // accountID in that order.
   603  func (sa *SigningAccounts) AccountsAndKeys(accountIDs ...string) []asserts.Assertion {
   604  	res := make([]asserts.Assertion, 0, 2*len(accountIDs))
   605  	for _, accountID := range accountIDs {
   606  		res = append(res, sa.Account(accountID))
   607  		res = append(res, sa.AccountKey(accountID))
   608  	}
   609  	return res
   610  }