2015-11-09 13:01:37 +01:00
|
|
|
// -*- Mode: Go; indent-tabs-mode: t -*-
|
|
|
|
|
|
|
|
|
|
/*
|
2016-01-22 14:54:04 +01:00
|
|
|
* Copyright (C) 2015-2016 Canonical Ltd
|
2015-11-09 13:01:37 +01:00
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2015-11-09 15:14:07 +01:00
|
|
|
// Package asserts implements snappy assertions and a database
|
2015-11-09 13:01:37 +01:00
|
|
|
// abstraction for managing and holding them.
|
|
|
|
|
package asserts
|
|
|
|
|
|
|
|
|
|
import (
|
2015-11-23 18:28:30 +01:00
|
|
|
"errors"
|
2015-11-09 16:11:26 +01:00
|
|
|
"fmt"
|
2015-12-02 21:23:15 +01:00
|
|
|
"regexp"
|
2015-11-18 18:08:59 +01:00
|
|
|
"time"
|
2015-11-09 13:01:37 +01:00
|
|
|
)
|
|
|
|
|
|
2016-01-21 14:43:51 +01:00
|
|
|
// A Backstore stores assertions. It can store and retrieve assertions
|
|
|
|
|
// by type under unique primary key headers (whose names are available
|
|
|
|
|
// from assertType.PrimaryKey). Plus it supports searching by headers.
|
2015-12-17 13:12:17 +01:00
|
|
|
type Backstore interface {
|
2016-01-21 14:43:51 +01:00
|
|
|
// Put stores an assertion.
|
2015-12-17 13:12:17 +01:00
|
|
|
// It is responsible for checking that assert is newer than a
|
2016-01-21 14:43:51 +01:00
|
|
|
// previously stored revision with the same primary key headers.
|
|
|
|
|
Put(assertType *AssertionType, assert Assertion) error
|
|
|
|
|
// Get returns the assertion with the given unique key for its primary key headers.
|
2015-12-17 13:12:17 +01:00
|
|
|
// If none is present it returns ErrNotFound.
|
2016-01-21 14:43:51 +01:00
|
|
|
Get(assertType *AssertionType, key []string) (Assertion, error)
|
2016-01-12 14:47:33 +01:00
|
|
|
// Search returns assertions matching the given headers.
|
2015-12-17 13:12:17 +01:00
|
|
|
// It invokes foundCb for each found assertion.
|
2016-01-21 14:43:51 +01:00
|
|
|
Search(assertType *AssertionType, headers map[string]string, foundCb func(Assertion)) error
|
2015-12-17 13:12:17 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-12 19:14:53 +01:00
|
|
|
type nullBackstore struct{}
|
|
|
|
|
|
2016-01-21 14:43:51 +01:00
|
|
|
func (nbs nullBackstore) Put(t *AssertionType, a Assertion) error {
|
2016-01-12 23:13:14 +01:00
|
|
|
return fmt.Errorf("cannot store assertions without setting a proper assertion backstore implementation")
|
2016-01-12 19:14:53 +01:00
|
|
|
}
|
|
|
|
|
|
2016-01-21 14:43:51 +01:00
|
|
|
func (nbs nullBackstore) Get(t *AssertionType, k []string) (Assertion, error) {
|
2016-01-12 19:14:53 +01:00
|
|
|
return nil, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-21 14:43:51 +01:00
|
|
|
func (nbs nullBackstore) Search(t *AssertionType, h map[string]string, f func(Assertion)) error {
|
2016-01-12 19:14:53 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-12 14:47:33 +01:00
|
|
|
// A KeypairManager is a manager and backstore for private/public key pairs.
|
2015-12-18 11:29:57 +01:00
|
|
|
type KeypairManager interface {
|
2016-08-21 16:41:28 +02:00
|
|
|
// Put stores the given private/public key pair,
|
|
|
|
|
// making sure it can be later retrieved by its unique key id with Get.
|
2016-01-07 17:56:52 +01:00
|
|
|
// Trying to store a key with an already present key id should
|
|
|
|
|
// result in an error.
|
2016-08-21 16:41:28 +02:00
|
|
|
Put(privKey PrivateKey) error
|
2016-01-07 17:56:52 +01:00
|
|
|
// Get returns the private/public key pair with the given key id.
|
2016-08-21 16:41:28 +02:00
|
|
|
Get(keyID string) (PrivateKey, error)
|
2015-12-18 11:29:57 +01:00
|
|
|
}
|
|
|
|
|
|
2015-11-09 15:14:07 +01:00
|
|
|
// DatabaseConfig for an assertion database.
|
|
|
|
|
type DatabaseConfig struct {
|
2016-07-15 12:17:49 +02:00
|
|
|
// trusted set of assertions (account and account-key supported)
|
2016-06-10 18:44:15 +02:00
|
|
|
Trusted []Assertion
|
2016-01-12 23:16:49 +01:00
|
|
|
// backstore for assertions, left unset storing assertions will error
|
2015-12-17 13:12:17 +01:00
|
|
|
Backstore Backstore
|
2016-08-21 15:52:24 +02:00
|
|
|
// manager/backstore for keypairs, defaults to in-memory implementation
|
2015-12-18 11:29:57 +01:00
|
|
|
KeypairManager KeypairManager
|
2016-02-09 20:39:42 +01:00
|
|
|
// assertion checkers used by Database.Check, left unset DefaultCheckers will be used which is recommended
|
|
|
|
|
Checkers []Checker
|
2015-11-09 13:01:37 +01:00
|
|
|
}
|
|
|
|
|
|
2015-11-23 18:28:30 +01:00
|
|
|
// Well-known errors
|
|
|
|
|
var (
|
2015-11-24 19:41:19 +01:00
|
|
|
ErrNotFound = errors.New("assertion not found")
|
2015-11-23 18:28:30 +01:00
|
|
|
)
|
|
|
|
|
|
2016-04-28 11:38:23 +01:00
|
|
|
// RevisionError indicates a revision improperly used for an operation.
|
|
|
|
|
type RevisionError struct {
|
|
|
|
|
Used, Current int
|
2016-04-27 13:48:04 +01:00
|
|
|
}
|
2016-04-05 16:45:32 +01:00
|
|
|
|
2016-04-28 11:38:23 +01:00
|
|
|
func (e *RevisionError) Error() string {
|
2016-04-28 14:00:51 +01:00
|
|
|
if e.Used < 0 || e.Current < 0 {
|
2016-04-28 16:28:27 +01:00
|
|
|
// TODO: message may need tweaking once there's a use.
|
2016-04-28 14:00:51 +01:00
|
|
|
return fmt.Sprintf("assertion revision is unknown")
|
2016-04-28 11:38:23 +01:00
|
|
|
}
|
|
|
|
|
if e.Used == e.Current {
|
|
|
|
|
return fmt.Sprintf("revision %d is already the current revision", e.Used)
|
|
|
|
|
}
|
|
|
|
|
if e.Used < e.Current {
|
|
|
|
|
return fmt.Sprintf("revision %d is older than current revision %d", e.Used, e.Current)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("revision %d is more recent than current revision %d", e.Used, e.Current)
|
2016-04-05 16:45:32 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-09 20:39:42 +01:00
|
|
|
// A RODatabase exposes read-only access to an assertion database.
|
|
|
|
|
type RODatabase interface {
|
2016-07-14 11:56:55 +02:00
|
|
|
// IsTrustedAccount returns whether the account is part of the trusted set.
|
2016-07-14 11:50:55 +02:00
|
|
|
IsTrustedAccount(accountID string) bool
|
2016-02-09 20:39:42 +01:00
|
|
|
// Find an assertion based on arbitrary headers.
|
|
|
|
|
// Provided headers must contain the primary key for the assertion type.
|
|
|
|
|
// It returns ErrNotFound if the assertion cannot be found.
|
|
|
|
|
Find(assertionType *AssertionType, headers map[string]string) (Assertion, error)
|
2016-07-15 12:17:49 +02:00
|
|
|
// FindTrusted finds an assertion in the trusted set based on arbitrary headers.
|
|
|
|
|
// Provided headers must contain the primary key for the assertion type.
|
|
|
|
|
// It returns ErrNotFound if the assertion cannot be found.
|
|
|
|
|
FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error)
|
2016-02-09 20:39:42 +01:00
|
|
|
// FindMany finds assertions based on arbitrary headers.
|
|
|
|
|
// It returns ErrNotFound if no assertion can be found.
|
|
|
|
|
FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error)
|
2016-08-15 19:44:23 +02:00
|
|
|
// Check tests whether the assertion is properly signed and consistent with all the stored knowledge.
|
|
|
|
|
Check(assert Assertion) error
|
2015-11-27 19:26:50 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-09 20:39:42 +01:00
|
|
|
// A Checker defines a check on an assertion considering aspects such as
|
2016-08-11 21:08:31 +02:00
|
|
|
// the signing key, and consistency with other
|
2016-02-09 20:39:42 +01:00
|
|
|
// assertions in the database.
|
2016-08-11 21:08:31 +02:00
|
|
|
type Checker func(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error
|
2016-02-09 20:39:42 +01:00
|
|
|
|
2016-08-30 17:11:17 +01:00
|
|
|
// A NoAuthorityChecker defines a check on a no-authority assertion.
|
2016-09-02 15:10:26 +01:00
|
|
|
type NoAuthorityChecker func(assert Assertion, roDB RODatabase, checkTime time.Time) error
|
2016-08-30 17:11:17 +01:00
|
|
|
|
2015-11-09 15:14:07 +01:00
|
|
|
// Database holds assertions and can be used to sign or check
|
2015-11-09 13:01:37 +01:00
|
|
|
// further assertions.
|
2015-11-09 15:14:07 +01:00
|
|
|
type Database struct {
|
2016-09-02 17:53:29 +01:00
|
|
|
bs Backstore
|
|
|
|
|
keypairMgr KeypairManager
|
|
|
|
|
trusted Backstore
|
|
|
|
|
backstores []Backstore
|
|
|
|
|
checkers []Checker
|
2015-11-09 13:01:37 +01:00
|
|
|
}
|
|
|
|
|
|
2015-11-09 15:14:07 +01:00
|
|
|
// OpenDatabase opens the assertion database based on the configuration.
|
|
|
|
|
func OpenDatabase(cfg *DatabaseConfig) (*Database, error) {
|
2016-01-05 17:29:51 +01:00
|
|
|
bs := cfg.Backstore
|
2015-12-18 11:29:57 +01:00
|
|
|
keypairMgr := cfg.KeypairManager
|
2015-12-17 13:12:17 +01:00
|
|
|
|
2016-01-12 23:13:14 +01:00
|
|
|
if bs == nil {
|
|
|
|
|
bs = nullBackstore{}
|
|
|
|
|
}
|
|
|
|
|
if keypairMgr == nil {
|
2016-08-21 15:52:24 +02:00
|
|
|
keypairMgr = NewMemoryKeypairManager()
|
2015-11-09 13:01:37 +01:00
|
|
|
}
|
2015-12-17 13:12:17 +01:00
|
|
|
|
2016-01-26 20:26:36 +01:00
|
|
|
trustedBackstore := NewMemoryBackstore()
|
|
|
|
|
|
2016-06-10 18:44:15 +02:00
|
|
|
for _, a := range cfg.Trusted {
|
2016-06-13 12:53:50 +02:00
|
|
|
switch accepted := a.(type) {
|
|
|
|
|
case *AccountKey:
|
|
|
|
|
accKey := accepted
|
2016-06-10 18:44:15 +02:00
|
|
|
err := trustedBackstore.Put(AccountKeyType, accKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error loading for use trusted account key %q for %q: %v", accKey.PublicKeyID(), accKey.AccountID(), err)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-13 12:53:50 +02:00
|
|
|
case *Account:
|
|
|
|
|
acct := accepted
|
2016-06-10 18:44:15 +02:00
|
|
|
err := trustedBackstore.Put(AccountType, acct)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error loading for use trusted account %q: %v", acct.DisplayName(), err)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("cannot load trusted assertions that are not account-key or account: %s", a.Type().Name)
|
2016-01-26 20:26:36 +01:00
|
|
|
}
|
2015-12-09 16:25:28 +01:00
|
|
|
}
|
2016-01-26 20:26:36 +01:00
|
|
|
|
2016-02-09 20:39:42 +01:00
|
|
|
checkers := cfg.Checkers
|
|
|
|
|
if len(checkers) == 0 {
|
|
|
|
|
checkers = DefaultCheckers
|
|
|
|
|
}
|
2016-02-10 09:57:32 +01:00
|
|
|
dbCheckers := make([]Checker, len(checkers))
|
|
|
|
|
copy(dbCheckers, checkers)
|
2016-02-09 20:39:42 +01:00
|
|
|
|
2015-12-17 13:12:17 +01:00
|
|
|
return &Database{
|
2016-01-26 20:26:36 +01:00
|
|
|
bs: bs,
|
|
|
|
|
keypairMgr: keypairMgr,
|
|
|
|
|
trusted: trustedBackstore,
|
2016-01-29 15:45:04 +01:00
|
|
|
// order here is relevant, Find* precedence and
|
|
|
|
|
// findAccountKey depend on it, trusted should win over the
|
|
|
|
|
// general backstore!
|
2016-09-02 17:53:29 +01:00
|
|
|
backstores: []Backstore{trustedBackstore, bs},
|
|
|
|
|
checkers: dbCheckers,
|
2015-12-17 13:12:17 +01:00
|
|
|
}, nil
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
2016-08-21 16:51:09 +02:00
|
|
|
// ImportKey stores the given private/public key pair.
|
|
|
|
|
func (db *Database) ImportKey(privKey PrivateKey) error {
|
2016-08-21 16:41:28 +02:00
|
|
|
return db.keypairMgr.Put(privKey)
|
2015-12-01 12:14:06 +01:00
|
|
|
}
|
|
|
|
|
|
2015-12-02 21:23:15 +01:00
|
|
|
var (
|
2016-08-02 16:00:07 +02:00
|
|
|
// for sanity checking of base64 hash strings
|
|
|
|
|
base64HashLike = regexp.MustCompile("^[[:alnum:]_-]*$")
|
2015-12-02 21:23:15 +01:00
|
|
|
)
|
|
|
|
|
|
2016-08-21 16:51:09 +02:00
|
|
|
func (db *Database) safeGetPrivateKey(keyID string) (PrivateKey, error) {
|
2016-01-07 17:56:52 +01:00
|
|
|
if keyID == "" {
|
|
|
|
|
return nil, fmt.Errorf("key id is empty")
|
2015-12-02 21:23:15 +01:00
|
|
|
}
|
2016-08-11 16:49:56 +02:00
|
|
|
if !base64HashLike.MatchString(keyID) {
|
2016-01-07 17:56:52 +01:00
|
|
|
return nil, fmt.Errorf("key id contains unexpected chars: %q", keyID)
|
|
|
|
|
}
|
2016-08-21 16:41:28 +02:00
|
|
|
return db.keypairMgr.Get(keyID)
|
2016-01-07 17:56:52 +01:00
|
|
|
}
|
|
|
|
|
|
2016-08-21 16:51:09 +02:00
|
|
|
// PublicKey returns the public key part of the key pair that has the given key id.
|
|
|
|
|
func (db *Database) PublicKey(keyID string) (PublicKey, error) {
|
|
|
|
|
privKey, err := db.safeGetPrivateKey(keyID)
|
2015-12-01 12:14:06 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2015-11-30 20:19:38 +01:00
|
|
|
return privKey.PublicKey(), nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-07 23:12:14 +01:00
|
|
|
// Sign assembles an assertion with the provided information and signs it
|
2016-01-07 17:56:52 +01:00
|
|
|
// with the private key from `headers["authority-id"]` that has the provided key id.
|
2016-07-27 15:21:36 +02:00
|
|
|
func (db *Database) Sign(assertType *AssertionType, headers map[string]interface{}, body []byte, keyID string) (Assertion, error) {
|
2016-08-21 16:51:09 +02:00
|
|
|
privKey, err := db.safeGetPrivateKey(keyID)
|
2015-12-01 12:14:06 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2016-01-07 23:12:14 +01:00
|
|
|
return assembleAndSign(assertType, headers, body, privKey)
|
2015-12-01 12:14:06 +01:00
|
|
|
}
|
|
|
|
|
|
2016-08-11 16:49:56 +02:00
|
|
|
// findAccountKey finds an AccountKey exactly with account id and key id.
|
2016-01-26 20:26:36 +01:00
|
|
|
func (db *Database) findAccountKey(authorityID, keyID string) (*AccountKey, error) {
|
2016-08-11 16:49:56 +02:00
|
|
|
key := []string{keyID}
|
2016-01-26 20:26:36 +01:00
|
|
|
// consider trusted account keys then disk stored account keys
|
2016-01-27 17:41:05 +01:00
|
|
|
for _, bs := range db.backstores {
|
2016-01-26 20:26:36 +01:00
|
|
|
a, err := bs.Get(AccountKeyType, key)
|
2016-01-29 15:40:25 +01:00
|
|
|
if err == nil {
|
2016-08-02 16:00:07 +02:00
|
|
|
hit := a.(*AccountKey)
|
|
|
|
|
if hit.AccountID() != authorityID {
|
2016-08-11 16:49:56 +02:00
|
|
|
return nil, fmt.Errorf("found public key %q from %q but expected it from: %s", keyID, hit.AccountID(), authorityID)
|
2016-08-02 16:00:07 +02:00
|
|
|
}
|
|
|
|
|
return hit, nil
|
2016-01-29 15:40:25 +01:00
|
|
|
}
|
|
|
|
|
if err != ErrNotFound {
|
2016-01-26 20:26:36 +01:00
|
|
|
return nil, err
|
2015-11-19 14:30:38 +01:00
|
|
|
}
|
|
|
|
|
}
|
2016-01-26 20:26:36 +01:00
|
|
|
return nil, ErrNotFound
|
2015-11-18 18:08:59 +01:00
|
|
|
}
|
|
|
|
|
|
2016-07-14 11:50:55 +02:00
|
|
|
// IsTrustedAccount returns whether the account is part of the trusted set.
|
|
|
|
|
func (db *Database) IsTrustedAccount(accountID string) bool {
|
|
|
|
|
if accountID == "" {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2016-07-07 18:32:57 +02:00
|
|
|
_, err := db.trusted.Get(AccountType, []string{accountID})
|
|
|
|
|
return err == nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-18 18:08:59 +01:00
|
|
|
// Check tests whether the assertion is properly signed and consistent with all the stored knowledge.
|
|
|
|
|
func (db *Database) Check(assert Assertion) error {
|
2016-09-01 16:39:39 +01:00
|
|
|
typ := assert.Type()
|
2016-09-02 17:53:29 +01:00
|
|
|
now := time.Now()
|
|
|
|
|
|
2016-09-01 16:39:39 +01:00
|
|
|
if typ.flags&noAuthority == 0 {
|
|
|
|
|
// TODO: later may need to consider type of assert to find candidate keys
|
|
|
|
|
accKey, err := db.findAccountKey(assert.AuthorityID(), assert.SignKeyID())
|
|
|
|
|
if err == ErrNotFound {
|
|
|
|
|
return fmt.Errorf("no matching public key %q for signature by %q", assert.SignKeyID(), assert.AuthorityID())
|
2016-01-26 20:26:36 +01:00
|
|
|
}
|
2016-08-30 17:11:17 +01:00
|
|
|
if err != nil {
|
2016-09-01 16:39:39 +01:00
|
|
|
return fmt.Errorf("error finding matching public key for signature: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, checker := range db.checkers {
|
|
|
|
|
err := checker(assert, accKey, db, now)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if assert.AuthorityID() != "" {
|
|
|
|
|
return fmt.Errorf("internal error: %q assertion cannot have authority-id set", typ.Name)
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-02 15:04:32 +01:00
|
|
|
for _, checker := range noAuthorityCheckers {
|
2016-09-02 15:10:26 +01:00
|
|
|
err := checker(assert, db, now)
|
2016-09-01 16:39:39 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2016-08-30 17:11:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-24 23:04:43 +01:00
|
|
|
// Add persists the assertion after ensuring it is properly signed and consistent with all the stored knowledge.
|
2015-11-24 19:41:19 +01:00
|
|
|
// It will return an error when trying to add an older revision of the assertion than the one currently stored.
|
2015-11-23 18:28:30 +01:00
|
|
|
func (db *Database) Add(assert Assertion) error {
|
2016-08-19 10:39:22 +02:00
|
|
|
ref := assert.Ref()
|
2016-07-29 18:14:18 +02:00
|
|
|
|
2016-08-19 10:39:22 +02:00
|
|
|
if len(ref.PrimaryKey) == 0 {
|
|
|
|
|
return fmt.Errorf("internal error: assertion type %q has no primary key", ref.Type.Name)
|
2016-07-29 18:14:18 +02:00
|
|
|
}
|
|
|
|
|
|
2016-09-01 16:39:39 +01:00
|
|
|
err := db.Check(assert)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
2016-01-28 11:41:33 +01:00
|
|
|
|
2016-08-19 10:39:22 +02:00
|
|
|
for i, keyVal := range ref.PrimaryKey {
|
2016-01-28 11:41:33 +01:00
|
|
|
if keyVal == "" {
|
2016-08-19 10:39:22 +02:00
|
|
|
return fmt.Errorf("missing or non-string primary key header: %v", ref.Type.PrimaryKey[i])
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
2016-01-27 17:41:05 +01:00
|
|
|
|
2016-01-28 11:41:33 +01:00
|
|
|
// assuming trusted account keys/assertions will be managed
|
|
|
|
|
// through the os snap this seems the safest policy until we
|
|
|
|
|
// know more/better
|
2016-09-01 16:39:39 +01:00
|
|
|
_, err = db.trusted.Get(ref.Type, ref.PrimaryKey)
|
2016-01-28 11:41:33 +01:00
|
|
|
if err != ErrNotFound {
|
2016-08-19 10:39:22 +02:00
|
|
|
return fmt.Errorf("cannot add %q assertion with primary key clashing with a trusted assertion: %v", ref.Type.Name, ref.PrimaryKey)
|
2016-01-28 11:41:33 +01:00
|
|
|
}
|
2016-01-27 17:41:05 +01:00
|
|
|
|
2016-08-19 10:39:22 +02:00
|
|
|
return db.bs.Put(ref.Type, assert)
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
|
|
|
|
|
2015-12-17 13:12:17 +01:00
|
|
|
func searchMatch(assert Assertion, expectedHeaders map[string]string) bool {
|
2015-12-09 18:26:24 +01:00
|
|
|
// check non-primary-key headers as well
|
2015-12-17 13:12:17 +01:00
|
|
|
for expectedKey, expectedValue := range expectedHeaders {
|
2015-12-09 18:26:24 +01:00
|
|
|
if assert.Header(expectedKey) != expectedValue {
|
2015-12-17 13:12:17 +01:00
|
|
|
return false
|
2015-12-09 18:26:24 +01:00
|
|
|
}
|
|
|
|
|
}
|
2015-12-17 13:12:17 +01:00
|
|
|
return true
|
2015-12-09 18:26:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-07-15 12:17:49 +02:00
|
|
|
func find(backstores []Backstore, assertionType *AssertionType, headers map[string]string) (Assertion, error) {
|
2016-01-20 21:18:47 +01:00
|
|
|
err := checkAssertType(assertionType)
|
2015-11-23 18:28:30 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2016-01-20 20:54:07 +01:00
|
|
|
keyValues := make([]string, len(assertionType.PrimaryKey))
|
|
|
|
|
for i, k := range assertionType.PrimaryKey {
|
2015-11-23 18:28:30 +01:00
|
|
|
keyVal := headers[k]
|
|
|
|
|
if keyVal == "" {
|
2015-11-24 12:22:04 +01:00
|
|
|
return nil, fmt.Errorf("must provide primary key: %v", k)
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
2016-01-08 11:19:42 +01:00
|
|
|
keyValues[i] = keyVal
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
2016-01-27 17:41:05 +01:00
|
|
|
|
|
|
|
|
var assert Assertion
|
2016-07-15 12:17:49 +02:00
|
|
|
for _, bs := range backstores {
|
2016-01-27 17:41:05 +01:00
|
|
|
a, err := bs.Get(assertionType, keyValues)
|
2016-01-29 15:40:25 +01:00
|
|
|
if err == nil {
|
2016-01-27 17:41:05 +01:00
|
|
|
assert = a
|
2016-01-29 15:40:25 +01:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if err != ErrNotFound {
|
2016-01-27 17:41:05 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2015-12-17 13:12:17 +01:00
|
|
|
}
|
2016-01-27 17:41:05 +01:00
|
|
|
|
|
|
|
|
if assert == nil || !searchMatch(assert, headers) {
|
2015-12-17 13:12:17 +01:00
|
|
|
return nil, ErrNotFound
|
|
|
|
|
}
|
2016-01-27 17:41:05 +01:00
|
|
|
|
2015-12-17 13:12:17 +01:00
|
|
|
return assert, nil
|
2015-12-09 18:26:24 +01:00
|
|
|
}
|
|
|
|
|
|
2016-07-15 12:17:49 +02:00
|
|
|
// Find an assertion based on arbitrary headers.
|
|
|
|
|
// Provided headers must contain the primary key for the assertion type.
|
|
|
|
|
// It returns ErrNotFound if the assertion cannot be found.
|
|
|
|
|
func (db *Database) Find(assertionType *AssertionType, headers map[string]string) (Assertion, error) {
|
|
|
|
|
return find(db.backstores, assertionType, headers)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FindTrusted finds an assertion in the trusted set based on arbitrary headers.
|
|
|
|
|
// Provided headers must contain the primary key for the assertion type.
|
|
|
|
|
// It returns ErrNotFound if the assertion cannot be found.
|
|
|
|
|
func (db *Database) FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error) {
|
|
|
|
|
return find([]Backstore{db.trusted}, assertionType, headers)
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-09 18:26:24 +01:00
|
|
|
// FindMany finds assertions based on arbitrary headers.
|
|
|
|
|
// It returns ErrNotFound if no assertion can be found.
|
2016-01-20 20:22:00 +01:00
|
|
|
func (db *Database) FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) {
|
2016-01-20 21:18:47 +01:00
|
|
|
err := checkAssertType(assertionType)
|
2015-11-23 18:28:30 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2015-12-09 18:26:24 +01:00
|
|
|
res := []Assertion{}
|
|
|
|
|
|
2015-12-17 13:12:17 +01:00
|
|
|
foundCb := func(assert Assertion) {
|
|
|
|
|
res = append(res, assert)
|
2015-12-09 18:26:24 +01:00
|
|
|
}
|
2016-01-27 17:41:05 +01:00
|
|
|
|
|
|
|
|
for _, bs := range db.backstores {
|
|
|
|
|
err = bs.Search(assertionType, headers, foundCb)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2015-12-09 18:26:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(res) == 0 {
|
|
|
|
|
return nil, ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return res, nil
|
2015-11-23 18:28:30 +01:00
|
|
|
}
|
2016-02-09 20:39:42 +01:00
|
|
|
|
|
|
|
|
// assertion checkers
|
|
|
|
|
|
|
|
|
|
// CheckSigningKeyIsNotExpired checks that the signing key is not expired.
|
2016-08-11 21:08:31 +02:00
|
|
|
func CheckSigningKeyIsNotExpired(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
|
2016-02-09 20:39:42 +01:00
|
|
|
if !signingKey.isKeyValidAt(checkTime) {
|
2016-08-12 18:19:27 +02:00
|
|
|
return fmt.Errorf("assertion is signed with expired public key %q from %q", assert.SignKeyID(), assert.AuthorityID())
|
2016-02-09 20:39:42 +01:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckSignature checks that the signature is valid.
|
2016-08-11 21:08:31 +02:00
|
|
|
func CheckSignature(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
|
|
|
|
|
content, encSig := assert.Signature()
|
|
|
|
|
signature, err := decodeSignature(encSig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = signingKey.publicKey().verify(content, signature)
|
2016-02-09 20:39:42 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed signature verification: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type timestamped interface {
|
|
|
|
|
Timestamp() time.Time
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckTimestampVsSigningKeyValidity verifies that the timestamp of
|
|
|
|
|
// the assertion is within the signing key validity.
|
2016-08-11 21:08:31 +02:00
|
|
|
func CheckTimestampVsSigningKeyValidity(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
|
2016-02-09 20:39:42 +01:00
|
|
|
if tstamped, ok := assert.(timestamped); ok {
|
|
|
|
|
if !signingKey.isKeyValidAt(tstamped.Timestamp()) {
|
|
|
|
|
return fmt.Errorf("%s assertion timestamp outside of signing key validity", assert.Type().Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX: keeping these in this form until we know better
|
|
|
|
|
|
|
|
|
|
// A consistencyChecker performs further checks based on the full
|
|
|
|
|
// assertion database knowledge and its own signing key.
|
|
|
|
|
type consistencyChecker interface {
|
|
|
|
|
checkConsistency(roDB RODatabase, signingKey *AccountKey) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckCrossConsistency verifies that the assertion is consistent with the other statements in the database.
|
2016-08-11 21:08:31 +02:00
|
|
|
func CheckCrossConsistency(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTime time.Time) error {
|
2016-02-09 20:39:42 +01:00
|
|
|
// see if the assertion requires further checks
|
|
|
|
|
if checker, ok := assert.(consistencyChecker); ok {
|
2016-07-07 18:32:57 +02:00
|
|
|
return checker.checkConsistency(roDB, signingKey)
|
2016-02-09 20:39:42 +01:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DefaultCheckers lists the default and recommended assertion
|
|
|
|
|
// checkers used by Database if none are specified in the
|
|
|
|
|
// DatabaseConfig.Checkers.
|
|
|
|
|
var DefaultCheckers = []Checker{
|
|
|
|
|
CheckSigningKeyIsNotExpired,
|
|
|
|
|
CheckSignature,
|
|
|
|
|
CheckTimestampVsSigningKeyValidity,
|
|
|
|
|
CheckCrossConsistency,
|
|
|
|
|
}
|
2016-08-30 17:11:17 +01:00
|
|
|
|
|
|
|
|
// no-authority assertion checkers
|
|
|
|
|
|
|
|
|
|
// NoAuthorityCheckSignature checks that the signature is valid.
|
2016-09-02 15:10:26 +01:00
|
|
|
func NoAuthorityCheckSignature(assert Assertion, roDB RODatabase, checkTime time.Time) error {
|
2016-09-01 19:57:51 +01:00
|
|
|
selfSigned, ok := assert.(selfSignedAssertion)
|
|
|
|
|
if !ok {
|
2016-09-02 15:13:18 +01:00
|
|
|
panic(fmt.Errorf("cannot check non-self-signed assertion type %q", assert.Type().Name))
|
2016-09-01 19:57:51 +01:00
|
|
|
}
|
|
|
|
|
signingKey := selfSigned.signKey()
|
2016-08-30 17:11:17 +01:00
|
|
|
content, encSig := assert.Signature()
|
|
|
|
|
signature, err := decodeSignature(encSig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
err = signingKey.verify(content, signature)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed signature verification: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A noAuthorityConsistencyChecker performs further checks based on the full
|
|
|
|
|
// assertion database knowledge and its own signing key.
|
|
|
|
|
type noAuthorityConsistencyChecker interface {
|
2016-09-01 19:57:51 +01:00
|
|
|
noAuthorityCheckConsistency(roDB RODatabase) error
|
2016-08-30 17:11:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NoAuthorityCheckCrossConsistency verifies that the assertion is consistent with the other statements in the database.
|
2016-09-02 15:10:26 +01:00
|
|
|
func NoAuthorityCheckCrossConsistency(assert Assertion, roDB RODatabase, checkTime time.Time) error {
|
2016-08-30 17:11:17 +01:00
|
|
|
// see if the assertion requires further checks
|
|
|
|
|
if checker, ok := assert.(noAuthorityConsistencyChecker); ok {
|
2016-09-01 19:57:51 +01:00
|
|
|
return checker.noAuthorityCheckConsistency(roDB)
|
2016-08-30 17:11:17 +01:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// noAuthorityCheckers lists the assertion checkers used by Database.
|
|
|
|
|
var noAuthorityCheckers = []NoAuthorityChecker{
|
|
|
|
|
NoAuthorityCheckSignature,
|
|
|
|
|
NoAuthorityCheckCrossConsistency,
|
|
|
|
|
}
|