gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/asserts/account_key_test.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_test 21 22 import ( 23 "encoding/base64" 24 "fmt" 25 "path/filepath" 26 "strings" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/asserts" 32 "github.com/snapcore/snapd/asserts/assertstest" 33 ) 34 35 type accountKeySuite struct { 36 privKey asserts.PrivateKey 37 pubKeyBody string 38 keyID string 39 since, until time.Time 40 sinceLine, untilLine string 41 } 42 43 var _ = Suite(&accountKeySuite{}) 44 45 func (aks *accountKeySuite) SetUpSuite(c *C) { 46 cfg1 := &asserts.DatabaseConfig{} 47 accDb, err := asserts.OpenDatabase(cfg1) 48 c.Assert(err, IsNil) 49 aks.privKey = testPrivKey1 50 err = accDb.ImportKey(aks.privKey) 51 c.Assert(err, IsNil) 52 aks.keyID = aks.privKey.PublicKey().ID() 53 54 pubKey, err := accDb.PublicKey(aks.keyID) 55 c.Assert(err, IsNil) 56 pubKeyEncoded, err := asserts.EncodePublicKey(pubKey) 57 c.Assert(err, IsNil) 58 aks.pubKeyBody = string(pubKeyEncoded) 59 60 aks.since, err = time.Parse(time.RFC822, "16 Nov 15 15:04 UTC") 61 c.Assert(err, IsNil) 62 aks.until = aks.since.AddDate(1, 0, 0) 63 aks.sinceLine = "since: " + aks.since.Format(time.RFC3339) + "\n" 64 aks.untilLine = "until: " + aks.until.Format(time.RFC3339) + "\n" 65 } 66 67 func (aks *accountKeySuite) TestDecodeOK(c *C) { 68 encoded := "type: account-key\n" + 69 "authority-id: canonical\n" + 70 "account-id: acc-id1\n" + 71 "name: default\n" + 72 "public-key-sha3-384: " + aks.keyID + "\n" + 73 aks.sinceLine + 74 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 75 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 76 aks.pubKeyBody + "\n\n" + 77 "AXNpZw==" 78 a, err := asserts.Decode([]byte(encoded)) 79 c.Assert(err, IsNil) 80 c.Check(a.Type(), Equals, asserts.AccountKeyType) 81 accKey := a.(*asserts.AccountKey) 82 c.Check(accKey.AccountID(), Equals, "acc-id1") 83 c.Check(accKey.Name(), Equals, "default") 84 c.Check(accKey.PublicKeyID(), Equals, aks.keyID) 85 c.Check(accKey.Since(), Equals, aks.since) 86 } 87 88 func (aks *accountKeySuite) TestDecodeNoName(c *C) { 89 // XXX: remove this test once name is mandatory 90 encoded := "type: account-key\n" + 91 "authority-id: canonical\n" + 92 "account-id: acc-id1\n" + 93 "public-key-sha3-384: " + aks.keyID + "\n" + 94 aks.sinceLine + 95 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 96 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 97 aks.pubKeyBody + "\n\n" + 98 "AXNpZw==" 99 a, err := asserts.Decode([]byte(encoded)) 100 c.Assert(err, IsNil) 101 c.Check(a.Type(), Equals, asserts.AccountKeyType) 102 accKey := a.(*asserts.AccountKey) 103 c.Check(accKey.AccountID(), Equals, "acc-id1") 104 c.Check(accKey.Name(), Equals, "") 105 c.Check(accKey.PublicKeyID(), Equals, aks.keyID) 106 c.Check(accKey.Since(), Equals, aks.since) 107 } 108 109 func (aks *accountKeySuite) TestUntil(c *C) { 110 111 untilSinceLine := "until: " + aks.since.Format(time.RFC3339) + "\n" 112 113 tests := []struct { 114 untilLine string 115 until time.Time 116 }{ 117 {"", time.Time{}}, // zero time default 118 {aks.untilLine, aks.until}, // in the future 119 {untilSinceLine, aks.since}, // same as since 120 } 121 122 for _, test := range tests { 123 c.Log(test) 124 encoded := "type: account-key\n" + 125 "authority-id: canonical\n" + 126 "account-id: acc-id1\n" + 127 "name: default\n" + 128 "public-key-sha3-384: " + aks.keyID + "\n" + 129 aks.sinceLine + 130 test.untilLine + 131 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 132 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 133 aks.pubKeyBody + "\n\n" + 134 "openpgp c2ln" 135 a, err := asserts.Decode([]byte(encoded)) 136 c.Assert(err, IsNil) 137 accKey := a.(*asserts.AccountKey) 138 c.Check(accKey.Until(), Equals, test.until) 139 } 140 } 141 142 const ( 143 accKeyErrPrefix = "assertion account-key: " 144 accKeyReqErrPrefix = "assertion account-key-request: " 145 ) 146 147 func (aks *accountKeySuite) TestDecodeInvalidHeaders(c *C) { 148 149 encoded := "type: account-key\n" + 150 "authority-id: canonical\n" + 151 "account-id: acc-id1\n" + 152 "name: default\n" + 153 "public-key-sha3-384: " + aks.keyID + "\n" + 154 aks.sinceLine + 155 aks.untilLine + 156 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 157 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 158 aks.pubKeyBody + "\n\n" + 159 "AXNpZw==" 160 161 untilPast := aks.since.AddDate(-1, 0, 0) 162 untilPastLine := "until: " + untilPast.Format(time.RFC3339) + "\n" 163 164 invalidHeaderTests := []struct{ original, invalid, expectedErr string }{ 165 {"account-id: acc-id1\n", "", `"account-id" header is mandatory`}, 166 {"account-id: acc-id1\n", "account-id: \n", `"account-id" header should not be empty`}, 167 // XXX: enable this once name is mandatory 168 // {"name: default\n", "", `"name" header is mandatory`}, 169 {"name: default\n", "name: \n", `"name" header should not be empty`}, 170 {"name: default\n", "name: a b\n", `"name" header contains invalid characters: "a b"`}, 171 {"name: default\n", "name: -default\n", `"name" header contains invalid characters: "-default"`}, 172 {"name: default\n", "name: foo:bar\n", `"name" header contains invalid characters: "foo:bar"`}, 173 {"name: default\n", "name: a--b\n", `"name" header contains invalid characters: "a--b"`}, 174 {"name: default\n", "name: 42\n", `"name" header contains invalid characters: "42"`}, 175 {"public-key-sha3-384: " + aks.keyID + "\n", "", `"public-key-sha3-384" header is mandatory`}, 176 {"public-key-sha3-384: " + aks.keyID + "\n", "public-key-sha3-384: \n", `"public-key-sha3-384" header should not be empty`}, 177 {aks.sinceLine, "", `"since" header is mandatory`}, 178 {aks.sinceLine, "since: \n", `"since" header should not be empty`}, 179 {aks.sinceLine, "since: 12:30\n", `"since" header is not a RFC3339 date: .*`}, 180 {aks.sinceLine, "since: \n", `"since" header should not be empty`}, 181 {aks.untilLine, "until: \n", `"until" header is not a RFC3339 date: .*`}, 182 {aks.untilLine, "until: 12:30\n", `"until" header is not a RFC3339 date: .*`}, 183 {aks.untilLine, untilPastLine, `'until' time cannot be before 'since' time`}, 184 } 185 186 for _, test := range invalidHeaderTests { 187 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 188 _, err := asserts.Decode([]byte(invalid)) 189 c.Check(err, ErrorMatches, accKeyErrPrefix+test.expectedErr) 190 } 191 } 192 193 func (aks *accountKeySuite) TestDecodeInvalidPublicKey(c *C) { 194 headers := "type: account-key\n" + 195 "authority-id: canonical\n" + 196 "account-id: acc-id1\n" + 197 "name: default\n" + 198 "public-key-sha3-384: " + aks.keyID + "\n" + 199 aks.sinceLine + 200 aks.untilLine 201 202 raw, err := base64.StdEncoding.DecodeString(aks.pubKeyBody) 203 c.Assert(err, IsNil) 204 spurious := base64.StdEncoding.EncodeToString(append(raw, "gorp"...)) 205 206 invalidPublicKeyTests := []struct{ body, expectedErr string }{ 207 {"", "cannot decode public key: no data"}, 208 {"==", "cannot decode public key: .*"}, 209 {"stuff", "cannot decode public key: .*"}, 210 {"AnNpZw==", "unsupported public key format version: 2"}, 211 {"AUJST0tFTg==", "cannot decode public key: .*"}, 212 {spurious, "public key has spurious trailing data"}, 213 } 214 215 for _, test := range invalidPublicKeyTests { 216 invalid := headers + 217 fmt.Sprintf("body-length: %v", len(test.body)) + "\n" + 218 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 219 test.body + "\n\n" + 220 "AXNpZw==" 221 222 _, err := asserts.Decode([]byte(invalid)) 223 c.Check(err, ErrorMatches, accKeyErrPrefix+test.expectedErr) 224 } 225 } 226 227 func (aks *accountKeySuite) TestDecodeKeyIDMismatch(c *C) { 228 invalid := "type: account-key\n" + 229 "authority-id: canonical\n" + 230 "account-id: acc-id1\n" + 231 "name: default\n" + 232 "public-key-sha3-384: aa\n" + 233 aks.sinceLine + 234 aks.untilLine + 235 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 236 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 237 aks.pubKeyBody + "\n\n" + 238 "AXNpZw==" 239 240 _, err := asserts.Decode([]byte(invalid)) 241 c.Check(err, ErrorMatches, accKeyErrPrefix+"public key does not match provided key id") 242 } 243 244 func (aks *accountKeySuite) openDB(c *C) *asserts.Database { 245 trustedKey := testPrivKey0 246 247 topDir := filepath.Join(c.MkDir(), "asserts-db") 248 bs, err := asserts.OpenFSBackstore(topDir) 249 c.Assert(err, IsNil) 250 cfg := &asserts.DatabaseConfig{ 251 Backstore: bs, 252 Trusted: []asserts.Assertion{ 253 asserts.BootstrapAccountForTest("canonical"), 254 asserts.BootstrapAccountKeyForTest("canonical", trustedKey.PublicKey()), 255 }, 256 } 257 db, err := asserts.OpenDatabase(cfg) 258 c.Assert(err, IsNil) 259 return db 260 } 261 262 func (aks *accountKeySuite) prereqAccount(c *C, db *asserts.Database) { 263 trustedKey := testPrivKey0 264 265 headers := map[string]interface{}{ 266 "authority-id": "canonical", 267 "display-name": "Acct1", 268 "account-id": "acc-id1", 269 "username": "acc-id1", 270 "validation": "unproven", 271 "timestamp": aks.since.Format(time.RFC3339), 272 } 273 acct1, err := asserts.AssembleAndSignInTest(asserts.AccountType, headers, nil, trustedKey) 274 c.Assert(err, IsNil) 275 276 // prereq 277 db.Add(acct1) 278 } 279 280 func (aks *accountKeySuite) TestAccountKeyCheck(c *C) { 281 trustedKey := testPrivKey0 282 283 headers := map[string]interface{}{ 284 "authority-id": "canonical", 285 "account-id": "acc-id1", 286 "name": "default", 287 "public-key-sha3-384": aks.keyID, 288 "since": aks.since.Format(time.RFC3339), 289 "until": aks.until.Format(time.RFC3339), 290 } 291 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 292 c.Assert(err, IsNil) 293 294 db := aks.openDB(c) 295 296 aks.prereqAccount(c, db) 297 298 err = db.Check(accKey) 299 c.Assert(err, IsNil) 300 } 301 302 func (aks *accountKeySuite) TestAccountKeyCheckNoAccount(c *C) { 303 trustedKey := testPrivKey0 304 305 headers := map[string]interface{}{ 306 "authority-id": "canonical", 307 "account-id": "acc-id1", 308 "name": "default", 309 "public-key-sha3-384": aks.keyID, 310 "since": aks.since.Format(time.RFC3339), 311 "until": aks.until.Format(time.RFC3339), 312 } 313 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 314 c.Assert(err, IsNil) 315 316 db := aks.openDB(c) 317 318 err = db.Check(accKey) 319 c.Assert(err, ErrorMatches, `account-key assertion for "acc-id1" does not have a matching account assertion`) 320 } 321 322 func (aks *accountKeySuite) TestAccountKeyCheckUntrustedAuthority(c *C) { 323 trustedKey := testPrivKey0 324 325 db := aks.openDB(c) 326 storeDB := assertstest.NewSigningDB("canonical", trustedKey) 327 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 328 329 headers := map[string]interface{}{ 330 "account-id": "acc-id1", 331 "name": "default", 332 "public-key-sha3-384": aks.keyID, 333 "since": aks.since.Format(time.RFC3339), 334 "until": aks.until.Format(time.RFC3339), 335 } 336 accKey, err := otherDB.Sign(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), "") 337 c.Assert(err, IsNil) 338 339 err = db.Check(accKey) 340 c.Assert(err, ErrorMatches, `account-key assertion for "acc-id1" is not signed by a directly trusted authority:.*`) 341 } 342 343 func (aks *accountKeySuite) TestAccountKeyCheckSameNameAndNewRevision(c *C) { 344 trustedKey := testPrivKey0 345 346 headers := map[string]interface{}{ 347 "authority-id": "canonical", 348 "account-id": "acc-id1", 349 "name": "default", 350 "public-key-sha3-384": aks.keyID, 351 "since": aks.since.Format(time.RFC3339), 352 "until": aks.until.Format(time.RFC3339), 353 } 354 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 355 c.Assert(err, IsNil) 356 357 db := aks.openDB(c) 358 aks.prereqAccount(c, db) 359 360 err = db.Add(accKey) 361 c.Assert(err, IsNil) 362 363 headers["revision"] = "1" 364 newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 365 c.Assert(err, IsNil) 366 367 err = db.Check(newAccKey) 368 c.Assert(err, IsNil) 369 } 370 371 func (aks *accountKeySuite) TestAccountKeyCheckSameAccountAndDifferentName(c *C) { 372 trustedKey := testPrivKey0 373 374 headers := map[string]interface{}{ 375 "authority-id": "canonical", 376 "account-id": "acc-id1", 377 "name": "default", 378 "public-key-sha3-384": aks.keyID, 379 "since": aks.since.Format(time.RFC3339), 380 "until": aks.until.Format(time.RFC3339), 381 } 382 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 383 c.Assert(err, IsNil) 384 385 db := aks.openDB(c) 386 aks.prereqAccount(c, db) 387 388 err = db.Add(accKey) 389 c.Assert(err, IsNil) 390 391 newPrivKey, _ := assertstest.GenerateKey(752) 392 err = db.ImportKey(newPrivKey) 393 c.Assert(err, IsNil) 394 newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID()) 395 c.Assert(err, IsNil) 396 newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey) 397 c.Assert(err, IsNil) 398 399 headers["name"] = "another" 400 headers["public-key-sha3-384"] = newPubKey.ID() 401 newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, newPubKeyEncoded, trustedKey) 402 c.Assert(err, IsNil) 403 404 err = db.Check(newAccKey) 405 c.Assert(err, IsNil) 406 } 407 408 func (aks *accountKeySuite) TestAccountKeyCheckSameNameAndDifferentAccount(c *C) { 409 trustedKey := testPrivKey0 410 411 headers := map[string]interface{}{ 412 "authority-id": "canonical", 413 "account-id": "acc-id1", 414 "name": "default", 415 "public-key-sha3-384": aks.keyID, 416 "since": aks.since.Format(time.RFC3339), 417 "until": aks.until.Format(time.RFC3339), 418 } 419 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 420 c.Assert(err, IsNil) 421 422 db := aks.openDB(c) 423 err = db.ImportKey(trustedKey) 424 c.Assert(err, IsNil) 425 aks.prereqAccount(c, db) 426 427 err = db.Add(accKey) 428 c.Assert(err, IsNil) 429 430 newPrivKey, _ := assertstest.GenerateKey(752) 431 err = db.ImportKey(newPrivKey) 432 c.Assert(err, IsNil) 433 newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID()) 434 c.Assert(err, IsNil) 435 newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey) 436 c.Assert(err, IsNil) 437 438 acct2 := assertstest.NewAccount(db, "acc-id2", map[string]interface{}{ 439 "authority-id": "canonical", 440 "account-id": "acc-id2", 441 }, trustedKey.PublicKey().ID()) 442 db.Add(acct2) 443 444 headers["account-id"] = "acc-id2" 445 headers["public-key-sha3-384"] = newPubKey.ID() 446 headers["revision"] = "1" 447 newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, newPubKeyEncoded, trustedKey) 448 c.Assert(err, IsNil) 449 450 err = db.Check(newAccKey) 451 c.Assert(err, IsNil) 452 } 453 454 func (aks *accountKeySuite) TestAccountKeyCheckNameClash(c *C) { 455 trustedKey := testPrivKey0 456 457 headers := map[string]interface{}{ 458 "authority-id": "canonical", 459 "account-id": "acc-id1", 460 "name": "default", 461 "public-key-sha3-384": aks.keyID, 462 "since": aks.since.Format(time.RFC3339), 463 "until": aks.until.Format(time.RFC3339), 464 } 465 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 466 c.Assert(err, IsNil) 467 468 db := aks.openDB(c) 469 aks.prereqAccount(c, db) 470 471 err = db.Add(accKey) 472 c.Assert(err, IsNil) 473 474 newPrivKey, _ := assertstest.GenerateKey(752) 475 err = db.ImportKey(newPrivKey) 476 c.Assert(err, IsNil) 477 newPubKey, err := db.PublicKey(newPrivKey.PublicKey().ID()) 478 c.Assert(err, IsNil) 479 newPubKeyEncoded, err := asserts.EncodePublicKey(newPubKey) 480 c.Assert(err, IsNil) 481 482 headers["public-key-sha3-384"] = newPubKey.ID() 483 headers["revision"] = "1" 484 newAccKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, newPubKeyEncoded, trustedKey) 485 c.Assert(err, IsNil) 486 487 err = db.Check(newAccKey) 488 c.Assert(err, ErrorMatches, fmt.Sprintf(`account-key assertion for "acc-id1" with ID %q has the same name "default" as existing ID %q`, newPubKey.ID(), aks.keyID)) 489 } 490 491 func (aks *accountKeySuite) TestAccountKeyAddAndFind(c *C) { 492 trustedKey := testPrivKey0 493 494 headers := map[string]interface{}{ 495 "authority-id": "canonical", 496 "account-id": "acc-id1", 497 "name": "default", 498 "public-key-sha3-384": aks.keyID, 499 "since": aks.since.Format(time.RFC3339), 500 "until": aks.until.Format(time.RFC3339), 501 } 502 accKey, err := asserts.AssembleAndSignInTest(asserts.AccountKeyType, headers, []byte(aks.pubKeyBody), trustedKey) 503 c.Assert(err, IsNil) 504 505 db := aks.openDB(c) 506 507 aks.prereqAccount(c, db) 508 509 err = db.Add(accKey) 510 c.Assert(err, IsNil) 511 512 found, err := db.Find(asserts.AccountKeyType, map[string]string{ 513 "account-id": "acc-id1", 514 "public-key-sha3-384": aks.keyID, 515 }) 516 c.Assert(err, IsNil) 517 c.Assert(found, NotNil) 518 c.Check(found.Body(), DeepEquals, []byte(aks.pubKeyBody)) 519 } 520 521 func (aks *accountKeySuite) TestPublicKeyIsValidAt(c *C) { 522 // With since and until, i.e. signing account-key expires. 523 encoded := "type: account-key\n" + 524 "authority-id: canonical\n" + 525 "account-id: acc-id1\n" + 526 "name: default\n" + 527 "public-key-sha3-384: " + aks.keyID + "\n" + 528 aks.sinceLine + 529 aks.untilLine + 530 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 531 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 532 aks.pubKeyBody + "\n\n" + 533 "AXNpZw==" 534 a, err := asserts.Decode([]byte(encoded)) 535 c.Assert(err, IsNil) 536 537 accKey := a.(*asserts.AccountKey) 538 539 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since), Equals, true) 540 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, -1)), Equals, false) 541 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, 1)), Equals, true) 542 543 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until), Equals, false) 544 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, -1, 0)), Equals, true) 545 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, 1, 0)), Equals, false) 546 547 // With no until, i.e. signing account-key never expires. 548 encoded = "type: account-key\n" + 549 "authority-id: canonical\n" + 550 "account-id: acc-id1\n" + 551 "name: default\n" + 552 "public-key-sha3-384: " + aks.keyID + "\n" + 553 aks.sinceLine + 554 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 555 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 556 aks.pubKeyBody + "\n\n" + 557 "openpgp c2ln" 558 a, err = asserts.Decode([]byte(encoded)) 559 c.Assert(err, IsNil) 560 561 accKey = a.(*asserts.AccountKey) 562 563 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since), Equals, true) 564 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, -1)), Equals, false) 565 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, 1)), Equals, true) 566 567 // With since == until, i.e. signing account-key has been revoked. 568 encoded = "type: account-key\n" + 569 "authority-id: canonical\n" + 570 "account-id: acc-id1\n" + 571 "name: default\n" + 572 "public-key-sha3-384: " + aks.keyID + "\n" + 573 aks.sinceLine + 574 "until: " + aks.since.Format(time.RFC3339) + "\n" + 575 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 576 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 577 aks.pubKeyBody + "\n\n" + 578 "openpgp c2ln" 579 a, err = asserts.Decode([]byte(encoded)) 580 c.Assert(err, IsNil) 581 582 accKey = a.(*asserts.AccountKey) 583 584 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since), Equals, false) 585 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, -1)), Equals, false) 586 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.since.AddDate(0, 0, 1)), Equals, false) 587 588 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until), Equals, false) 589 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, -1, 0)), Equals, false) 590 c.Check(asserts.AccountKeyIsKeyValidAt(accKey, aks.until.AddDate(0, 1, 0)), Equals, false) 591 } 592 593 func (aks *accountKeySuite) TestPublicKeyIsValidAssumingCurTimeWithinWithUntilPunctual(c *C) { 594 // With since and until, i.e. signing account-key expires. 595 // Key is valid over [since, until) 596 encoded := "type: account-key\n" + 597 "authority-id: canonical\n" + 598 "account-id: acc-id1\n" + 599 "name: default\n" + 600 "public-key-sha3-384: " + aks.keyID + "\n" + 601 aks.sinceLine + 602 aks.untilLine + 603 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 604 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 605 aks.pubKeyBody + "\n\n" + 606 "AXNpZw==" 607 a, err := asserts.Decode([]byte(encoded)) 608 c.Assert(err, IsNil) 609 610 accKey := a.(*asserts.AccountKey) 611 612 tests := []struct { 613 timePt time.Time 614 valid bool 615 }{ 616 {aks.since, true}, 617 {aks.since.AddDate(0, 3, 0), true}, 618 {aks.since.AddDate(0, -2, 0), false}, 619 {aks.until, false}, 620 {aks.until.AddDate(0, 3, 0), false}, 621 {aks.until.AddDate(0, -2, 0), true}, 622 } 623 624 for _, t := range tests { 625 c.Check(asserts.AccountKeyIsKeyValidAssumingCurTimeWithin(accKey, t.timePt, t.timePt), Equals, t.valid) 626 } 627 } 628 629 func (aks *accountKeySuite) TestPublicKeyIsValidAssumingCurTimeWithinNoUntilPunctual(c *C) { 630 // With since but no until, i.e. signing account-key never expires. 631 // Key is valid for time >= since. 632 encoded := "type: account-key\n" + 633 "authority-id: canonical\n" + 634 "account-id: acc-id1\n" + 635 "name: default\n" + 636 "public-key-sha3-384: " + aks.keyID + "\n" + 637 aks.sinceLine + 638 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 639 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 640 aks.pubKeyBody + "\n\n" + 641 "AXNpZw==" 642 a, err := asserts.Decode([]byte(encoded)) 643 c.Assert(err, IsNil) 644 645 accKey := a.(*asserts.AccountKey) 646 647 later := aks.until 648 tests := []struct { 649 timePt time.Time 650 valid bool 651 }{ 652 {aks.since, true}, 653 {aks.since.AddDate(0, 3, 0), true}, 654 {aks.since.AddDate(0, -2, 0), false}, 655 {later, true}, 656 {later.AddDate(0, 3, 0), true}, 657 } 658 659 for _, t := range tests { 660 c.Check(asserts.AccountKeyIsKeyValidAssumingCurTimeWithin(accKey, t.timePt, t.timePt), Equals, t.valid) 661 } 662 } 663 664 func (aks *accountKeySuite) TestPublicKeyIsValidAssumingCurTimeWithinWithUntilInterval(c *C) { 665 // With since and until, i.e. signing account-key expires. 666 // Key is valid over [since, until) 667 encoded := "type: account-key\n" + 668 "authority-id: canonical\n" + 669 "account-id: acc-id1\n" + 670 "name: default\n" + 671 "public-key-sha3-384: " + aks.keyID + "\n" + 672 aks.sinceLine + 673 aks.untilLine + 674 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 675 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 676 aks.pubKeyBody + "\n\n" + 677 "AXNpZw==" 678 a, err := asserts.Decode([]byte(encoded)) 679 c.Assert(err, IsNil) 680 681 accKey := a.(*asserts.AccountKey) 682 683 z := time.Time{} 684 685 tests := []struct { 686 earliest time.Time 687 latest time.Time 688 valid bool 689 }{ 690 {aks.since, aks.until, true}, 691 {aks.since, aks.since.AddDate(0, 3, 0), true}, 692 {aks.since.AddDate(0, 1, 0), aks.since.AddDate(0, 3, 0), true}, 693 {aks.since.AddDate(0, 1, 0), aks.until, true}, 694 {aks.until, aks.until.AddDate(0, 3, 0), false}, 695 {aks.until.AddDate(0, 2, 0), aks.until.AddDate(0, 3, 0), false}, 696 {aks.since.AddDate(0, -1, 0), aks.since, true}, 697 {aks.since.AddDate(0, -1, 0), aks.since.AddDate(0, 1, 0), true}, 698 {aks.since.AddDate(0, -2, 0), aks.since.AddDate(0, -2, 0), false}, 699 {aks.until.AddDate(0, -1, 0), aks.until.AddDate(0, 1, 0), true}, 700 {aks.since, z, true}, 701 {aks.since.AddDate(0, 1, 0), z, true}, 702 {aks.since.AddDate(0, -3, 0), z, true}, 703 {aks.until, z, false}, 704 {aks.until.AddDate(0, 1, 0), z, false}, 705 // with earliest set to time.Time zero 706 {z, aks.since, true}, 707 {z, aks.since.AddDate(0, 1, 0), true}, 708 {z, aks.since.AddDate(0, -2, 0), false}, 709 {z, aks.until.AddDate(0, 1, 0), true}, 710 {z, z, true}, 711 } 712 713 for _, t := range tests { 714 c.Check(asserts.AccountKeyIsKeyValidAssumingCurTimeWithin(accKey, t.earliest, t.latest), Equals, t.valid) 715 } 716 717 } 718 719 func (aks *accountKeySuite) TestPublicKeyIsValidAssumingCurTimeWithinNoUntilInterval(c *C) { 720 // With since but no until, i.e. signing account-key never expires. 721 // Key is valid for time >= since. 722 encoded := "type: account-key\n" + 723 "authority-id: canonical\n" + 724 "account-id: acc-id1\n" + 725 "name: default\n" + 726 "public-key-sha3-384: " + aks.keyID + "\n" + 727 aks.sinceLine + 728 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 729 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 730 aks.pubKeyBody + "\n\n" + 731 "AXNpZw==" 732 a, err := asserts.Decode([]byte(encoded)) 733 c.Assert(err, IsNil) 734 735 accKey := a.(*asserts.AccountKey) 736 737 z := time.Time{} 738 later := aks.until 739 740 tests := []struct { 741 earliest time.Time 742 latest time.Time 743 valid bool 744 }{ 745 {aks.since, later, true}, 746 {aks.since, aks.since.AddDate(0, 3, 0), true}, 747 {aks.since.AddDate(0, 1, 0), aks.since.AddDate(0, 3, 0), true}, 748 {aks.since.AddDate(0, 1, 0), later, true}, 749 {later, later.AddDate(0, 3, 0), true}, 750 {later.AddDate(0, 2, 0), later.AddDate(0, 3, 0), true}, 751 {aks.since.AddDate(0, -1, 0), aks.since, true}, 752 {aks.since.AddDate(0, -1, 0), aks.since.AddDate(0, 1, 0), true}, 753 {aks.since.AddDate(0, -2, 0), aks.since.AddDate(0, -2, 0), false}, 754 {later.AddDate(0, -1, 0), later.AddDate(0, 1, 0), true}, 755 {aks.since, z, true}, 756 {aks.since.AddDate(0, 1, 0), z, true}, 757 {aks.since.AddDate(0, -3, 0), z, true}, 758 {later, z, true}, 759 {later.AddDate(0, 1, 0), z, true}, 760 // with earliest set to time.Time zero 761 {z, aks.since, true}, 762 {z, aks.since.AddDate(0, 1, 0), true}, 763 {z, aks.since.AddDate(0, -2, 0), false}, 764 {z, later.AddDate(0, 1, 0), true}, 765 {z, z, true}, 766 } 767 768 for _, t := range tests { 769 c.Check(asserts.AccountKeyIsKeyValidAssumingCurTimeWithin(accKey, t.earliest, t.latest), Equals, t.valid) 770 } 771 772 } 773 774 func (aks *accountKeySuite) TestPrerequisites(c *C) { 775 encoded := "type: account-key\n" + 776 "authority-id: canonical\n" + 777 "account-id: acc-id1\n" + 778 "name: default\n" + 779 "public-key-sha3-384: " + aks.keyID + "\n" + 780 aks.sinceLine + 781 aks.untilLine + 782 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 783 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + "\n\n" + 784 aks.pubKeyBody + "\n\n" + 785 "AXNpZw==" 786 a, err := asserts.Decode([]byte(encoded)) 787 c.Assert(err, IsNil) 788 789 prereqs := a.Prerequisites() 790 c.Assert(prereqs, HasLen, 1) 791 c.Check(prereqs[0], DeepEquals, &asserts.Ref{ 792 Type: asserts.AccountType, 793 PrimaryKey: []string{"acc-id1"}, 794 }) 795 } 796 797 func (aks *accountKeySuite) TestAccountKeyRequestHappy(c *C) { 798 akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType, 799 map[string]interface{}{ 800 "account-id": "acc-id1", 801 "name": "default", 802 "public-key-sha3-384": aks.keyID, 803 "since": aks.since.Format(time.RFC3339), 804 }, []byte(aks.pubKeyBody), aks.privKey) 805 c.Assert(err, IsNil) 806 807 // roundtrip 808 a, err := asserts.Decode(asserts.Encode(akr)) 809 c.Assert(err, IsNil) 810 811 akr2, ok := a.(*asserts.AccountKeyRequest) 812 c.Assert(ok, Equals, true) 813 814 db := aks.openDB(c) 815 aks.prereqAccount(c, db) 816 817 err = db.Check(akr2) 818 c.Check(err, IsNil) 819 820 c.Check(akr2.AccountID(), Equals, "acc-id1") 821 c.Check(akr2.Name(), Equals, "default") 822 c.Check(akr2.PublicKeyID(), Equals, aks.keyID) 823 c.Check(akr2.Since(), Equals, aks.since) 824 } 825 826 func (aks *accountKeySuite) TestAccountKeyRequestUntil(c *C) { 827 db := aks.openDB(c) 828 aks.prereqAccount(c, db) 829 830 tests := []struct { 831 untilHeader string 832 until time.Time 833 }{ 834 {"", time.Time{}}, // zero time default 835 {aks.until.Format(time.RFC3339), aks.until}, // in the future 836 {aks.since.Format(time.RFC3339), aks.since}, // same as since 837 } 838 839 for _, test := range tests { 840 c.Log(test) 841 headers := map[string]interface{}{ 842 "account-id": "acc-id1", 843 "name": "default", 844 "public-key-sha3-384": aks.keyID, 845 "since": aks.since.Format(time.RFC3339), 846 } 847 if test.untilHeader != "" { 848 headers["until"] = test.untilHeader 849 } 850 akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType, headers, []byte(aks.pubKeyBody), aks.privKey) 851 c.Assert(err, IsNil) 852 a, err := asserts.Decode(asserts.Encode(akr)) 853 c.Assert(err, IsNil) 854 akr2 := a.(*asserts.AccountKeyRequest) 855 c.Check(akr2.Until(), Equals, test.until) 856 err = db.Check(akr2) 857 c.Check(err, IsNil) 858 } 859 } 860 861 func (aks *accountKeySuite) TestAccountKeyRequestAddAndFind(c *C) { 862 akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType, 863 map[string]interface{}{ 864 "account-id": "acc-id1", 865 "name": "default", 866 "public-key-sha3-384": aks.keyID, 867 "since": aks.since.Format(time.RFC3339), 868 }, []byte(aks.pubKeyBody), aks.privKey) 869 c.Assert(err, IsNil) 870 871 db := aks.openDB(c) 872 aks.prereqAccount(c, db) 873 874 err = db.Add(akr) 875 c.Assert(err, IsNil) 876 877 found, err := db.Find(asserts.AccountKeyRequestType, map[string]string{ 878 "account-id": "acc-id1", 879 "public-key-sha3-384": aks.keyID, 880 }) 881 c.Assert(err, IsNil) 882 c.Assert(found, NotNil) 883 c.Check(found.Body(), DeepEquals, []byte(aks.pubKeyBody)) 884 } 885 886 func (aks *accountKeySuite) TestAccountKeyRequestDecodeInvalid(c *C) { 887 encoded := "type: account-key-request\n" + 888 "account-id: acc-id1\n" + 889 "name: default\n" + 890 "public-key-sha3-384: " + aks.keyID + "\n" + 891 aks.sinceLine + 892 aks.untilLine + 893 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 894 "sign-key-sha3-384: " + aks.privKey.PublicKey().ID() + "\n\n" + 895 aks.pubKeyBody + "\n\n" + 896 "AXNpZw==" 897 898 untilPast := aks.since.AddDate(-1, 0, 0) 899 untilPastLine := "until: " + untilPast.Format(time.RFC3339) + "\n" 900 901 invalidTests := []struct{ original, invalid, expectedErr string }{ 902 {"account-id: acc-id1\n", "", `"account-id" header is mandatory`}, 903 {"account-id: acc-id1\n", "account-id: \n", `"account-id" header should not be empty`}, 904 {"name: default\n", "", `"name" header is mandatory`}, 905 {"name: default\n", "name: \n", `"name" header should not be empty`}, 906 {"name: default\n", "name: a b\n", `"name" header contains invalid characters: "a b"`}, 907 {"name: default\n", "name: -default\n", `"name" header contains invalid characters: "-default"`}, 908 {"name: default\n", "name: foo:bar\n", `"name" header contains invalid characters: "foo:bar"`}, 909 {"public-key-sha3-384: " + aks.keyID + "\n", "", `"public-key-sha3-384" header is mandatory`}, 910 {"public-key-sha3-384: " + aks.keyID + "\n", "public-key-sha3-384: \n", `"public-key-sha3-384" header should not be empty`}, 911 {aks.sinceLine, "", `"since" header is mandatory`}, 912 {aks.sinceLine, "since: \n", `"since" header should not be empty`}, 913 {aks.sinceLine, "since: 12:30\n", `"since" header is not a RFC3339 date: .*`}, 914 {aks.sinceLine, "since: \n", `"since" header should not be empty`}, 915 {aks.untilLine, "until: \n", `"until" header is not a RFC3339 date: .*`}, 916 {aks.untilLine, "until: 12:30\n", `"until" header is not a RFC3339 date: .*`}, 917 {aks.untilLine, untilPastLine, `'until' time cannot be before 'since' time`}, 918 } 919 920 for _, test := range invalidTests { 921 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 922 _, err := asserts.Decode([]byte(invalid)) 923 c.Check(err, ErrorMatches, accKeyReqErrPrefix+test.expectedErr) 924 } 925 } 926 927 func (aks *accountKeySuite) TestAccountKeyRequestDecodeInvalidPublicKey(c *C) { 928 headers := "type: account-key-request\n" + 929 "account-id: acc-id1\n" + 930 "name: default\n" + 931 "public-key-sha3-384: " + aks.keyID + "\n" + 932 aks.sinceLine + 933 aks.untilLine 934 935 raw, err := base64.StdEncoding.DecodeString(aks.pubKeyBody) 936 c.Assert(err, IsNil) 937 spurious := base64.StdEncoding.EncodeToString(append(raw, "gorp"...)) 938 939 invalidPublicKeyTests := []struct{ body, expectedErr string }{ 940 {"", "cannot decode public key: no data"}, 941 {"==", "cannot decode public key: .*"}, 942 {"stuff", "cannot decode public key: .*"}, 943 {"AnNpZw==", "unsupported public key format version: 2"}, 944 {"AUJST0tFTg==", "cannot decode public key: .*"}, 945 {spurious, "public key has spurious trailing data"}, 946 } 947 948 for _, test := range invalidPublicKeyTests { 949 invalid := headers + 950 fmt.Sprintf("body-length: %v", len(test.body)) + "\n" + 951 "sign-key-sha3-384: " + aks.privKey.PublicKey().ID() + "\n\n" + 952 test.body + "\n\n" + 953 "AXNpZw==" 954 955 _, err := asserts.Decode([]byte(invalid)) 956 c.Check(err, ErrorMatches, accKeyReqErrPrefix+test.expectedErr) 957 } 958 } 959 960 func (aks *accountKeySuite) TestAccountKeyRequestDecodeKeyIDMismatch(c *C) { 961 invalid := "type: account-key-request\n" + 962 "account-id: acc-id1\n" + 963 "name: default\n" + 964 "public-key-sha3-384: aa\n" + 965 aks.sinceLine + 966 aks.untilLine + 967 fmt.Sprintf("body-length: %v", len(aks.pubKeyBody)) + "\n" + 968 "sign-key-sha3-384: " + aks.privKey.PublicKey().ID() + "\n\n" + 969 aks.pubKeyBody + "\n\n" + 970 "AXNpZw==" 971 972 _, err := asserts.Decode([]byte(invalid)) 973 c.Check(err, ErrorMatches, "assertion account-key-request: public key does not match provided key id") 974 } 975 976 func (aks *accountKeySuite) TestAccountKeyRequestNoAccount(c *C) { 977 headers := map[string]interface{}{ 978 "account-id": "acc-id1", 979 "name": "default", 980 "public-key-sha3-384": aks.keyID, 981 "since": aks.since.Format(time.RFC3339), 982 } 983 akr, err := asserts.SignWithoutAuthority(asserts.AccountKeyRequestType, headers, []byte(aks.pubKeyBody), aks.privKey) 984 c.Assert(err, IsNil) 985 986 db := aks.openDB(c) 987 988 err = db.Check(akr) 989 c.Assert(err, ErrorMatches, `account-key-request assertion for "acc-id1" does not have a matching account assertion`) 990 }