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 }