github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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
   164  // other missing headers as needed. It panics on error.
   165  func NewAccount(db SignerDB, username string, otherHeaders map[string]interface{}, keyID string) *asserts.Account {
   166  	if otherHeaders == nil {
   167  		otherHeaders = make(map[string]interface{})
   168  	}
   169  	otherHeaders["username"] = username
   170  	if otherHeaders["account-id"] == nil {
   171  		otherHeaders["account-id"] = randutil.RandomString(32)
   172  	}
   173  	if otherHeaders["display-name"] == nil {
   174  		otherHeaders["display-name"] = strings.ToTitle(username[:1]) + username[1:]
   175  	}
   176  	if otherHeaders["validation"] == nil {
   177  		otherHeaders["validation"] = "unproven"
   178  	}
   179  	if otherHeaders["timestamp"] == nil {
   180  		otherHeaders["timestamp"] = time.Now().Format(time.RFC3339)
   181  	}
   182  	a, err := db.Sign(asserts.AccountType, otherHeaders, nil, keyID)
   183  	if err != nil {
   184  		panic(err)
   185  	}
   186  	return a.(*asserts.Account)
   187  }
   188  
   189  // NewAccountKey creates an account-key assertion for the account, it fills in
   190  // values for missing headers as needed. In panics on error.
   191  func NewAccountKey(db SignerDB, acct *asserts.Account, otherHeaders map[string]interface{}, pubKey asserts.PublicKey, keyID string) *asserts.AccountKey {
   192  	if otherHeaders == nil {
   193  		otherHeaders = make(map[string]interface{})
   194  	}
   195  	otherHeaders["account-id"] = acct.AccountID()
   196  	otherHeaders["public-key-sha3-384"] = pubKey.ID()
   197  	if otherHeaders["name"] == nil {
   198  		otherHeaders["name"] = "default"
   199  	}
   200  	if otherHeaders["since"] == nil {
   201  		otherHeaders["since"] = time.Now().Format(time.RFC3339)
   202  	}
   203  	encodedPubKey, err := asserts.EncodePublicKey(pubKey)
   204  	if err != nil {
   205  		panic(err)
   206  	}
   207  	a, err := db.Sign(asserts.AccountKeyType, otherHeaders, encodedPubKey, keyID)
   208  	if err != nil {
   209  		panic(err)
   210  	}
   211  	return a.(*asserts.AccountKey)
   212  }
   213  
   214  // SigningDB embeds a signing assertion database with a default private key and assigned authority id.
   215  // Sign will use the assigned authority id.
   216  // "" can be passed for keyID to Sign and PublicKey to use the default key.
   217  type SigningDB struct {
   218  	AuthorityID string
   219  	KeyID       string
   220  
   221  	*asserts.Database
   222  }
   223  
   224  // NewSigningDB creates a test signing assertion db with the given defaults. It panics on error.
   225  func NewSigningDB(authorityID string, privKey asserts.PrivateKey) *SigningDB {
   226  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{})
   227  	if err != nil {
   228  		panic(err)
   229  	}
   230  	err = db.ImportKey(privKey)
   231  	if err != nil {
   232  		panic(err)
   233  	}
   234  	return &SigningDB{
   235  		AuthorityID: authorityID,
   236  		KeyID:       privKey.PublicKey().ID(),
   237  		Database:    db,
   238  	}
   239  }
   240  
   241  func (db *SigningDB) Sign(assertType *asserts.AssertionType, headers map[string]interface{}, body []byte, keyID string) (asserts.Assertion, error) {
   242  	if _, ok := headers["authority-id"]; !ok {
   243  		// copy before modifying
   244  		headers2 := make(map[string]interface{}, len(headers)+1)
   245  		for h, v := range headers {
   246  			headers2[h] = v
   247  		}
   248  		headers = headers2
   249  		headers["authority-id"] = db.AuthorityID
   250  	}
   251  	if keyID == "" {
   252  		keyID = db.KeyID
   253  	}
   254  	return db.Database.Sign(assertType, headers, body, keyID)
   255  }
   256  
   257  func (db *SigningDB) PublicKey(keyID string) (asserts.PublicKey, error) {
   258  	if keyID == "" {
   259  		keyID = db.KeyID
   260  	}
   261  	return db.Database.PublicKey(keyID)
   262  }
   263  
   264  // StoreStack realises a store-like set of founding trusted assertions and signing setup.
   265  type StoreStack struct {
   266  	// Trusted authority assertions.
   267  	TrustedAccount *asserts.Account
   268  	TrustedKey     *asserts.AccountKey
   269  	Trusted        []asserts.Assertion
   270  
   271  	// Generic authority assertions.
   272  	GenericAccount      *asserts.Account
   273  	GenericKey          *asserts.AccountKey
   274  	GenericModelsKey    *asserts.AccountKey
   275  	Generic             []asserts.Assertion
   276  	GenericClassicModel *asserts.Model
   277  
   278  	// Signing assertion db that signs with the root private key.
   279  	RootSigning *SigningDB
   280  
   281  	// 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.
   282  	*SigningDB
   283  }
   284  
   285  // StoreKeys holds a set of store private keys.
   286  type StoreKeys struct {
   287  	Root          asserts.PrivateKey
   288  	Store         asserts.PrivateKey
   289  	Generic       asserts.PrivateKey
   290  	GenericModels asserts.PrivateKey
   291  }
   292  
   293  var (
   294  	rootPrivKey, _          = GenerateKey(1024)
   295  	storePrivKey, _         = GenerateKey(752)
   296  	genericPrivKey, _       = GenerateKey(752)
   297  	genericModelsPrivKey, _ = GenerateKey(752)
   298  
   299  	pregenKeys = StoreKeys{
   300  		Root:          rootPrivKey,
   301  		Store:         storePrivKey,
   302  		Generic:       genericPrivKey,
   303  		GenericModels: genericModelsPrivKey,
   304  	}
   305  )
   306  
   307  // NewStoreStack creates a new store assertion stack. It panics on error.
   308  // Optional keys specify private keys to use for the various roles.
   309  func NewStoreStack(authorityID string, keys *StoreKeys) *StoreStack {
   310  	if keys == nil {
   311  		keys = &pregenKeys
   312  	}
   313  
   314  	rootSigning := NewSigningDB(authorityID, keys.Root)
   315  	ts := time.Now().Format(time.RFC3339)
   316  	trustedAcct := NewAccount(rootSigning, authorityID, map[string]interface{}{
   317  		"account-id": authorityID,
   318  		"validation": "verified",
   319  		"timestamp":  ts,
   320  	}, "")
   321  	trustedKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{
   322  		"name":  "root",
   323  		"since": ts,
   324  	}, keys.Root.PublicKey(), "")
   325  	trusted := []asserts.Assertion{trustedAcct, trustedKey}
   326  
   327  	genericAcct := NewAccount(rootSigning, "generic", map[string]interface{}{
   328  		"account-id": "generic",
   329  		"validation": "verified",
   330  		"timestamp":  ts,
   331  	}, "")
   332  
   333  	err := rootSigning.ImportKey(keys.GenericModels)
   334  	if err != nil {
   335  		panic(err)
   336  	}
   337  	genericModelsKey := NewAccountKey(rootSigning, genericAcct, map[string]interface{}{
   338  		"name":  "models",
   339  		"since": ts,
   340  	}, keys.GenericModels.PublicKey(), "")
   341  	generic := []asserts.Assertion{genericAcct, genericModelsKey}
   342  
   343  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   344  		Backstore:       asserts.NewMemoryBackstore(),
   345  		Trusted:         trusted,
   346  		OtherPredefined: generic,
   347  	})
   348  	if err != nil {
   349  		panic(err)
   350  	}
   351  	err = db.ImportKey(keys.Store)
   352  	if err != nil {
   353  		panic(err)
   354  	}
   355  	storeKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{
   356  		"name": "store",
   357  	}, keys.Store.PublicKey(), "")
   358  	err = db.Add(storeKey)
   359  	if err != nil {
   360  		panic(err)
   361  	}
   362  
   363  	err = db.ImportKey(keys.Generic)
   364  	if err != nil {
   365  		panic(err)
   366  	}
   367  	genericKey := NewAccountKey(rootSigning, genericAcct, map[string]interface{}{
   368  		"name":  "serials",
   369  		"since": ts,
   370  	}, keys.Generic.PublicKey(), "")
   371  	err = db.Add(genericKey)
   372  	if err != nil {
   373  		panic(err)
   374  	}
   375  
   376  	a, err := rootSigning.Sign(asserts.ModelType, map[string]interface{}{
   377  		"authority-id": "generic",
   378  		"series":       "16",
   379  		"brand-id":     "generic",
   380  		"model":        "generic-classic",
   381  		"classic":      "true",
   382  		"timestamp":    ts,
   383  	}, nil, genericModelsKey.PublicKeyID())
   384  	if err != nil {
   385  		panic(err)
   386  	}
   387  	genericClassicMod := a.(*asserts.Model)
   388  
   389  	return &StoreStack{
   390  		TrustedAccount: trustedAcct,
   391  		TrustedKey:     trustedKey,
   392  		Trusted:        trusted,
   393  
   394  		GenericAccount:      genericAcct,
   395  		GenericKey:          genericKey,
   396  		GenericModelsKey:    genericModelsKey,
   397  		Generic:             generic,
   398  		GenericClassicModel: genericClassicMod,
   399  
   400  		RootSigning: rootSigning,
   401  
   402  		SigningDB: &SigningDB{
   403  			AuthorityID: authorityID,
   404  			KeyID:       storeKey.PublicKeyID(),
   405  			Database:    db,
   406  		},
   407  	}
   408  }
   409  
   410  // StoreAccountKey retrieves one of the account-key assertions for the signing keys of the simulated store signing database.
   411  // "" for keyID means the default one. It panics on error.
   412  func (ss *StoreStack) StoreAccountKey(keyID string) *asserts.AccountKey {
   413  	if keyID == "" {
   414  		keyID = ss.KeyID
   415  	}
   416  	key, err := ss.Find(asserts.AccountKeyType, map[string]string{
   417  		"account-id":          ss.AuthorityID,
   418  		"public-key-sha3-384": keyID,
   419  	})
   420  	if asserts.IsNotFound(err) {
   421  		return nil
   422  	}
   423  	if err != nil {
   424  		panic(err)
   425  	}
   426  	return key.(*asserts.AccountKey)
   427  }
   428  
   429  // MockBuiltinBaseDeclaration mocks the builtin base-declaration exposed by asserts.BuiltinBaseDeclaration.
   430  func MockBuiltinBaseDeclaration(headers []byte) (restore func()) {
   431  	var prevHeaders []byte
   432  	decl := asserts.BuiltinBaseDeclaration()
   433  	if decl != nil {
   434  		prevHeaders, _ = decl.Signature()
   435  	}
   436  
   437  	err := asserts.InitBuiltinBaseDeclaration(headers)
   438  	if err != nil {
   439  		panic(err)
   440  	}
   441  
   442  	return func() {
   443  		err := asserts.InitBuiltinBaseDeclaration(prevHeaders)
   444  		if err != nil {
   445  			panic(err)
   446  		}
   447  	}
   448  }
   449  
   450  // FakeAssertionWithBody builds a fake assertion with the given body
   451  // and layered headers. A fake assertion cannot be verified or added
   452  // to a database or properly encoded. It can still be useful for unit
   453  // tests but shouldn't be used in integration tests.
   454  func FakeAssertionWithBody(body []byte, headerLayers ...map[string]interface{}) asserts.Assertion {
   455  	headers := map[string]interface{}{
   456  		"sign-key-sha3-384": "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
   457  	}
   458  	for _, h := range headerLayers {
   459  		for k, v := range h {
   460  			headers[k] = v
   461  		}
   462  	}
   463  
   464  	_, hasTimestamp := headers["timestamp"]
   465  	_, hasSince := headers["since"]
   466  	if !(hasTimestamp || hasSince) {
   467  		headers["timestamp"] = time.Now().Format(time.RFC3339)
   468  	}
   469  
   470  	a, err := asserts.Assemble(headers, body, nil, []byte("AXNpZw=="))
   471  	if err != nil {
   472  		panic(fmt.Sprintf("cannot build fake assertion: %v", err))
   473  	}
   474  	return a
   475  }
   476  
   477  // FakeAssertion builds a fake assertion with given layered headers
   478  // and an empty body. A fake assertion cannot be verified or added to
   479  // a database or properly encoded. It can still be useful for unit
   480  // tests but shouldn't be used in integration tests.
   481  func FakeAssertion(headerLayers ...map[string]interface{}) asserts.Assertion {
   482  	return FakeAssertionWithBody(nil, headerLayers...)
   483  }
   484  
   485  type accuDB interface {
   486  	Add(asserts.Assertion) error
   487  }
   488  
   489  // AddMany conveniently adds the given assertions to the db.
   490  // It is idempotent but otherwise panics on error.
   491  func AddMany(db accuDB, assertions ...asserts.Assertion) {
   492  	for _, a := range assertions {
   493  		err := db.Add(a)
   494  		if _, ok := err.(*asserts.RevisionError); !ok {
   495  			if err != nil {
   496  				panic(fmt.Sprintf("cannot add test assertions: %v", err))
   497  			}
   498  		}
   499  	}
   500  }
   501  
   502  // SigningAccounts manages a set of brand or user accounts,
   503  // with their keys that can sign models etc.
   504  type SigningAccounts struct {
   505  	store *StoreStack
   506  
   507  	signing map[string]*SigningDB
   508  
   509  	accts    map[string]*asserts.Account
   510  	acctKeys map[string]*asserts.AccountKey
   511  }
   512  
   513  // NewSigningAccounts creates a new SigningAccounts instance.
   514  func NewSigningAccounts(store *StoreStack) *SigningAccounts {
   515  	return &SigningAccounts{
   516  		store:    store,
   517  		signing:  make(map[string]*SigningDB),
   518  		accts:    make(map[string]*asserts.Account),
   519  		acctKeys: make(map[string]*asserts.AccountKey),
   520  	}
   521  }
   522  
   523  func (sa *SigningAccounts) Register(accountID string, brandPrivKey asserts.PrivateKey, extra map[string]interface{}) *SigningDB {
   524  	brandSigning := NewSigningDB(accountID, brandPrivKey)
   525  	sa.signing[accountID] = brandSigning
   526  
   527  	acctHeaders := map[string]interface{}{
   528  		"account-id": accountID,
   529  	}
   530  	for k, v := range extra {
   531  		acctHeaders[k] = v
   532  	}
   533  
   534  	brandAcct := NewAccount(sa.store, accountID, acctHeaders, "")
   535  	sa.accts[accountID] = brandAcct
   536  
   537  	brandPubKey, err := brandSigning.PublicKey("")
   538  	if err != nil {
   539  		panic(err)
   540  	}
   541  	brandAcctKey := NewAccountKey(sa.store, brandAcct, nil, brandPubKey, "")
   542  	sa.acctKeys[accountID] = brandAcctKey
   543  
   544  	return brandSigning
   545  }
   546  
   547  func (sa *SigningAccounts) Account(accountID string) *asserts.Account {
   548  	if acct := sa.accts[accountID]; acct != nil {
   549  		return acct
   550  	}
   551  	panic(fmt.Sprintf("unknown test account-id: %s", accountID))
   552  }
   553  
   554  func (sa *SigningAccounts) AccountKey(accountID string) *asserts.AccountKey {
   555  	if acctKey := sa.acctKeys[accountID]; acctKey != nil {
   556  		return acctKey
   557  	}
   558  	panic(fmt.Sprintf("unknown test account-id: %s", accountID))
   559  }
   560  
   561  func (sa *SigningAccounts) PublicKey(accountID string) asserts.PublicKey {
   562  	pubKey, err := sa.Signing(accountID).PublicKey("")
   563  	if err != nil {
   564  		panic(err)
   565  	}
   566  	return pubKey
   567  }
   568  
   569  func (sa *SigningAccounts) Signing(accountID string) *SigningDB {
   570  	// convenience
   571  	if accountID == sa.store.RootSigning.AuthorityID {
   572  		return sa.store.RootSigning
   573  	}
   574  	if signer := sa.signing[accountID]; signer != nil {
   575  		return signer
   576  	}
   577  	panic(fmt.Sprintf("unknown test account-id: %s", accountID))
   578  }
   579  
   580  // Model creates a new model for accountID. accountID can also be the account-id of the underlying store stack.
   581  func (sa *SigningAccounts) Model(accountID, model string, extras ...map[string]interface{}) *asserts.Model {
   582  	headers := map[string]interface{}{
   583  		"series":    "16",
   584  		"brand-id":  accountID,
   585  		"model":     model,
   586  		"timestamp": time.Now().Format(time.RFC3339),
   587  	}
   588  	for _, extra := range extras {
   589  		for k, v := range extra {
   590  			headers[k] = v
   591  		}
   592  	}
   593  
   594  	signer := sa.Signing(accountID)
   595  
   596  	modelAs, err := signer.Sign(asserts.ModelType, headers, nil, "")
   597  	if err != nil {
   598  		panic(err)
   599  	}
   600  	return modelAs.(*asserts.Model)
   601  }
   602  
   603  // AccountsAndKeys returns the account and account-key for each given
   604  // accountID in that order.
   605  func (sa *SigningAccounts) AccountsAndKeys(accountIDs ...string) []asserts.Assertion {
   606  	res := make([]asserts.Assertion, 0, 2*len(accountIDs))
   607  	for _, accountID := range accountIDs {
   608  		res = append(res, sa.Account(accountID))
   609  		res = append(res, sa.AccountKey(accountID))
   610  	}
   611  	return res
   612  }