github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/asserts/database.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2021 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 // SequenceMemberAfter returns for a sequence-forming assertType the 69 // first assertion in the sequence under the given sequenceKey 70 // with sequence number larger than after. If after==-1 it 71 // returns the assertion with largest sequence number. If none 72 // exists it returns a NotFoundError, usually with omitted 73 // Headers. If assertType is not sequence-forming it can 74 // panic. 75 SequenceMemberAfter(assertType *AssertionType, sequenceKey []string, after, maxFormat int) (SequenceMember, error) 76 } 77 78 type nullBackstore struct{} 79 80 func (nbs nullBackstore) Put(t *AssertionType, a Assertion) error { 81 return fmt.Errorf("cannot store assertions without setting a proper assertion backstore implementation") 82 } 83 84 func (nbs nullBackstore) Get(t *AssertionType, k []string, maxFormat int) (Assertion, error) { 85 return nil, &NotFoundError{Type: t} 86 } 87 88 func (nbs nullBackstore) Search(t *AssertionType, h map[string]string, f func(Assertion), maxFormat int) error { 89 return nil 90 } 91 92 func (nbs nullBackstore) SequenceMemberAfter(t *AssertionType, kp []string, after, maxFormat int) (SequenceMember, error) { 93 return nil, &NotFoundError{Type: t} 94 } 95 96 // A KeypairManager is a manager and backstore for private/public key pairs. 97 type KeypairManager interface { 98 // Put stores the given private/public key pair, 99 // making sure it can be later retrieved by its unique key id with Get. 100 // Trying to store a key with an already present key id should 101 // result in an error. 102 Put(privKey PrivateKey) error 103 // Get returns the private/public key pair with the given key id. 104 Get(keyID string) (PrivateKey, error) 105 } 106 107 // DatabaseConfig for an assertion database. 108 type DatabaseConfig struct { 109 // trusted set of assertions (account and account-key supported), 110 // used to establish root keys and trusted authorities 111 Trusted []Assertion 112 // predefined assertions but that do not establish foundational trust 113 OtherPredefined []Assertion 114 // backstore for assertions, left unset storing assertions will error 115 Backstore Backstore 116 // manager/backstore for keypairs, defaults to in-memory implementation 117 KeypairManager KeypairManager 118 // assertion checkers used by Database.Check, left unset DefaultCheckers will be used which is recommended 119 Checkers []Checker 120 } 121 122 // RevisionError indicates a revision improperly used for an operation. 123 type RevisionError struct { 124 Used, Current int 125 } 126 127 func (e *RevisionError) Error() string { 128 if e.Used < 0 || e.Current < 0 { 129 // TODO: message may need tweaking once there's a use. 130 return "assertion revision is unknown" 131 } 132 if e.Used == e.Current { 133 return fmt.Sprintf("revision %d is already the current revision", e.Used) 134 } 135 if e.Used < e.Current { 136 return fmt.Sprintf("revision %d is older than current revision %d", e.Used, e.Current) 137 } 138 return fmt.Sprintf("revision %d is more recent than current revision %d", e.Used, e.Current) 139 } 140 141 // UnsupportedFormatError indicates an assertion with a format iteration not yet supported by the present version of asserts. 142 type UnsupportedFormatError struct { 143 Ref *Ref 144 Format int 145 // Update marks there was already a current revision of the assertion and it has been kept. 146 Update bool 147 } 148 149 func (e *UnsupportedFormatError) Error() string { 150 postfx := "" 151 if e.Update { 152 postfx = " (current not updated)" 153 } 154 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) 155 } 156 157 // IsUnaccceptedUpdate returns whether the error indicates that an 158 // assertion revision was already present and has been kept because 159 // the update was not accepted. 160 func IsUnaccceptedUpdate(err error) bool { 161 switch x := err.(type) { 162 case *UnsupportedFormatError: 163 return x.Update 164 case *RevisionError: 165 return x.Used <= x.Current 166 } 167 return false 168 } 169 170 // A RODatabase exposes read-only access to an assertion database. 171 type RODatabase interface { 172 // IsTrustedAccount returns whether the account is part of the trusted set. 173 IsTrustedAccount(accountID string) bool 174 // Find an assertion based on arbitrary headers. 175 // Provided headers must contain the primary key for the assertion type. 176 // It returns a NotFoundError if the assertion cannot be found. 177 Find(assertionType *AssertionType, headers map[string]string) (Assertion, error) 178 // FindPredefined finds an assertion in the predefined sets 179 // (trusted or not) based on arbitrary headers. Provided 180 // headers must contain the primary key for the assertion 181 // type. It returns a NotFoundError if the assertion cannot 182 // be found. 183 FindPredefined(assertionType *AssertionType, headers map[string]string) (Assertion, error) 184 // FindTrusted finds an assertion in the trusted set based on 185 // arbitrary headers. Provided headers must contain the 186 // primary key for the assertion type. It returns a 187 // NotFoundError if the assertion cannot be found. 188 FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error) 189 // FindMany finds assertions based on arbitrary headers. 190 // It returns a NotFoundError if no assertion can be found. 191 FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) 192 // FindManyPredefined finds assertions in the predefined sets 193 // (trusted or not) based on arbitrary headers. It returns a 194 // NotFoundError if no assertion can be found. 195 FindManyPredefined(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) 196 // FindSequence finds an assertion for the given headers and after for 197 // a sequence-forming type. 198 // The provided headers must contain a sequence key, i.e. a prefix of 199 // the primary key for the assertion type except for the sequence 200 // number header. 201 // The assertion is the first in the sequence under the sequence key 202 // with sequence number > after. 203 // If after is -1 it returns instead the assertion with the largest 204 // sequence number. 205 // It will constraint itself to assertions with format <= maxFormat 206 // unless maxFormat is -1. 207 // It returns a NotFoundError if the assertion cannot be found. 208 FindSequence(assertType *AssertionType, sequenceHeaders map[string]string, after, maxFormat int) (SequenceMember, error) 209 // Check tests whether the assertion is properly signed and consistent with all the stored knowledge. 210 Check(assert Assertion) error 211 } 212 213 // A Checker defines a check on an assertion considering aspects such as 214 // the signing key, and consistency with other 215 // assertions in the database. 216 type Checker func(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTimeEarliest, checkTimeLatest time.Time) error 217 218 // Database holds assertions and can be used to sign or check 219 // further assertions. 220 type Database struct { 221 bs Backstore 222 keypairMgr KeypairManager 223 224 trusted Backstore 225 predefined Backstore 226 // all backstores to consider for find 227 backstores []Backstore 228 // backstores of dbs this was built on by stacking 229 stackedOn []Backstore 230 231 checkers []Checker 232 earliestTime time.Time 233 } 234 235 // OpenDatabase opens the assertion database based on the configuration. 236 func OpenDatabase(cfg *DatabaseConfig) (*Database, error) { 237 bs := cfg.Backstore 238 keypairMgr := cfg.KeypairManager 239 240 if bs == nil { 241 bs = nullBackstore{} 242 } 243 if keypairMgr == nil { 244 keypairMgr = NewMemoryKeypairManager() 245 } 246 247 trustedBackstore := NewMemoryBackstore() 248 249 for _, a := range cfg.Trusted { 250 switch accepted := a.(type) { 251 case *AccountKey: 252 accKey := accepted 253 err := trustedBackstore.Put(AccountKeyType, accKey) 254 if err != nil { 255 return nil, fmt.Errorf("cannot predefine trusted account key %q for %q: %v", accKey.PublicKeyID(), accKey.AccountID(), err) 256 } 257 258 case *Account: 259 acct := accepted 260 err := trustedBackstore.Put(AccountType, acct) 261 if err != nil { 262 return nil, fmt.Errorf("cannot predefine trusted account %q: %v", acct.DisplayName(), err) 263 } 264 default: 265 return nil, fmt.Errorf("cannot predefine trusted assertions that are not account-key or account: %s", a.Type().Name) 266 } 267 } 268 269 otherPredefinedBackstore := NewMemoryBackstore() 270 271 for _, a := range cfg.OtherPredefined { 272 err := otherPredefinedBackstore.Put(a.Type(), a) 273 if err != nil { 274 return nil, fmt.Errorf("cannot predefine assertion %v: %v", a.Ref(), err) 275 } 276 } 277 278 checkers := cfg.Checkers 279 if len(checkers) == 0 { 280 checkers = DefaultCheckers 281 } 282 dbCheckers := make([]Checker, len(checkers)) 283 copy(dbCheckers, checkers) 284 285 return &Database{ 286 bs: bs, 287 keypairMgr: keypairMgr, 288 trusted: trustedBackstore, 289 predefined: otherPredefinedBackstore, 290 // order here is relevant, Find* precedence and 291 // findAccountKey depend on it, trusted should win over the 292 // general backstore! 293 backstores: []Backstore{trustedBackstore, otherPredefinedBackstore, bs}, 294 checkers: dbCheckers, 295 }, nil 296 } 297 298 // WithStackedBackstore returns a new database that adds to the given backstore 299 // only but finds in backstore and the base database backstores and 300 // cross-checks against all of them. 301 // This is useful to cross-check a set of assertions without adding 302 // them to the database. 303 func (db *Database) WithStackedBackstore(backstore Backstore) *Database { 304 // original bs goes in front of stacked-on ones 305 stackedOn := []Backstore{db.bs} 306 stackedOn = append(stackedOn, db.stackedOn...) 307 // find order: trusted, predefined, new backstore, stacked-on ones 308 backstores := []Backstore{db.trusted, db.predefined} 309 backstores = append(backstores, backstore) 310 backstores = append(backstores, stackedOn...) 311 return &Database{ 312 bs: backstore, 313 keypairMgr: db.keypairMgr, 314 trusted: db.trusted, 315 predefined: db.predefined, 316 backstores: backstores, 317 stackedOn: stackedOn, 318 checkers: db.checkers, 319 } 320 } 321 322 // ImportKey stores the given private/public key pair. 323 func (db *Database) ImportKey(privKey PrivateKey) error { 324 return db.keypairMgr.Put(privKey) 325 } 326 327 var ( 328 // for sanity checking of base64 hash strings 329 base64HashLike = regexp.MustCompile("^[[:alnum:]_-]*$") 330 ) 331 332 func (db *Database) safeGetPrivateKey(keyID string) (PrivateKey, error) { 333 if keyID == "" { 334 return nil, fmt.Errorf("key id is empty") 335 } 336 if !base64HashLike.MatchString(keyID) { 337 return nil, fmt.Errorf("key id contains unexpected chars: %q", keyID) 338 } 339 return db.keypairMgr.Get(keyID) 340 } 341 342 // PublicKey returns the public key part of the key pair that has the given key id. 343 func (db *Database) PublicKey(keyID string) (PublicKey, error) { 344 privKey, err := db.safeGetPrivateKey(keyID) 345 if err != nil { 346 return nil, err 347 } 348 return privKey.PublicKey(), nil 349 } 350 351 // Sign assembles an assertion with the provided information and signs it 352 // with the private key from `headers["authority-id"]` that has the provided key id. 353 func (db *Database) Sign(assertType *AssertionType, headers map[string]interface{}, body []byte, keyID string) (Assertion, error) { 354 privKey, err := db.safeGetPrivateKey(keyID) 355 if err != nil { 356 return nil, err 357 } 358 return assembleAndSign(assertType, headers, body, privKey) 359 } 360 361 // findAccountKey finds an AccountKey exactly with account id and key id. 362 func (db *Database) findAccountKey(authorityID, keyID string) (*AccountKey, error) { 363 key := []string{keyID} 364 // consider trusted account keys then disk stored account keys 365 for _, bs := range db.backstores { 366 a, err := bs.Get(AccountKeyType, key, AccountKeyType.MaxSupportedFormat()) 367 if err == nil { 368 hit := a.(*AccountKey) 369 if hit.AccountID() != authorityID { 370 return nil, fmt.Errorf("found public key %q from %q but expected it from: %s", keyID, hit.AccountID(), authorityID) 371 } 372 return hit, nil 373 } 374 if !IsNotFound(err) { 375 return nil, err 376 } 377 } 378 return nil, &NotFoundError{Type: AccountKeyType} 379 } 380 381 // IsTrustedAccount returns whether the account is part of the trusted set. 382 func (db *Database) IsTrustedAccount(accountID string) bool { 383 if accountID == "" { 384 return false 385 } 386 _, err := db.trusted.Get(AccountType, []string{accountID}, AccountType.MaxSupportedFormat()) 387 return err == nil 388 } 389 390 var timeNow = time.Now 391 392 // SetEarliestTime affects how key expiration is checked. 393 // Instead of considering current system time, only assume that current time 394 // is >= earliest. If earliest is zero reset to considering current system time. 395 func (db *Database) SetEarliestTime(earliest time.Time) { 396 db.earliestTime = earliest 397 } 398 399 // Check tests whether the assertion is properly signed and consistent with all the stored knowledge. 400 func (db *Database) Check(assert Assertion) error { 401 if !assert.SupportedFormat() { 402 return &UnsupportedFormatError{Ref: assert.Ref(), Format: assert.Format()} 403 } 404 405 typ := assert.Type() 406 // assume current time is >= earliestTime and <= latestTime 407 earliestTime := db.earliestTime 408 var latestTime time.Time 409 if earliestTime.IsZero() { 410 // use the current system time by setting both to it 411 earliestTime = timeNow() 412 latestTime = earliestTime 413 } 414 415 var accKey *AccountKey 416 var err error 417 if typ.flags&noAuthority == 0 { 418 // TODO: later may need to consider type of assert to find candidate keys 419 accKey, err = db.findAccountKey(assert.AuthorityID(), assert.SignKeyID()) 420 if IsNotFound(err) { 421 return fmt.Errorf("no matching public key %q for signature by %q", assert.SignKeyID(), assert.AuthorityID()) 422 } 423 if err != nil { 424 return fmt.Errorf("error finding matching public key for signature: %v", err) 425 } 426 } else { 427 if assert.AuthorityID() != "" { 428 return fmt.Errorf("internal error: %q assertion cannot have authority-id set", typ.Name) 429 } 430 } 431 432 for _, checker := range db.checkers { 433 err := checker(assert, accKey, db, earliestTime, latestTime) 434 if err != nil { 435 return err 436 } 437 } 438 439 return nil 440 } 441 442 // Add persists the assertion after ensuring it is properly signed and consistent with all the stored knowledge. 443 // It will return an error when trying to add an older revision of the assertion than the one currently stored. 444 func (db *Database) Add(assert Assertion) error { 445 ref := assert.Ref() 446 447 if len(ref.PrimaryKey) == 0 { 448 return fmt.Errorf("internal error: assertion type %q has no primary key", ref.Type.Name) 449 } 450 451 err := db.Check(assert) 452 if err != nil { 453 if ufe, ok := err.(*UnsupportedFormatError); ok { 454 _, err := ref.Resolve(db.Find) 455 if err != nil && !IsNotFound(err) { 456 return err 457 } 458 return &UnsupportedFormatError{Ref: ufe.Ref, Format: ufe.Format, Update: err == nil} 459 } 460 return err 461 } 462 463 for i, keyVal := range ref.PrimaryKey { 464 if keyVal == "" { 465 return fmt.Errorf("missing or non-string primary key header: %v", ref.Type.PrimaryKey[i]) 466 } 467 } 468 469 // assuming trusted account keys/assertions will be managed 470 // through the os snap this seems the safest policy until we 471 // know more/better 472 _, err = db.trusted.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat()) 473 if !IsNotFound(err) { 474 return fmt.Errorf("cannot add %q assertion with primary key clashing with a trusted assertion: %v", ref.Type.Name, ref.PrimaryKey) 475 } 476 477 _, err = db.predefined.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat()) 478 if !IsNotFound(err) { 479 return fmt.Errorf("cannot add %q assertion with primary key clashing with a predefined assertion: %v", ref.Type.Name, ref.PrimaryKey) 480 } 481 482 // this is non empty only in the stacked case 483 if len(db.stackedOn) != 0 { 484 headers, err := HeadersFromPrimaryKey(ref.Type, ref.PrimaryKey) 485 if err != nil { 486 return fmt.Errorf("internal error: HeadersFromPrimaryKey for %q failed on prechecked data: %s", ref.Type.Name, ref.PrimaryKey) 487 } 488 cur, err := find(db.stackedOn, ref.Type, headers, -1) 489 if err == nil { 490 curRev := cur.Revision() 491 rev := assert.Revision() 492 if curRev >= rev { 493 return &RevisionError{Current: curRev, Used: rev} 494 } 495 } else if !IsNotFound(err) { 496 return err 497 } 498 } 499 500 return db.bs.Put(ref.Type, assert) 501 } 502 503 func searchMatch(assert Assertion, expectedHeaders map[string]string) bool { 504 // check non-primary-key headers as well 505 for expectedKey, expectedValue := range expectedHeaders { 506 if assert.Header(expectedKey) != expectedValue { 507 return false 508 } 509 } 510 return true 511 } 512 513 func find(backstores []Backstore, assertionType *AssertionType, headers map[string]string, maxFormat int) (Assertion, error) { 514 err := checkAssertType(assertionType) 515 if err != nil { 516 return nil, err 517 } 518 maxSupp := assertionType.MaxSupportedFormat() 519 if maxFormat == -1 { 520 maxFormat = maxSupp 521 } else { 522 if maxFormat > maxSupp { 523 return nil, fmt.Errorf("cannot find %q assertions for format %d higher than supported format %d", assertionType.Name, maxFormat, maxSupp) 524 } 525 } 526 527 keyValues, err := PrimaryKeyFromHeaders(assertionType, headers) 528 if err != nil { 529 return nil, err 530 } 531 532 var assert Assertion 533 for _, bs := range backstores { 534 a, err := bs.Get(assertionType, keyValues, maxFormat) 535 if err == nil { 536 assert = a 537 break 538 } 539 if !IsNotFound(err) { 540 return nil, err 541 } 542 } 543 544 if assert == nil || !searchMatch(assert, headers) { 545 return nil, &NotFoundError{Type: assertionType, Headers: headers} 546 } 547 548 return assert, nil 549 } 550 551 // Find an assertion based on arbitrary headers. 552 // Provided headers must contain the primary key for the assertion type. 553 // It returns a NotFoundError if the assertion cannot be found. 554 func (db *Database) Find(assertionType *AssertionType, headers map[string]string) (Assertion, error) { 555 return find(db.backstores, assertionType, headers, -1) 556 } 557 558 // FindMaxFormat finds an assertion like Find but such that its 559 // format is <= maxFormat by passing maxFormat along to the backend. 560 // It returns a NotFoundError if such an assertion cannot be found. 561 func (db *Database) FindMaxFormat(assertionType *AssertionType, headers map[string]string, maxFormat int) (Assertion, error) { 562 return find(db.backstores, assertionType, headers, maxFormat) 563 } 564 565 // FindPredefined finds an assertion in the predefined sets (trusted 566 // or not) based on arbitrary headers. Provided headers must contain 567 // the primary key for the assertion type. It returns a NotFoundError 568 // if the assertion cannot be found. 569 func (db *Database) FindPredefined(assertionType *AssertionType, headers map[string]string) (Assertion, error) { 570 return find([]Backstore{db.trusted, db.predefined}, assertionType, headers, -1) 571 } 572 573 // FindTrusted finds an assertion in the trusted set based on arbitrary headers. 574 // Provided headers must contain the primary key for the assertion type. 575 // It returns a NotFoundError if the assertion cannot be found. 576 func (db *Database) FindTrusted(assertionType *AssertionType, headers map[string]string) (Assertion, error) { 577 return find([]Backstore{db.trusted}, assertionType, headers, -1) 578 } 579 580 func (db *Database) findMany(backstores []Backstore, assertionType *AssertionType, headers map[string]string) ([]Assertion, error) { 581 err := checkAssertType(assertionType) 582 if err != nil { 583 return nil, err 584 } 585 res := []Assertion{} 586 587 foundCb := func(assert Assertion) { 588 res = append(res, assert) 589 } 590 591 // TODO: Find variant taking this 592 maxFormat := assertionType.MaxSupportedFormat() 593 for _, bs := range backstores { 594 err = bs.Search(assertionType, headers, foundCb, maxFormat) 595 if err != nil { 596 return nil, err 597 } 598 } 599 600 if len(res) == 0 { 601 return nil, &NotFoundError{Type: assertionType, Headers: headers} 602 } 603 return res, nil 604 } 605 606 // FindMany finds assertions based on arbitrary headers. 607 // It returns a NotFoundError if no assertion can be found. 608 func (db *Database) FindMany(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) { 609 return db.findMany(db.backstores, assertionType, headers) 610 } 611 612 // FindManyPrefined finds assertions in the predefined sets (trusted 613 // or not) based on arbitrary headers. It returns a NotFoundError if 614 // no assertion can be found. 615 func (db *Database) FindManyPredefined(assertionType *AssertionType, headers map[string]string) ([]Assertion, error) { 616 return db.findMany([]Backstore{db.trusted, db.predefined}, assertionType, headers) 617 } 618 619 // FindSequence finds an assertion for the given headers and after for 620 // a sequence-forming type. 621 // The provided headers must contain a sequence key, i.e. a prefix of 622 // the primary key for the assertion type except for the sequence 623 // number header. 624 // The assertion is the first in the sequence under the sequence key 625 // with sequence number > after. 626 // If after is -1 it returns instead the assertion with the largest 627 // sequence number. 628 // It will constraint itself to assertions with format <= maxFormat 629 // unless maxFormat is -1. 630 // It returns a NotFoundError if the assertion cannot be found. 631 func (db *Database) FindSequence(assertType *AssertionType, sequenceHeaders map[string]string, after, maxFormat int) (SequenceMember, error) { 632 err := checkAssertType(assertType) 633 if err != nil { 634 return nil, err 635 } 636 if !assertType.SequenceForming() { 637 return nil, fmt.Errorf("cannot use FindSequence with non sequence-forming assertion type %q", assertType.Name) 638 } 639 maxSupp := assertType.MaxSupportedFormat() 640 if maxFormat == -1 { 641 maxFormat = maxSupp 642 } else { 643 if maxFormat > maxSupp { 644 return nil, fmt.Errorf("cannot find %q assertions for format %d higher than supported format %d", assertType.Name, maxFormat, maxSupp) 645 } 646 } 647 648 // form the sequence key using all keys but the last one which 649 // is the sequence number 650 seqKey, err := keysFromHeaders(assertType.PrimaryKey[:len(assertType.PrimaryKey)-1], sequenceHeaders) 651 if err != nil { 652 return nil, err 653 } 654 655 // find the better result across backstores' results 656 better := func(cur, a SequenceMember) SequenceMember { 657 if cur == nil { 658 return a 659 } 660 curSeq := cur.Sequence() 661 aSeq := a.Sequence() 662 if after == -1 { 663 if aSeq > curSeq { 664 return a 665 } 666 } else { 667 if aSeq < curSeq { 668 return a 669 } 670 } 671 return cur 672 } 673 674 var assert SequenceMember 675 for _, bs := range db.backstores { 676 a, err := bs.SequenceMemberAfter(assertType, seqKey, after, maxFormat) 677 if err == nil { 678 assert = better(assert, a) 679 continue 680 } 681 if !IsNotFound(err) { 682 return nil, err 683 } 684 } 685 686 if assert != nil { 687 return assert, nil 688 } 689 690 return nil, &NotFoundError{Type: assertType, Headers: sequenceHeaders} 691 } 692 693 // assertion checkers 694 695 // CheckSigningKeyIsNotExpired checks that the signing key is not expired. 696 func CheckSigningKeyIsNotExpired(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTimeEarliest, checkTimeLatest time.Time) error { 697 if signingKey == nil { 698 // assert isn't signed with an account-key key, CheckSignature 699 // will fail anyway unless we teach it more stuff, 700 // Also this check isn't so relevant for self-signed asserts 701 // (e.g. account-key-request) 702 return nil 703 } 704 if !signingKey.isKeyValidAssumingCurTimeWithin(checkTimeEarliest, checkTimeLatest) { 705 return fmt.Errorf("assertion is signed with expired public key %q from %q", assert.SignKeyID(), assert.AuthorityID()) 706 } 707 return nil 708 } 709 710 // CheckSignature checks that the signature is valid. 711 func CheckSignature(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTimeEarliest, checkTimeLatest time.Time) error { 712 var pubKey PublicKey 713 if signingKey != nil { 714 pubKey = signingKey.publicKey() 715 if assert.AuthorityID() != signingKey.AccountID() { 716 return fmt.Errorf("assertion authority %q does not match public key from %q", assert.AuthorityID(), signingKey.AccountID()) 717 } 718 } else { 719 custom, ok := assert.(customSigner) 720 if !ok { 721 return fmt.Errorf("cannot check no-authority assertion type %q", assert.Type().Name) 722 } 723 pubKey = custom.signKey() 724 } 725 content, encSig := assert.Signature() 726 signature, err := decodeSignature(encSig) 727 if err != nil { 728 return err 729 } 730 err = pubKey.verify(content, signature) 731 if err != nil { 732 return fmt.Errorf("failed signature verification: %v", err) 733 } 734 return nil 735 } 736 737 type timestamped interface { 738 Timestamp() time.Time 739 } 740 741 // CheckTimestampVsSigningKeyValidity verifies that the timestamp of 742 // the assertion is within the signing key validity. 743 func CheckTimestampVsSigningKeyValidity(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTimeEarliest, checkTimeLatest time.Time) error { 744 if signingKey == nil { 745 // assert isn't signed with an account-key key, CheckSignature 746 // will fail anyway unless we teach it more stuff. 747 // Also this check isn't so relevant for self-signed asserts 748 // (e.g. account-key-request) 749 return nil 750 } 751 if tstamped, ok := assert.(timestamped); ok { 752 checkTime := tstamped.Timestamp() 753 if !signingKey.isKeyValidAt(checkTime) { 754 until := "" 755 if !signingKey.Until().IsZero() { 756 until = fmt.Sprintf(" until %q", signingKey.Until()) 757 } 758 return fmt.Errorf("%s assertion timestamp %q outside of signing key validity (key valid since %q%s)", 759 assert.Type().Name, checkTime, signingKey.Since(), until) 760 } 761 } 762 return nil 763 } 764 765 // XXX: keeping these in this form until we know better 766 767 // A consistencyChecker performs further checks based on the full 768 // assertion database knowledge and its own signing key. 769 type consistencyChecker interface { 770 checkConsistency(roDB RODatabase, signingKey *AccountKey) error 771 } 772 773 // CheckCrossConsistency verifies that the assertion is consistent with the other statements in the database. 774 func CheckCrossConsistency(assert Assertion, signingKey *AccountKey, roDB RODatabase, checkTimeEarliest, checkTimeLatest time.Time) error { 775 // see if the assertion requires further checks 776 if checker, ok := assert.(consistencyChecker); ok { 777 return checker.checkConsistency(roDB, signingKey) 778 } 779 return nil 780 } 781 782 // DefaultCheckers lists the default and recommended assertion 783 // checkers used by Database if none are specified in the 784 // DatabaseConfig.Checkers. 785 var DefaultCheckers = []Checker{ 786 CheckSigningKeyIsNotExpired, 787 CheckSignature, 788 CheckTimestampVsSigningKeyValidity, 789 CheckCrossConsistency, 790 }