github.com/rigado/snapd@v2.42.5-go-mod+incompatible/asserts/database.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  // Package asserts implements snappy assertions and a database
    21  // abstraction for managing and holding them.
    22  package asserts
    23  
    24  import (
    25  	"fmt"
    26  	"regexp"
    27  	"time"
    28  )
    29  
    30  // NotFoundError is returned when an assertion can not be found.
    31  type NotFoundError struct {
    32  	Type    *AssertionType
    33  	Headers map[string]string
    34  }
    35  
    36  func (e *NotFoundError) Error() string {
    37  	pk, err := PrimaryKeyFromHeaders(e.Type, e.Headers)
    38  	if err != nil || len(e.Headers) != len(pk) {
    39  		// TODO: worth conveying more information?
    40  		return fmt.Sprintf("%s assertion not found", e.Type.Name)
    41  	}
    42  
    43  	return fmt.Sprintf("%v not found", &Ref{Type: e.Type, PrimaryKey: pk})
    44  }
    45  
    46  // IsNotFound returns whether err is an assertion not found error.
    47  func IsNotFound(err error) bool {
    48  	_, ok := err.(*NotFoundError)
    49  	return ok
    50  }
    51  
    52  // A Backstore stores assertions. It can store and retrieve assertions
    53  // by type under unique primary key headers (whose names are available
    54  // from assertType.PrimaryKey). Plus it supports searching by headers.
    55  // Lookups can be limited to a maximum allowed format.
    56  type Backstore interface {
    57  	// Put stores an assertion.
    58  	// It is responsible for checking that assert is newer than a
    59  	// previously stored revision with the same primary key headers.
    60  	Put(assertType *AssertionType, assert Assertion) error
    61  	// Get returns the assertion with the given unique key for its
    62  	// primary key headers.  If none is present it returns a
    63  	// NotFoundError, usually with omitted Headers.
    64  	Get(assertType *AssertionType, key []string, maxFormat int) (Assertion, error)
    65  	// Search returns assertions matching the given headers.
    66  	// It invokes foundCb for each found assertion.
    67  	Search(assertType *AssertionType, headers map[string]string, foundCb func(Assertion), maxFormat int) error
    68  }
    69  
    70  type nullBackstore struct{}
    71  
    72  func (nbs nullBackstore) Put(t *AssertionType, a Assertion) error {
    73  	return fmt.Errorf("cannot store assertions without setting a proper assertion backstore implementation")
    74  }
    75  
    76  func (nbs nullBackstore) Get(t *AssertionType, k []string, maxFormat int) (Assertion, error) {
    77  	return nil, &NotFoundError{Type: t}
    78  }
    79  
    80  func (nbs nullBackstore) Search(t *AssertionType, h map[string]string, f func(Assertion), maxFormat int) error {
    81  	return nil
    82  }
    83  
    84  // A KeypairManager is a manager and backstore for private/public key pairs.
    85  type KeypairManager interface {
    86  	// Put stores the given private/public key pair,
    87  	// making sure it can be later retrieved by its unique key id with Get.
    88  	// Trying to store a key with an already present key id should
    89  	// result in an error.
    90  	Put(privKey PrivateKey) error
    91  	// Get returns the private/public key pair with the given key id.
    92  	Get(keyID string) (PrivateKey, error)
    93  }
    94  
    95  // DatabaseConfig for an assertion database.
    96  type DatabaseConfig struct {
    97  	// trusted set of assertions (account and account-key supported),
    98  	// used to establish root keys and trusted authorities
    99  	Trusted []Assertion
   100  	// predefined assertions but that do not establish foundational trust
   101  	OtherPredefined []Assertion
   102  	// backstore for assertions, left unset storing assertions will error
   103  	Backstore Backstore
   104  	// manager/backstore for keypairs, defaults to in-memory implementation
   105  	KeypairManager KeypairManager
   106  	// assertion checkers used by Database.Check, left unset DefaultCheckers will be used which is recommended
   107  	Checkers []Checker
   108  }
   109  
   110  // RevisionError indicates a revision improperly used for an operation.
   111  type RevisionError struct {
   112  	Used, Current int
   113  }
   114  
   115  func (e *RevisionError) Error() string {
   116  	if e.Used < 0 || e.Current < 0 {
   117  		// TODO: message may need tweaking once there's a use.
   118  		return fmt.Sprintf("assertion revision is unknown")
   119  	}
   120  	if e.Used == e.Current {
   121  		return fmt.Sprintf("revision %d is already the current revision", e.Used)
   122  	}
   123  	if e.Used < e.Current {
   124  		return fmt.Sprintf("revision %d is older than current revision %d", e.Used, e.Current)
   125  	}
   126  	return fmt.Sprintf("revision %d is more recent than current revision %d", e.Used, e.Current)
   127  }
   128  
   129  // UnsupportedFormatError indicates an assertion with a format iteration not yet supported by the present version of asserts.
   130  type UnsupportedFormatError struct {
   131  	Ref    *Ref
   132  	Format int
   133  	// Update marks there was already a current revision of the assertion and it has been kept.
   134  	Update bool
   135  }
   136  
   137  func (e *UnsupportedFormatError) Error() string {
   138  	postfx := ""
   139  	if e.Update {
   140  		postfx = " (current not updated)"
   141  	}
   142  	return fmt.Sprintf("proposed %q assertion has format %d but %d is latest supported%s", e.Ref.Type.Name, e.Format, e.Ref.Type.MaxSupportedFormat(), postfx)
   143  }
   144  
   145  // IsUnaccceptedUpdate returns whether the error indicates that an
   146  // assertion revision was already present and has been kept because
   147  // the update was not accepted.
   148  func IsUnaccceptedUpdate(err error) bool {
   149  	switch x := err.(type) {
   150  	case *UnsupportedFormatError:
   151  		return x.Update
   152  	case *RevisionError:
   153  		return x.Used <= x.Current
   154  	}
   155  	return false
   156  }
   157  
   158  // A RODatabase exposes read-only access to an assertion database.
   159  type RODatabase interface {
   160  	// IsTrustedAccount returns whether the account is part of the trusted set.
   161  	IsTrustedAccount(accountID string) bool
   162  	// Find an assertion based on arbitrary headers.
   163  	// Provided headers must contain the primary key for the assertion type.
   164  	// It returns a NotFoundError if the assertion cannot be found.
   165  	Find(assertionType *AssertionType, headers map[string]string) (Assertion, error)
   166  	// FindPredefined finds an assertion in the predefined sets
   167  	// (trusted or not) based on arbitrary headers.  Provided
   168  	// headers must contain the primary key for the assertion
   169  	// type.  It returns a NotFoundError if the assertion cannot
   170  	// be found.
   171  	FindPredefined(assertionType *AssertionType, headers map[string]string) (Assertion, error)
   172  	// FindTrusted finds an assertion in the trusted set based on
   173  	// arbitrary headers.  Provided headers must contain the
   174  	// primary key for the assertion type.  It returns a
   175  	// NotFoundError if the assertion cannot be found.
   176  	FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error)
   177  	// FindMany finds assertions based on arbitrary headers.
   178  	// It returns a NotFoundError if no assertion can be found.
   179  	FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error)
   180  	// FindManyPredefined finds assertions in the predefined sets
   181  	// (trusted or not) based on arbitrary headers.  It returns a
   182  	// NotFoundError if no assertion can be found.
   183  	FindManyPredefined(assertionType *AssertionType, headers map[string]string) ([]Assertion, error)
   184  	// Check tests whether the assertion is properly signed and consistent with all the stored knowledge.
   185  	Check(assert Assertion) error
   186  }
   187  
   188  // A Checker defines a check on an assertion considering aspects such as
   189  // the signing key, and consistency with other
   190  // assertions in the database.
   191  type Checker func(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error
   192  
   193  // Database holds assertions and can be used to sign or check
   194  // further assertions.
   195  type Database struct {
   196  	bs         Backstore
   197  	keypairMgr KeypairManager
   198  
   199  	trusted    Backstore
   200  	predefined Backstore
   201  	// all backstores to consider for find
   202  	backstores []Backstore
   203  	// backstores of dbs this was built on by stacking
   204  	stackedOn []Backstore
   205  
   206  	checkers []Checker
   207  }
   208  
   209  // OpenDatabase opens the assertion database based on the configuration.
   210  func OpenDatabase(cfg *DatabaseConfig) (*Database, error) {
   211  	bs := cfg.Backstore
   212  	keypairMgr := cfg.KeypairManager
   213  
   214  	if bs == nil {
   215  		bs = nullBackstore{}
   216  	}
   217  	if keypairMgr == nil {
   218  		keypairMgr = NewMemoryKeypairManager()
   219  	}
   220  
   221  	trustedBackstore := NewMemoryBackstore()
   222  
   223  	for _, a := range cfg.Trusted {
   224  		switch accepted := a.(type) {
   225  		case *AccountKey:
   226  			accKey := accepted
   227  			err := trustedBackstore.Put(AccountKeyType, accKey)
   228  			if err != nil {
   229  				return nil, fmt.Errorf("cannot predefine trusted account key %q for %q: %v", accKey.PublicKeyID(), accKey.AccountID(), err)
   230  			}
   231  
   232  		case *Account:
   233  			acct := accepted
   234  			err := trustedBackstore.Put(AccountType, acct)
   235  			if err != nil {
   236  				return nil, fmt.Errorf("cannot predefine trusted account %q: %v", acct.DisplayName(), err)
   237  			}
   238  		default:
   239  			return nil, fmt.Errorf("cannot predefine trusted assertions that are not account-key or account: %s", a.Type().Name)
   240  		}
   241  	}
   242  
   243  	otherPredefinedBackstore := NewMemoryBackstore()
   244  
   245  	for _, a := range cfg.OtherPredefined {
   246  		err := otherPredefinedBackstore.Put(a.Type(), a)
   247  		if err != nil {
   248  			return nil, fmt.Errorf("cannot predefine assertion %v: %v", a.Ref(), err)
   249  		}
   250  	}
   251  
   252  	checkers := cfg.Checkers
   253  	if len(checkers) == 0 {
   254  		checkers = DefaultCheckers
   255  	}
   256  	dbCheckers := make([]Checker, len(checkers))
   257  	copy(dbCheckers, checkers)
   258  
   259  	return &Database{
   260  		bs:         bs,
   261  		keypairMgr: keypairMgr,
   262  		trusted:    trustedBackstore,
   263  		predefined: otherPredefinedBackstore,
   264  		// order here is relevant, Find* precedence and
   265  		// findAccountKey depend on it, trusted should win over the
   266  		// general backstore!
   267  		backstores: []Backstore{trustedBackstore, otherPredefinedBackstore, bs},
   268  		checkers:   dbCheckers,
   269  	}, nil
   270  }
   271  
   272  // WithStackedBackstore returns a new database that adds to the given backstore
   273  // only but finds in backstore and the base database backstores and
   274  // cross-checks against all of them.
   275  // This is useful to cross-check a set of assertions without adding
   276  // them to the database.
   277  func (db *Database) WithStackedBackstore(backstore Backstore) *Database {
   278  	// original bs goes in front of stacked-on ones
   279  	stackedOn := []Backstore{db.bs}
   280  	stackedOn = append(stackedOn, db.stackedOn...)
   281  	// find order: trusted, predefined, new backstore, stacked-on ones
   282  	backstores := []Backstore{db.trusted, db.predefined}
   283  	backstores = append(backstores, backstore)
   284  	backstores = append(backstores, stackedOn...)
   285  	return &Database{
   286  		bs:         backstore,
   287  		keypairMgr: db.keypairMgr,
   288  		trusted:    db.trusted,
   289  		predefined: db.predefined,
   290  		backstores: backstores,
   291  		stackedOn:  stackedOn,
   292  		checkers:   db.checkers,
   293  	}
   294  }
   295  
   296  // ImportKey stores the given private/public key pair.
   297  func (db *Database) ImportKey(privKey PrivateKey) error {
   298  	return db.keypairMgr.Put(privKey)
   299  }
   300  
   301  var (
   302  	// for sanity checking of base64 hash strings
   303  	base64HashLike = regexp.MustCompile("^[[:alnum:]_-]*$")
   304  )
   305  
   306  func (db *Database) safeGetPrivateKey(keyID string) (PrivateKey, error) {
   307  	if keyID == "" {
   308  		return nil, fmt.Errorf("key id is empty")
   309  	}
   310  	if !base64HashLike.MatchString(keyID) {
   311  		return nil, fmt.Errorf("key id contains unexpected chars: %q", keyID)
   312  	}
   313  	return db.keypairMgr.Get(keyID)
   314  }
   315  
   316  // PublicKey returns the public key part of the key pair that has the given key id.
   317  func (db *Database) PublicKey(keyID string) (PublicKey, error) {
   318  	privKey, err := db.safeGetPrivateKey(keyID)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	return privKey.PublicKey(), nil
   323  }
   324  
   325  // Sign assembles an assertion with the provided information and signs it
   326  // with the private key from `headers["authority-id"]` that has the provided key id.
   327  func (db *Database) Sign(assertType *AssertionType, headers map[string]interface{}, body []byte, keyID string) (Assertion, error) {
   328  	privKey, err := db.safeGetPrivateKey(keyID)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	return assembleAndSign(assertType, headers, body, privKey)
   333  }
   334  
   335  // findAccountKey finds an AccountKey exactly with account id and key id.
   336  func (db *Database) findAccountKey(authorityID, keyID string) (*AccountKey, error) {
   337  	key := []string{keyID}
   338  	// consider trusted account keys then disk stored account keys
   339  	for _, bs := range db.backstores {
   340  		a, err := bs.Get(AccountKeyType, key, AccountKeyType.MaxSupportedFormat())
   341  		if err == nil {
   342  			hit := a.(*AccountKey)
   343  			if hit.AccountID() != authorityID {
   344  				return nil, fmt.Errorf("found public key %q from %q but expected it from: %s", keyID, hit.AccountID(), authorityID)
   345  			}
   346  			return hit, nil
   347  		}
   348  		if !IsNotFound(err) {
   349  			return nil, err
   350  		}
   351  	}
   352  	return nil, &NotFoundError{Type: AccountKeyType}
   353  }
   354  
   355  // IsTrustedAccount returns whether the account is part of the trusted set.
   356  func (db *Database) IsTrustedAccount(accountID string) bool {
   357  	if accountID == "" {
   358  		return false
   359  	}
   360  	_, err := db.trusted.Get(AccountType, []string{accountID}, AccountType.MaxSupportedFormat())
   361  	return err == nil
   362  }
   363  
   364  // Check tests whether the assertion is properly signed and consistent with all the stored knowledge.
   365  func (db *Database) Check(assert Assertion) error {
   366  	if !assert.SupportedFormat() {
   367  		return &UnsupportedFormatError{Ref: assert.Ref(), Format: assert.Format()}
   368  	}
   369  
   370  	typ := assert.Type()
   371  	now := time.Now()
   372  
   373  	var accKey *AccountKey
   374  	var err error
   375  	if typ.flags&noAuthority == 0 {
   376  		// TODO: later may need to consider type of assert to find candidate keys
   377  		accKey, err = db.findAccountKey(assert.AuthorityID(), assert.SignKeyID())
   378  		if IsNotFound(err) {
   379  			return fmt.Errorf("no matching public key %q for signature by %q", assert.SignKeyID(), assert.AuthorityID())
   380  		}
   381  		if err != nil {
   382  			return fmt.Errorf("error finding matching public key for signature: %v", err)
   383  		}
   384  	} else {
   385  		if assert.AuthorityID() != "" {
   386  			return fmt.Errorf("internal error: %q assertion cannot have authority-id set", typ.Name)
   387  		}
   388  	}
   389  
   390  	for _, checker := range db.checkers {
   391  		err := checker(assert, accKey, db, now)
   392  		if err != nil {
   393  			return err
   394  		}
   395  	}
   396  
   397  	return nil
   398  }
   399  
   400  // Add persists the assertion after ensuring it is properly signed and consistent with all the stored knowledge.
   401  // It will return an error when trying to add an older revision of the assertion than the one currently stored.
   402  func (db *Database) Add(assert Assertion) error {
   403  	ref := assert.Ref()
   404  
   405  	if len(ref.PrimaryKey) == 0 {
   406  		return fmt.Errorf("internal error: assertion type %q has no primary key", ref.Type.Name)
   407  	}
   408  
   409  	err := db.Check(assert)
   410  	if err != nil {
   411  		if ufe, ok := err.(*UnsupportedFormatError); ok {
   412  			_, err := ref.Resolve(db.Find)
   413  			if err != nil && !IsNotFound(err) {
   414  				return err
   415  			}
   416  			return &UnsupportedFormatError{Ref: ufe.Ref, Format: ufe.Format, Update: err == nil}
   417  		}
   418  		return err
   419  	}
   420  
   421  	for i, keyVal := range ref.PrimaryKey {
   422  		if keyVal == "" {
   423  			return fmt.Errorf("missing or non-string primary key header: %v", ref.Type.PrimaryKey[i])
   424  		}
   425  	}
   426  
   427  	// assuming trusted account keys/assertions will be managed
   428  	// through the os snap this seems the safest policy until we
   429  	// know more/better
   430  	_, err = db.trusted.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat())
   431  	if !IsNotFound(err) {
   432  		return fmt.Errorf("cannot add %q assertion with primary key clashing with a trusted assertion: %v", ref.Type.Name, ref.PrimaryKey)
   433  	}
   434  
   435  	_, err = db.predefined.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat())
   436  	if !IsNotFound(err) {
   437  		return fmt.Errorf("cannot add %q assertion with primary key clashing with a predefined assertion: %v", ref.Type.Name, ref.PrimaryKey)
   438  	}
   439  
   440  	// this is non empty only in the stacked case
   441  	if len(db.stackedOn) != 0 {
   442  		headers, err := HeadersFromPrimaryKey(ref.Type, ref.PrimaryKey)
   443  		if err != nil {
   444  			return fmt.Errorf("internal error: HeadersFromPrimaryKey for %q failed on prechecked data: %s", ref.Type.Name, ref.PrimaryKey)
   445  		}
   446  		cur, err := find(db.stackedOn, ref.Type, headers, -1)
   447  		if err == nil {
   448  			curRev := cur.Revision()
   449  			rev := assert.Revision()
   450  			if curRev >= rev {
   451  				return &RevisionError{Current: curRev, Used: rev}
   452  			}
   453  		} else if !IsNotFound(err) {
   454  			return err
   455  		}
   456  	}
   457  
   458  	return db.bs.Put(ref.Type, assert)
   459  }
   460  
   461  func searchMatch(assert Assertion, expectedHeaders map[string]string) bool {
   462  	// check non-primary-key headers as well
   463  	for expectedKey, expectedValue := range expectedHeaders {
   464  		if assert.Header(expectedKey) != expectedValue {
   465  			return false
   466  		}
   467  	}
   468  	return true
   469  }
   470  
   471  func find(backstores []Backstore, assertionType *AssertionType, headers map[string]string, maxFormat int) (Assertion, error) {
   472  	err := checkAssertType(assertionType)
   473  	if err != nil {
   474  		return nil, err
   475  	}
   476  	maxSupp := assertionType.MaxSupportedFormat()
   477  	if maxFormat == -1 {
   478  		maxFormat = maxSupp
   479  	} else {
   480  		if maxFormat > maxSupp {
   481  			return nil, fmt.Errorf("cannot find %q assertions for format %d higher than supported format %d", assertionType.Name, maxFormat, maxSupp)
   482  		}
   483  	}
   484  
   485  	keyValues, err := PrimaryKeyFromHeaders(assertionType, headers)
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  
   490  	var assert Assertion
   491  	for _, bs := range backstores {
   492  		a, err := bs.Get(assertionType, keyValues, maxFormat)
   493  		if err == nil {
   494  			assert = a
   495  			break
   496  		}
   497  		if !IsNotFound(err) {
   498  			return nil, err
   499  		}
   500  	}
   501  
   502  	if assert == nil || !searchMatch(assert, headers) {
   503  		return nil, &NotFoundError{Type: assertionType, Headers: headers}
   504  	}
   505  
   506  	return assert, nil
   507  }
   508  
   509  // Find an assertion based on arbitrary headers.
   510  // Provided headers must contain the primary key for the assertion type.
   511  // It returns a NotFoundError if the assertion cannot be found.
   512  func (db *Database) Find(assertionType *AssertionType, headers map[string]string) (Assertion, error) {
   513  	return find(db.backstores, assertionType, headers, -1)
   514  }
   515  
   516  // FindMaxFormat finds an assertion like Find but such that its
   517  // format is <= maxFormat by passing maxFormat along to the backend.
   518  // It returns a NotFoundError if such an assertion cannot be found.
   519  func (db *Database) FindMaxFormat(assertionType *AssertionType, headers map[string]string, maxFormat int) (Assertion, error) {
   520  	return find(db.backstores, assertionType, headers, maxFormat)
   521  }
   522  
   523  // FindPredefined finds an assertion in the predefined sets (trusted
   524  // or not) based on arbitrary headers.  Provided headers must contain
   525  // the primary key for the assertion type.  It returns a NotFoundError
   526  // if the assertion cannot be found.
   527  func (db *Database) FindPredefined(assertionType *AssertionType, headers map[string]string) (Assertion, error) {
   528  	return find([]Backstore{db.trusted, db.predefined}, assertionType, headers, -1)
   529  }
   530  
   531  // FindTrusted finds an assertion in the trusted set based on arbitrary headers.
   532  // Provided headers must contain the primary key for the assertion type.
   533  // It returns a NotFoundError if the assertion cannot be found.
   534  func (db *Database) FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error) {
   535  	return find([]Backstore{db.trusted}, assertionType, headers, -1)
   536  }
   537  
   538  func (db *Database) findMany(backstores []Backstore, assertionType *AssertionType, headers map[string]string) ([]Assertion, error) {
   539  	err := checkAssertType(assertionType)
   540  	if err != nil {
   541  		return nil, err
   542  	}
   543  	res := []Assertion{}
   544  
   545  	foundCb := func(assert Assertion) {
   546  		res = append(res, assert)
   547  	}
   548  
   549  	// TODO: Find variant taking this
   550  	maxFormat := assertionType.MaxSupportedFormat()
   551  	for _, bs := range backstores {
   552  		err = bs.Search(assertionType, headers, foundCb, maxFormat)
   553  		if err != nil {
   554  			return nil, err
   555  		}
   556  	}
   557  
   558  	if len(res) == 0 {
   559  		return nil, &NotFoundError{Type: assertionType, Headers: headers}
   560  	}
   561  	return res, nil
   562  }
   563  
   564  // FindMany finds assertions based on arbitrary headers.
   565  // It returns a NotFoundError if no assertion can be found.
   566  func (db *Database) FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) {
   567  	return db.findMany(db.backstores, assertionType, headers)
   568  }
   569  
   570  // FindManyPrefined finds assertions in the predefined sets (trusted
   571  // or not) based on arbitrary headers.  It returns a NotFoundError if
   572  // no assertion can be found.
   573  func (db *Database) FindManyPredefined(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) {
   574  	return db.findMany([]Backstore{db.trusted, db.predefined}, assertionType, headers)
   575  }
   576  
   577  // assertion checkers
   578  
   579  // CheckSigningKeyIsNotExpired checks that the signing key is not expired.
   580  func CheckSigningKeyIsNotExpired(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
   581  	if signingKey == nil {
   582  		// assert isn't signed with an account-key key, CheckSignature
   583  		// will fail anyway unless we teach it more stuff,
   584  		// Also this check isn't so relevant for self-signed asserts
   585  		// (e.g. account-key-request)
   586  		return nil
   587  	}
   588  	if !signingKey.isKeyValidAt(checkTime) {
   589  		return fmt.Errorf("assertion is signed with expired public key %q from %q", assert.SignKeyID(), assert.AuthorityID())
   590  	}
   591  	return nil
   592  }
   593  
   594  // CheckSignature checks that the signature is valid.
   595  func CheckSignature(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
   596  	var pubKey PublicKey
   597  	if signingKey != nil {
   598  		pubKey = signingKey.publicKey()
   599  	} else {
   600  		custom, ok := assert.(customSigner)
   601  		if !ok {
   602  			return fmt.Errorf("cannot check no-authority assertion type %q", assert.Type().Name)
   603  		}
   604  		pubKey = custom.signKey()
   605  	}
   606  	content, encSig := assert.Signature()
   607  	signature, err := decodeSignature(encSig)
   608  	if err != nil {
   609  		return err
   610  	}
   611  	err = pubKey.verify(content, signature)
   612  	if err != nil {
   613  		return fmt.Errorf("failed signature verification: %v", err)
   614  	}
   615  	return nil
   616  }
   617  
   618  type timestamped interface {
   619  	Timestamp() time.Time
   620  }
   621  
   622  // CheckTimestampVsSigningKeyValidity verifies that the timestamp of
   623  // the assertion is within the signing key validity.
   624  func CheckTimestampVsSigningKeyValidity(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
   625  	if signingKey == nil {
   626  		// assert isn't signed with an account-key key, CheckSignature
   627  		// will fail anyway unless we teach it more stuff.
   628  		// Also this check isn't so relevant for self-signed asserts
   629  		// (e.g. account-key-request)
   630  		return nil
   631  	}
   632  	if tstamped, ok := assert.(timestamped); ok {
   633  		checkTime := tstamped.Timestamp()
   634  		if !signingKey.isKeyValidAt(checkTime) {
   635  			until := ""
   636  			if !signingKey.Until().IsZero() {
   637  				until = fmt.Sprintf(" until %q", signingKey.Until())
   638  			}
   639  			return fmt.Errorf("%s assertion timestamp outside of signing key validity (key valid since %q%s)", assert.Type().Name, signingKey.Since(), until)
   640  		}
   641  	}
   642  	return nil
   643  }
   644  
   645  // XXX: keeping these in this form until we know better
   646  
   647  // A consistencyChecker performs further checks based on the full
   648  // assertion database knowledge and its own signing key.
   649  type consistencyChecker interface {
   650  	checkConsistency(roDB RODatabase, signingKey *AccountKey) error
   651  }
   652  
   653  // CheckCrossConsistency verifies that the assertion is consistent with the other statements in the database.
   654  func CheckCrossConsistency(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
   655  	// see if the assertion requires further checks
   656  	if checker, ok := assert.(consistencyChecker); ok {
   657  		return checker.checkConsistency(roDB, signingKey)
   658  	}
   659  	return nil
   660  }
   661  
   662  // DefaultCheckers lists the default and recommended assertion
   663  // checkers used by Database if none are specified in the
   664  // DatabaseConfig.Checkers.
   665  var DefaultCheckers = []Checker{
   666  	CheckSigningKeyIsNotExpired,
   667  	CheckSignature,
   668  	CheckTimestampVsSigningKeyValidity,
   669  	CheckCrossConsistency,
   670  }