github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/asserts/snap_asserts_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2017 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 "io/ioutil" 25 "path/filepath" 26 "sort" 27 "strings" 28 "time" 29 30 "golang.org/x/crypto/sha3" 31 . "gopkg.in/check.v1" 32 33 "github.com/snapcore/snapd/asserts" 34 "github.com/snapcore/snapd/asserts/assertstest" 35 ) 36 37 var ( 38 _ = Suite(&snapDeclSuite{}) 39 _ = Suite(&snapFileDigestSuite{}) 40 _ = Suite(&snapBuildSuite{}) 41 _ = Suite(&snapRevSuite{}) 42 _ = Suite(&validationSuite{}) 43 _ = Suite(&baseDeclSuite{}) 44 _ = Suite(&snapDevSuite{}) 45 ) 46 47 type snapDeclSuite struct { 48 ts time.Time 49 tsLine string 50 } 51 52 type emptyAttrerObject struct{} 53 54 func (o emptyAttrerObject) Lookup(path string) (interface{}, bool) { 55 return nil, false 56 } 57 58 func (sds *snapDeclSuite) SetUpSuite(c *C) { 59 sds.ts = time.Now().Truncate(time.Second).UTC() 60 sds.tsLine = "timestamp: " + sds.ts.Format(time.RFC3339) + "\n" 61 } 62 63 func (sds *snapDeclSuite) TestDecodeOK(c *C) { 64 encoded := "type: snap-declaration\n" + 65 "authority-id: canonical\n" + 66 "series: 16\n" + 67 "snap-id: snap-id-1\n" + 68 "snap-name: first\n" + 69 "publisher-id: dev-id1\n" + 70 "refresh-control:\n - foo\n - bar\n" + 71 "auto-aliases:\n - cmd1\n - cmd_2\n - Cmd-3\n - CMD.4\n" + 72 sds.tsLine + 73 `aliases: 74 - 75 name: cmd1 76 target: cmd-1 77 - 78 name: cmd_2 79 target: cmd-2 80 - 81 name: Cmd-3 82 target: cmd-3 83 - 84 name: CMD.4 85 target: cmd-4 86 ` + 87 "body-length: 0\n" + 88 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 89 "\n\n" + 90 "AXNpZw==" 91 a, err := asserts.Decode([]byte(encoded)) 92 c.Assert(err, IsNil) 93 c.Check(a.Type(), Equals, asserts.SnapDeclarationType) 94 snapDecl := a.(*asserts.SnapDeclaration) 95 c.Check(snapDecl.AuthorityID(), Equals, "canonical") 96 c.Check(snapDecl.Timestamp(), Equals, sds.ts) 97 c.Check(snapDecl.Series(), Equals, "16") 98 c.Check(snapDecl.SnapID(), Equals, "snap-id-1") 99 c.Check(snapDecl.SnapName(), Equals, "first") 100 c.Check(snapDecl.PublisherID(), Equals, "dev-id1") 101 c.Check(snapDecl.RefreshControl(), DeepEquals, []string{"foo", "bar"}) 102 c.Check(snapDecl.AutoAliases(), DeepEquals, []string{"cmd1", "cmd_2", "Cmd-3", "CMD.4"}) 103 c.Check(snapDecl.Aliases(), DeepEquals, map[string]string{ 104 "cmd1": "cmd-1", 105 "cmd_2": "cmd-2", 106 "Cmd-3": "cmd-3", 107 "CMD.4": "cmd-4", 108 }) 109 } 110 111 func (sds *snapDeclSuite) TestEmptySnapName(c *C) { 112 encoded := "type: snap-declaration\n" + 113 "authority-id: canonical\n" + 114 "series: 16\n" + 115 "snap-id: snap-id-1\n" + 116 "snap-name: \n" + 117 "publisher-id: dev-id1\n" + 118 sds.tsLine + 119 "body-length: 0\n" + 120 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 121 "\n\n" + 122 "AXNpZw==" 123 a, err := asserts.Decode([]byte(encoded)) 124 c.Assert(err, IsNil) 125 snapDecl := a.(*asserts.SnapDeclaration) 126 c.Check(snapDecl.SnapName(), Equals, "") 127 } 128 129 func (sds *snapDeclSuite) TestMissingRefreshControlAutoAliases(c *C) { 130 encoded := "type: snap-declaration\n" + 131 "authority-id: canonical\n" + 132 "series: 16\n" + 133 "snap-id: snap-id-1\n" + 134 "snap-name: \n" + 135 "publisher-id: dev-id1\n" + 136 sds.tsLine + 137 "body-length: 0\n" + 138 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 139 "\n\n" + 140 "AXNpZw==" 141 a, err := asserts.Decode([]byte(encoded)) 142 c.Assert(err, IsNil) 143 snapDecl := a.(*asserts.SnapDeclaration) 144 c.Check(snapDecl.RefreshControl(), HasLen, 0) 145 c.Check(snapDecl.AutoAliases(), HasLen, 0) 146 } 147 148 const ( 149 snapDeclErrPrefix = "assertion snap-declaration: " 150 ) 151 152 func (sds *snapDeclSuite) TestDecodeInvalid(c *C) { 153 aliases := `aliases: 154 - 155 name: cmd_1 156 target: cmd-1 157 ` 158 encoded := "type: snap-declaration\n" + 159 "authority-id: canonical\n" + 160 "series: 16\n" + 161 "snap-id: snap-id-1\n" + 162 "snap-name: first\n" + 163 "publisher-id: dev-id1\n" + 164 "refresh-control:\n - foo\n - bar\n" + 165 "auto-aliases:\n - cmd1\n - cmd2\n" + 166 aliases + 167 "plugs:\n interface1: true\n" + 168 "slots:\n interface2: true\n" + 169 sds.tsLine + 170 "body-length: 0\n" + 171 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 172 "\n\n" + 173 "AXNpZw==" 174 175 invalidTests := []struct{ original, invalid, expectedErr string }{ 176 {"series: 16\n", "", `"series" header is mandatory`}, 177 {"series: 16\n", "series: \n", `"series" header should not be empty`}, 178 {"snap-id: snap-id-1\n", "", `"snap-id" header is mandatory`}, 179 {"snap-id: snap-id-1\n", "snap-id: \n", `"snap-id" header should not be empty`}, 180 {"snap-name: first\n", "", `"snap-name" header is mandatory`}, 181 {"publisher-id: dev-id1\n", "", `"publisher-id" header is mandatory`}, 182 {"publisher-id: dev-id1\n", "publisher-id: \n", `"publisher-id" header should not be empty`}, 183 {"refresh-control:\n - foo\n - bar\n", "refresh-control: foo\n", `"refresh-control" header must be a list of strings`}, 184 {"refresh-control:\n - foo\n - bar\n", "refresh-control:\n -\n - nested\n", `"refresh-control" header must be a list of strings`}, 185 {"plugs:\n interface1: true\n", "plugs: \n", `"plugs" header must be a map`}, 186 {"plugs:\n interface1: true\n", "plugs:\n intf1:\n foo: bar\n", `plug rule for interface "intf1" must specify at least one of.*`}, 187 {"slots:\n interface2: true\n", "slots: \n", `"slots" header must be a map`}, 188 {"slots:\n interface2: true\n", "slots:\n intf1:\n foo: bar\n", `slot rule for interface "intf1" must specify at least one of.*`}, 189 {"auto-aliases:\n - cmd1\n - cmd2\n", "auto-aliases: cmd0\n", `"auto-aliases" header must be a list of strings`}, 190 {"auto-aliases:\n - cmd1\n - cmd2\n", "auto-aliases:\n -\n - nested\n", `"auto-aliases" header must be a list of strings`}, 191 {"auto-aliases:\n - cmd1\n - cmd2\n", "auto-aliases:\n - _cmd-1\n - cmd2\n", `"auto-aliases" header contains an invalid element: "_cmd-1"`}, 192 {aliases, "aliases: cmd0\n", `"aliases" header must be a list of alias maps`}, 193 {aliases, "aliases:\n - cmd1\n", `"aliases" header must be a list of alias maps`}, 194 {"name: cmd_1\n", "name: .cmd1\n", `"name" in "aliases" item 1 contains invalid characters: ".cmd1"`}, 195 {"target: cmd-1\n", "target: -cmd-1\n", `"target" for alias "cmd_1" contains invalid characters: "-cmd-1"`}, 196 {aliases, aliases + " -\n name: cmd_1\n target: foo\n", `duplicated definition in "aliases" for alias "cmd_1"`}, 197 {sds.tsLine, "", `"timestamp" header is mandatory`}, 198 {sds.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 199 {sds.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 200 } 201 202 for _, test := range invalidTests { 203 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 204 _, err := asserts.Decode([]byte(invalid)) 205 c.Check(err, ErrorMatches, snapDeclErrPrefix+test.expectedErr) 206 } 207 208 } 209 210 func (sds *snapDeclSuite) TestDecodePlugsAndSlots(c *C) { 211 encoded := `type: snap-declaration 212 format: 1 213 authority-id: canonical 214 series: 16 215 snap-id: snap-id-1 216 snap-name: first 217 publisher-id: dev-id1 218 plugs: 219 interface1: 220 deny-installation: false 221 allow-auto-connection: 222 slot-snap-type: 223 - app 224 slot-publisher-id: 225 - acme 226 slot-attributes: 227 a1: /foo/.* 228 plug-attributes: 229 b1: B1 230 deny-auto-connection: 231 slot-attributes: 232 a1: !A1 233 plug-attributes: 234 b1: !B1 235 interface2: 236 allow-installation: true 237 allow-connection: 238 plug-attributes: 239 a2: A2 240 slot-attributes: 241 b2: B2 242 deny-connection: 243 slot-snap-id: 244 - snapidsnapidsnapidsnapidsnapid01 245 - snapidsnapidsnapidsnapidsnapid02 246 plug-attributes: 247 a2: !A2 248 slot-attributes: 249 b2: !B2 250 slots: 251 interface3: 252 deny-installation: false 253 allow-auto-connection: 254 plug-snap-type: 255 - app 256 plug-publisher-id: 257 - acme 258 slot-attributes: 259 c1: /foo/.* 260 plug-attributes: 261 d1: C1 262 deny-auto-connection: 263 slot-attributes: 264 c1: !C1 265 plug-attributes: 266 d1: !D1 267 interface4: 268 allow-connection: 269 plug-attributes: 270 c2: C2 271 slot-attributes: 272 d2: D2 273 deny-connection: 274 plug-snap-id: 275 - snapidsnapidsnapidsnapidsnapid01 276 - snapidsnapidsnapidsnapidsnapid02 277 plug-attributes: 278 c2: !D2 279 slot-attributes: 280 d2: !D2 281 allow-installation: 282 slot-snap-type: 283 - app 284 slot-attributes: 285 e1: E1 286 TSLINE 287 body-length: 0 288 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 289 290 AXNpZw==` 291 encoded = strings.Replace(encoded, "TSLINE\n", sds.tsLine, 1) 292 a, err := asserts.Decode([]byte(encoded)) 293 c.Assert(err, IsNil) 294 c.Check(a.SupportedFormat(), Equals, true) 295 snapDecl := a.(*asserts.SnapDeclaration) 296 c.Check(snapDecl.Series(), Equals, "16") 297 c.Check(snapDecl.SnapID(), Equals, "snap-id-1") 298 299 c.Check(snapDecl.PlugRule("interfaceX"), IsNil) 300 c.Check(snapDecl.SlotRule("interfaceX"), IsNil) 301 302 plugRule1 := snapDecl.PlugRule("interface1") 303 c.Assert(plugRule1, NotNil) 304 c.Assert(plugRule1.DenyInstallation, HasLen, 1) 305 c.Check(plugRule1.DenyInstallation[0].PlugAttributes, Equals, asserts.NeverMatchAttributes) 306 c.Assert(plugRule1.AllowAutoConnection, HasLen, 1) 307 308 plug := emptyAttrerObject{} 309 slot := emptyAttrerObject{} 310 311 c.Check(plugRule1.AllowAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "a1".*`) 312 c.Check(plugRule1.AllowAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "b1".*`) 313 c.Check(plugRule1.AllowAutoConnection[0].SlotSnapTypes, DeepEquals, []string{"app"}) 314 c.Check(plugRule1.AllowAutoConnection[0].SlotPublisherIDs, DeepEquals, []string{"acme"}) 315 c.Assert(plugRule1.DenyAutoConnection, HasLen, 1) 316 c.Check(plugRule1.DenyAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "a1".*`) 317 c.Check(plugRule1.DenyAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "b1".*`) 318 plugRule2 := snapDecl.PlugRule("interface2") 319 c.Assert(plugRule2, NotNil) 320 c.Assert(plugRule2.AllowInstallation, HasLen, 1) 321 c.Check(plugRule2.AllowInstallation[0].PlugAttributes, Equals, asserts.AlwaysMatchAttributes) 322 c.Assert(plugRule2.AllowConnection, HasLen, 1) 323 c.Check(plugRule2.AllowConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "a2".*`) 324 c.Check(plugRule2.AllowConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "b2".*`) 325 c.Assert(plugRule2.DenyConnection, HasLen, 1) 326 c.Check(plugRule2.DenyConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "a2".*`) 327 c.Check(plugRule2.DenyConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "b2".*`) 328 c.Check(plugRule2.DenyConnection[0].SlotSnapIDs, DeepEquals, []string{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}) 329 330 slotRule3 := snapDecl.SlotRule("interface3") 331 c.Assert(slotRule3, NotNil) 332 c.Assert(slotRule3.DenyInstallation, HasLen, 1) 333 c.Check(slotRule3.DenyInstallation[0].SlotAttributes, Equals, asserts.NeverMatchAttributes) 334 c.Assert(slotRule3.AllowAutoConnection, HasLen, 1) 335 c.Check(slotRule3.AllowAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "c1".*`) 336 c.Check(slotRule3.AllowAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "d1".*`) 337 c.Check(slotRule3.AllowAutoConnection[0].PlugSnapTypes, DeepEquals, []string{"app"}) 338 c.Check(slotRule3.AllowAutoConnection[0].PlugPublisherIDs, DeepEquals, []string{"acme"}) 339 c.Assert(slotRule3.DenyAutoConnection, HasLen, 1) 340 c.Check(slotRule3.DenyAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "c1".*`) 341 c.Check(slotRule3.DenyAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "d1".*`) 342 slotRule4 := snapDecl.SlotRule("interface4") 343 c.Assert(slotRule4, NotNil) 344 c.Assert(slotRule4.AllowAutoConnection, HasLen, 1) 345 c.Check(slotRule4.AllowConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "c2".*`) 346 c.Check(slotRule4.AllowConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "d2".*`) 347 c.Assert(slotRule4.DenyAutoConnection, HasLen, 1) 348 c.Check(slotRule4.DenyConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "c2".*`) 349 c.Check(slotRule4.DenyConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "d2".*`) 350 c.Check(slotRule4.DenyConnection[0].PlugSnapIDs, DeepEquals, []string{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}) 351 c.Assert(slotRule4.AllowInstallation, HasLen, 1) 352 c.Check(slotRule4.AllowInstallation[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "e1".*`) 353 c.Check(slotRule4.AllowInstallation[0].SlotSnapTypes, DeepEquals, []string{"app"}) 354 } 355 356 func (sds *snapDeclSuite) TestSuggestedFormat(c *C) { 357 fmtnum, err := asserts.SuggestFormat(asserts.SnapDeclarationType, nil, nil) 358 c.Assert(err, IsNil) 359 c.Check(fmtnum, Equals, 0) 360 361 headers := map[string]interface{}{ 362 "plugs": map[string]interface{}{ 363 "interface1": "true", 364 }, 365 } 366 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 367 c.Assert(err, IsNil) 368 c.Check(fmtnum, Equals, 1) 369 370 headers = map[string]interface{}{ 371 "slots": map[string]interface{}{ 372 "interface2": "true", 373 }, 374 } 375 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 376 c.Assert(err, IsNil) 377 c.Check(fmtnum, Equals, 1) 378 379 headers = map[string]interface{}{ 380 "plugs": map[string]interface{}{ 381 "interface3": map[string]interface{}{ 382 "allow-auto-connection": map[string]interface{}{ 383 "plug-attributes": map[string]interface{}{ 384 "x": "$SLOT(x)", 385 }, 386 }, 387 }, 388 }, 389 } 390 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 391 c.Assert(err, IsNil) 392 c.Check(fmtnum, Equals, 2) 393 394 headers = map[string]interface{}{ 395 "slots": map[string]interface{}{ 396 "interface3": map[string]interface{}{ 397 "allow-auto-connection": map[string]interface{}{ 398 "plug-attributes": map[string]interface{}{ 399 "x": "$SLOT(x)", 400 }, 401 }, 402 }, 403 }, 404 } 405 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 406 c.Assert(err, IsNil) 407 c.Check(fmtnum, Equals, 2) 408 409 // combinations with on-store/on-brand/on-model => format 3 410 for _, side := range []string{"plugs", "slots"} { 411 for k, vals := range deviceScopeConstrs { 412 413 headers := map[string]interface{}{ 414 side: map[string]interface{}{ 415 "interface3": map[string]interface{}{ 416 "allow-installation": map[string]interface{}{ 417 k: vals, 418 }, 419 }, 420 }, 421 } 422 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 423 c.Assert(err, IsNil) 424 c.Check(fmtnum, Equals, 3) 425 426 for _, conn := range []string{"connection", "auto-connection"} { 427 428 headers = map[string]interface{}{ 429 side: map[string]interface{}{ 430 "interface3": map[string]interface{}{ 431 "allow-" + conn: map[string]interface{}{ 432 k: vals, 433 }, 434 }, 435 }, 436 } 437 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 438 c.Assert(err, IsNil) 439 c.Check(fmtnum, Equals, 3) 440 } 441 } 442 } 443 444 // higher format features win 445 446 headers = map[string]interface{}{ 447 "plugs": map[string]interface{}{ 448 "interface3": map[string]interface{}{ 449 "allow-auto-connection": map[string]interface{}{ 450 "on-store": []interface{}{"store"}, 451 }, 452 }, 453 }, 454 "slots": map[string]interface{}{ 455 "interface4": map[string]interface{}{ 456 "allow-auto-connection": map[string]interface{}{ 457 "plug-attributes": map[string]interface{}{ 458 "x": "$SLOT(x)", 459 }, 460 }, 461 }, 462 }, 463 } 464 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 465 c.Assert(err, IsNil) 466 c.Check(fmtnum, Equals, 3) 467 468 headers = map[string]interface{}{ 469 "plugs": map[string]interface{}{ 470 "interface4": map[string]interface{}{ 471 "allow-auto-connection": map[string]interface{}{ 472 "slot-attributes": map[string]interface{}{ 473 "x": "$SLOT(x)", 474 }, 475 }, 476 }, 477 }, 478 "slots": map[string]interface{}{ 479 "interface3": map[string]interface{}{ 480 "allow-auto-connection": map[string]interface{}{ 481 "on-store": []interface{}{"store"}, 482 }, 483 }, 484 }, 485 } 486 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 487 c.Assert(err, IsNil) 488 c.Check(fmtnum, Equals, 3) 489 490 // errors 491 headers = map[string]interface{}{ 492 "plugs": "what", 493 } 494 _, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 495 c.Assert(err, ErrorMatches, `assertion snap-declaration: "plugs" header must be a map`) 496 497 headers = map[string]interface{}{ 498 "slots": "what", 499 } 500 _, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 501 c.Assert(err, ErrorMatches, `assertion snap-declaration: "slots" header must be a map`) 502 503 // plug-names/slot-names => format 4 504 for _, sidePrefix := range []string{"plug", "slot"} { 505 side := sidePrefix + "s" 506 headers := map[string]interface{}{ 507 side: map[string]interface{}{ 508 "interface3": map[string]interface{}{ 509 "allow-installation": map[string]interface{}{ 510 sidePrefix + "-names": []interface{}{"foo"}, 511 }, 512 }, 513 }, 514 } 515 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 516 c.Assert(err, IsNil) 517 c.Check(fmtnum, Equals, 4) 518 519 for _, conn := range []string{"connection", "auto-connection"} { 520 521 headers = map[string]interface{}{ 522 side: map[string]interface{}{ 523 "interface3": map[string]interface{}{ 524 "allow-" + conn: map[string]interface{}{ 525 sidePrefix + "-names": []interface{}{"foo"}, 526 }, 527 }, 528 }, 529 } 530 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 531 c.Assert(err, IsNil) 532 c.Check(fmtnum, Equals, 4) 533 534 headers = map[string]interface{}{ 535 side: map[string]interface{}{ 536 "interface3": map[string]interface{}{ 537 "allow-" + conn: map[string]interface{}{ 538 "plug-names": []interface{}{"Pfoo"}, 539 "slot-names": []interface{}{"Sfoo"}, 540 }, 541 }, 542 }, 543 } 544 fmtnum, err = asserts.SuggestFormat(asserts.SnapDeclarationType, headers, nil) 545 c.Assert(err, IsNil) 546 c.Check(fmtnum, Equals, 4) 547 } 548 } 549 } 550 551 func prereqDevAccount(c *C, storeDB assertstest.SignerDB, db *asserts.Database) { 552 dev1Acct := assertstest.NewAccount(storeDB, "developer1", map[string]interface{}{ 553 "account-id": "dev-id1", 554 }, "") 555 err := db.Add(dev1Acct) 556 c.Assert(err, IsNil) 557 } 558 559 func (sds *snapDeclSuite) TestSnapDeclarationCheck(c *C) { 560 storeDB, db := makeStoreAndCheckDB(c) 561 562 prereqDevAccount(c, storeDB, db) 563 564 headers := map[string]interface{}{ 565 "series": "16", 566 "snap-id": "snap-id-1", 567 "snap-name": "foo", 568 "publisher-id": "dev-id1", 569 "timestamp": time.Now().Format(time.RFC3339), 570 } 571 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, headers, nil, "") 572 c.Assert(err, IsNil) 573 574 err = db.Check(snapDecl) 575 c.Assert(err, IsNil) 576 } 577 578 func (sds *snapDeclSuite) TestSnapDeclarationCheckUntrustedAuthority(c *C) { 579 storeDB, db := makeStoreAndCheckDB(c) 580 581 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 582 583 headers := map[string]interface{}{ 584 "series": "16", 585 "snap-id": "snap-id-1", 586 "snap-name": "foo", 587 "publisher-id": "dev-id1", 588 "timestamp": time.Now().Format(time.RFC3339), 589 } 590 snapDecl, err := otherDB.Sign(asserts.SnapDeclarationType, headers, nil, "") 591 c.Assert(err, IsNil) 592 593 err = db.Check(snapDecl) 594 c.Assert(err, ErrorMatches, `snap-declaration assertion for "foo" \(id "snap-id-1"\) is not signed by a directly trusted authority:.*`) 595 } 596 597 func (sds *snapDeclSuite) TestSnapDeclarationCheckMissingPublisherAccount(c *C) { 598 storeDB, db := makeStoreAndCheckDB(c) 599 600 headers := map[string]interface{}{ 601 "series": "16", 602 "snap-id": "snap-id-1", 603 "snap-name": "foo", 604 "publisher-id": "dev-id1", 605 "timestamp": time.Now().Format(time.RFC3339), 606 } 607 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, headers, nil, "") 608 c.Assert(err, IsNil) 609 610 err = db.Check(snapDecl) 611 c.Assert(err, ErrorMatches, `snap-declaration assertion for "foo" \(id "snap-id-1"\) does not have a matching account assertion for the publisher "dev-id1"`) 612 } 613 614 type snapFileDigestSuite struct{} 615 616 func (s *snapFileDigestSuite) TestSnapFileSHA3_384(c *C) { 617 exData := []byte("hashmeplease") 618 619 tempdir := c.MkDir() 620 snapFn := filepath.Join(tempdir, "ex.snap") 621 err := ioutil.WriteFile(snapFn, exData, 0644) 622 c.Assert(err, IsNil) 623 624 encDgst, size, err := asserts.SnapFileSHA3_384(snapFn) 625 c.Assert(err, IsNil) 626 c.Check(size, Equals, uint64(len(exData))) 627 628 h3_384 := sha3.Sum384(exData) 629 expected := base64.RawURLEncoding.EncodeToString(h3_384[:]) 630 c.Check(encDgst, DeepEquals, expected) 631 } 632 633 type snapBuildSuite struct { 634 ts time.Time 635 tsLine string 636 } 637 638 func (sds *snapDeclSuite) TestPrerequisites(c *C) { 639 encoded := "type: snap-declaration\n" + 640 "authority-id: canonical\n" + 641 "series: 16\n" + 642 "snap-id: snap-id-1\n" + 643 "snap-name: first\n" + 644 "publisher-id: dev-id1\n" + 645 sds.tsLine + 646 "body-length: 0\n" + 647 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 648 "\n\n" + 649 "AXNpZw==" 650 a, err := asserts.Decode([]byte(encoded)) 651 c.Assert(err, IsNil) 652 653 prereqs := a.Prerequisites() 654 c.Assert(prereqs, HasLen, 1) 655 c.Check(prereqs[0], DeepEquals, &asserts.Ref{ 656 Type: asserts.AccountType, 657 PrimaryKey: []string{"dev-id1"}, 658 }) 659 } 660 661 func (sbs *snapBuildSuite) SetUpSuite(c *C) { 662 sbs.ts = time.Now().Truncate(time.Second).UTC() 663 sbs.tsLine = "timestamp: " + sbs.ts.Format(time.RFC3339) + "\n" 664 } 665 666 const ( 667 blobSHA3_384 = "QlqR0uAWEAWF5Nwnzj5kqmmwFslYPu1IL16MKtLKhwhv0kpBv5wKZ_axf_nf_2cL" 668 ) 669 670 func (sbs *snapBuildSuite) TestDecodeOK(c *C) { 671 encoded := "type: snap-build\n" + 672 "authority-id: dev-id1\n" + 673 "snap-sha3-384: " + blobSHA3_384 + "\n" + 674 "grade: stable\n" + 675 "snap-id: snap-id-1\n" + 676 "snap-size: 10000\n" + 677 sbs.tsLine + 678 "body-length: 0\n" + 679 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 680 "\n\n" + 681 "AXNpZw==" 682 a, err := asserts.Decode([]byte(encoded)) 683 c.Assert(err, IsNil) 684 c.Check(a.Type(), Equals, asserts.SnapBuildType) 685 snapBuild := a.(*asserts.SnapBuild) 686 c.Check(snapBuild.AuthorityID(), Equals, "dev-id1") 687 c.Check(snapBuild.Timestamp(), Equals, sbs.ts) 688 c.Check(snapBuild.SnapID(), Equals, "snap-id-1") 689 c.Check(snapBuild.SnapSHA3_384(), Equals, blobSHA3_384) 690 c.Check(snapBuild.SnapSize(), Equals, uint64(10000)) 691 c.Check(snapBuild.Grade(), Equals, "stable") 692 } 693 694 const ( 695 snapBuildErrPrefix = "assertion snap-build: " 696 ) 697 698 func (sbs *snapBuildSuite) TestDecodeInvalid(c *C) { 699 digestHdr := "snap-sha3-384: " + blobSHA3_384 + "\n" 700 701 encoded := "type: snap-build\n" + 702 "authority-id: dev-id1\n" + 703 digestHdr + 704 "grade: stable\n" + 705 "snap-id: snap-id-1\n" + 706 "snap-size: 10000\n" + 707 sbs.tsLine + 708 "body-length: 0\n" + 709 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 710 "\n\n" + 711 "AXNpZw==" 712 713 invalidTests := []struct{ original, invalid, expectedErr string }{ 714 {"snap-id: snap-id-1\n", "", `"snap-id" header is mandatory`}, 715 {"snap-id: snap-id-1\n", "snap-id: \n", `"snap-id" header should not be empty`}, 716 {digestHdr, "", `"snap-sha3-384" header is mandatory`}, 717 {digestHdr, "snap-sha3-384: \n", `"snap-sha3-384" header should not be empty`}, 718 {digestHdr, "snap-sha3-384: #\n", `"snap-sha3-384" header cannot be decoded:.*`}, 719 {"snap-size: 10000\n", "", `"snap-size" header is mandatory`}, 720 {"snap-size: 10000\n", "snap-size: -1\n", `"snap-size" header is not an unsigned integer: -1`}, 721 {"snap-size: 10000\n", "snap-size: zzz\n", `"snap-size" header is not an unsigned integer: zzz`}, 722 {"snap-size: 10000\n", "snap-size: 010\n", `"snap-size" header has invalid prefix zeros: 010`}, 723 {"snap-size: 10000\n", "snap-size: 99999999999999999999\n", `"snap-size" header is out of range: 99999999999999999999`}, 724 {"grade: stable\n", "", `"grade" header is mandatory`}, 725 {"grade: stable\n", "grade: \n", `"grade" header should not be empty`}, 726 {sbs.tsLine, "", `"timestamp" header is mandatory`}, 727 {sbs.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 728 {sbs.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 729 } 730 731 for _, test := range invalidTests { 732 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 733 _, err := asserts.Decode([]byte(invalid)) 734 c.Check(err, ErrorMatches, snapBuildErrPrefix+test.expectedErr) 735 } 736 } 737 738 func makeStoreAndCheckDB(c *C) (store *assertstest.StoreStack, checkDB *asserts.Database) { 739 store = assertstest.NewStoreStack("canonical", nil) 740 cfg := &asserts.DatabaseConfig{ 741 Backstore: asserts.NewMemoryBackstore(), 742 Trusted: store.Trusted, 743 OtherPredefined: store.Generic, 744 } 745 checkDB, err := asserts.OpenDatabase(cfg) 746 c.Assert(err, IsNil) 747 748 // add store key 749 err = checkDB.Add(store.StoreAccountKey("")) 750 c.Assert(err, IsNil) 751 // add generic key 752 err = checkDB.Add(store.GenericKey) 753 c.Assert(err, IsNil) 754 755 return store, checkDB 756 } 757 758 func setup3rdPartySigning(c *C, username string, storeDB assertstest.SignerDB, checkDB *asserts.Database) (signingDB *assertstest.SigningDB) { 759 privKey := testPrivKey2 760 761 acct := assertstest.NewAccount(storeDB, username, map[string]interface{}{ 762 "account-id": username, 763 }, "") 764 accKey := assertstest.NewAccountKey(storeDB, acct, nil, privKey.PublicKey(), "") 765 766 err := checkDB.Add(acct) 767 c.Assert(err, IsNil) 768 err = checkDB.Add(accKey) 769 c.Assert(err, IsNil) 770 771 return assertstest.NewSigningDB(acct.AccountID(), privKey) 772 } 773 774 func (sbs *snapBuildSuite) TestSnapBuildCheck(c *C) { 775 storeDB, db := makeStoreAndCheckDB(c) 776 devDB := setup3rdPartySigning(c, "devel1", storeDB, db) 777 778 headers := map[string]interface{}{ 779 "authority-id": "devel1", 780 "snap-sha3-384": blobSHA3_384, 781 "snap-id": "snap-id-1", 782 "grade": "devel", 783 "snap-size": "1025", 784 "timestamp": time.Now().Format(time.RFC3339), 785 } 786 snapBuild, err := devDB.Sign(asserts.SnapBuildType, headers, nil, "") 787 c.Assert(err, IsNil) 788 789 err = db.Check(snapBuild) 790 c.Assert(err, IsNil) 791 } 792 793 func (sbs *snapBuildSuite) TestSnapBuildCheckInconsistentTimestamp(c *C) { 794 storeDB, db := makeStoreAndCheckDB(c) 795 devDB := setup3rdPartySigning(c, "devel1", storeDB, db) 796 797 headers := map[string]interface{}{ 798 "snap-sha3-384": blobSHA3_384, 799 "snap-id": "snap-id-1", 800 "grade": "devel", 801 "snap-size": "1025", 802 "timestamp": "2013-01-01T14:00:00Z", 803 } 804 snapBuild, err := devDB.Sign(asserts.SnapBuildType, headers, nil, "") 805 c.Assert(err, IsNil) 806 807 err = db.Check(snapBuild) 808 c.Assert(err, ErrorMatches, `snap-build assertion timestamp "2013-01-01 14:00:00 \+0000 UTC" outside of signing key validity \(key valid since.*\)`) 809 } 810 811 type snapRevSuite struct { 812 ts time.Time 813 tsLine string 814 } 815 816 func (srs *snapRevSuite) SetUpSuite(c *C) { 817 srs.ts = time.Now().Truncate(time.Second).UTC() 818 srs.tsLine = "timestamp: " + srs.ts.Format(time.RFC3339) + "\n" 819 } 820 821 func (srs *snapRevSuite) makeValidEncoded() string { 822 return "type: snap-revision\n" + 823 "authority-id: store-id1\n" + 824 "snap-sha3-384: " + blobSHA3_384 + "\n" + 825 "snap-id: snap-id-1\n" + 826 "snap-size: 123\n" + 827 "snap-revision: 1\n" + 828 "developer-id: dev-id1\n" + 829 "revision: 1\n" + 830 srs.tsLine + 831 "body-length: 0\n" + 832 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 833 "\n\n" + 834 "AXNpZw==" 835 } 836 837 func (srs *snapRevSuite) makeHeaders(overrides map[string]interface{}) map[string]interface{} { 838 headers := map[string]interface{}{ 839 "authority-id": "canonical", 840 "snap-sha3-384": blobSHA3_384, 841 "snap-id": "snap-id-1", 842 "snap-size": "123", 843 "snap-revision": "1", 844 "developer-id": "dev-id1", 845 "revision": "1", 846 "timestamp": time.Now().Format(time.RFC3339), 847 } 848 for k, v := range overrides { 849 headers[k] = v 850 } 851 return headers 852 } 853 854 func (srs *snapRevSuite) TestDecodeOK(c *C) { 855 encoded := srs.makeValidEncoded() 856 a, err := asserts.Decode([]byte(encoded)) 857 c.Assert(err, IsNil) 858 c.Check(a.Type(), Equals, asserts.SnapRevisionType) 859 snapRev := a.(*asserts.SnapRevision) 860 c.Check(snapRev.AuthorityID(), Equals, "store-id1") 861 c.Check(snapRev.Timestamp(), Equals, srs.ts) 862 c.Check(snapRev.SnapID(), Equals, "snap-id-1") 863 c.Check(snapRev.SnapSHA3_384(), Equals, blobSHA3_384) 864 c.Check(snapRev.SnapSize(), Equals, uint64(123)) 865 c.Check(snapRev.SnapRevision(), Equals, 1) 866 c.Check(snapRev.DeveloperID(), Equals, "dev-id1") 867 c.Check(snapRev.Revision(), Equals, 1) 868 } 869 870 const ( 871 snapRevErrPrefix = "assertion snap-revision: " 872 ) 873 874 func (srs *snapRevSuite) TestDecodeInvalid(c *C) { 875 encoded := srs.makeValidEncoded() 876 877 digestHdr := "snap-sha3-384: " + blobSHA3_384 + "\n" 878 invalidTests := []struct{ original, invalid, expectedErr string }{ 879 {"snap-id: snap-id-1\n", "", `"snap-id" header is mandatory`}, 880 {"snap-id: snap-id-1\n", "snap-id: \n", `"snap-id" header should not be empty`}, 881 {digestHdr, "", `"snap-sha3-384" header is mandatory`}, 882 {digestHdr, "snap-sha3-384: \n", `"snap-sha3-384" header should not be empty`}, 883 {digestHdr, "snap-sha3-384: #\n", `"snap-sha3-384" header cannot be decoded:.*`}, 884 {digestHdr, "snap-sha3-384: eHl6\n", `"snap-sha3-384" header does not have the expected bit length: 24`}, 885 {"snap-size: 123\n", "", `"snap-size" header is mandatory`}, 886 {"snap-size: 123\n", "snap-size: \n", `"snap-size" header should not be empty`}, 887 {"snap-size: 123\n", "snap-size: -1\n", `"snap-size" header is not an unsigned integer: -1`}, 888 {"snap-size: 123\n", "snap-size: zzz\n", `"snap-size" header is not an unsigned integer: zzz`}, 889 {"snap-revision: 1\n", "", `"snap-revision" header is mandatory`}, 890 {"snap-revision: 1\n", "snap-revision: \n", `"snap-revision" header should not be empty`}, 891 {"snap-revision: 1\n", "snap-revision: -1\n", `"snap-revision" header must be >=1: -1`}, 892 {"snap-revision: 1\n", "snap-revision: 0\n", `"snap-revision" header must be >=1: 0`}, 893 {"snap-revision: 1\n", "snap-revision: zzz\n", `"snap-revision" header is not an integer: zzz`}, 894 {"developer-id: dev-id1\n", "", `"developer-id" header is mandatory`}, 895 {"developer-id: dev-id1\n", "developer-id: \n", `"developer-id" header should not be empty`}, 896 {srs.tsLine, "", `"timestamp" header is mandatory`}, 897 {srs.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 898 {srs.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 899 } 900 901 for _, test := range invalidTests { 902 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 903 _, err := asserts.Decode([]byte(invalid)) 904 c.Check(err, ErrorMatches, snapRevErrPrefix+test.expectedErr) 905 } 906 } 907 908 func prereqSnapDecl(c *C, storeDB assertstest.SignerDB, db *asserts.Database) { 909 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 910 "series": "16", 911 "snap-id": "snap-id-1", 912 "snap-name": "foo", 913 "publisher-id": "dev-id1", 914 "timestamp": time.Now().Format(time.RFC3339), 915 }, nil, "") 916 c.Assert(err, IsNil) 917 err = db.Add(snapDecl) 918 c.Assert(err, IsNil) 919 } 920 921 func (srs *snapRevSuite) TestSnapRevisionCheck(c *C) { 922 storeDB, db := makeStoreAndCheckDB(c) 923 924 prereqDevAccount(c, storeDB, db) 925 prereqSnapDecl(c, storeDB, db) 926 927 headers := srs.makeHeaders(nil) 928 snapRev, err := storeDB.Sign(asserts.SnapRevisionType, headers, nil, "") 929 c.Assert(err, IsNil) 930 931 err = db.Check(snapRev) 932 c.Assert(err, IsNil) 933 } 934 935 func (srs *snapRevSuite) TestSnapRevisionCheckInconsistentTimestamp(c *C) { 936 storeDB, db := makeStoreAndCheckDB(c) 937 938 headers := srs.makeHeaders(map[string]interface{}{ 939 "timestamp": "2013-01-01T14:00:00Z", 940 }) 941 snapRev, err := storeDB.Sign(asserts.SnapRevisionType, headers, nil, "") 942 c.Assert(err, IsNil) 943 944 err = db.Check(snapRev) 945 c.Assert(err, ErrorMatches, `snap-revision assertion timestamp "2013-01-01 14:00:00 \+0000 UTC" outside of signing key validity \(key valid since.*\)`) 946 } 947 948 func (srs *snapRevSuite) TestSnapRevisionCheckUntrustedAuthority(c *C) { 949 storeDB, db := makeStoreAndCheckDB(c) 950 951 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 952 953 headers := srs.makeHeaders(map[string]interface{}{ 954 "authority-id": "other", 955 }) 956 snapRev, err := otherDB.Sign(asserts.SnapRevisionType, headers, nil, "") 957 c.Assert(err, IsNil) 958 959 err = db.Check(snapRev) 960 c.Assert(err, ErrorMatches, `snap-revision assertion for snap id "snap-id-1" is not signed by a store:.*`) 961 } 962 963 func (srs *snapRevSuite) TestSnapRevisionCheckMissingDeveloperAccount(c *C) { 964 storeDB, db := makeStoreAndCheckDB(c) 965 966 headers := srs.makeHeaders(nil) 967 snapRev, err := storeDB.Sign(asserts.SnapRevisionType, headers, nil, "") 968 c.Assert(err, IsNil) 969 970 err = db.Check(snapRev) 971 c.Assert(err, ErrorMatches, `snap-revision assertion for snap id "snap-id-1" does not have a matching account assertion for the developer "dev-id1"`) 972 } 973 974 func (srs *snapRevSuite) TestSnapRevisionCheckMissingDeclaration(c *C) { 975 storeDB, db := makeStoreAndCheckDB(c) 976 977 prereqDevAccount(c, storeDB, db) 978 979 headers := srs.makeHeaders(nil) 980 snapRev, err := storeDB.Sign(asserts.SnapRevisionType, headers, nil, "") 981 c.Assert(err, IsNil) 982 983 err = db.Check(snapRev) 984 c.Assert(err, ErrorMatches, `snap-revision assertion for snap id "snap-id-1" does not have a matching snap-declaration assertion`) 985 } 986 987 func (srs *snapRevSuite) TestPrimaryKey(c *C) { 988 storeDB, db := makeStoreAndCheckDB(c) 989 990 prereqDevAccount(c, storeDB, db) 991 prereqSnapDecl(c, storeDB, db) 992 993 headers := srs.makeHeaders(nil) 994 snapRev, err := storeDB.Sign(asserts.SnapRevisionType, headers, nil, "") 995 c.Assert(err, IsNil) 996 err = db.Add(snapRev) 997 c.Assert(err, IsNil) 998 999 _, err = db.Find(asserts.SnapRevisionType, map[string]string{ 1000 "snap-sha3-384": headers["snap-sha3-384"].(string), 1001 }) 1002 c.Assert(err, IsNil) 1003 } 1004 1005 func (srs *snapRevSuite) TestPrerequisites(c *C) { 1006 encoded := srs.makeValidEncoded() 1007 a, err := asserts.Decode([]byte(encoded)) 1008 c.Assert(err, IsNil) 1009 1010 prereqs := a.Prerequisites() 1011 c.Assert(prereqs, HasLen, 2) 1012 c.Check(prereqs[0], DeepEquals, &asserts.Ref{ 1013 Type: asserts.SnapDeclarationType, 1014 PrimaryKey: []string{"16", "snap-id-1"}, 1015 }) 1016 c.Check(prereqs[1], DeepEquals, &asserts.Ref{ 1017 Type: asserts.AccountType, 1018 PrimaryKey: []string{"dev-id1"}, 1019 }) 1020 } 1021 1022 type validationSuite struct { 1023 ts time.Time 1024 tsLine string 1025 } 1026 1027 func (vs *validationSuite) SetUpSuite(c *C) { 1028 vs.ts = time.Now().Truncate(time.Second).UTC() 1029 vs.tsLine = "timestamp: " + vs.ts.Format(time.RFC3339) + "\n" 1030 } 1031 1032 func (vs *validationSuite) makeValidEncoded() string { 1033 return "type: validation\n" + 1034 "authority-id: dev-id1\n" + 1035 "series: 16\n" + 1036 "snap-id: snap-id-1\n" + 1037 "approved-snap-id: snap-id-2\n" + 1038 "approved-snap-revision: 42\n" + 1039 "revision: 1\n" + 1040 vs.tsLine + 1041 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 1042 "\n\n" + 1043 "AXNpZw==" 1044 } 1045 1046 func (vs *validationSuite) makeHeaders(overrides map[string]interface{}) map[string]interface{} { 1047 headers := map[string]interface{}{ 1048 "authority-id": "dev-id1", 1049 "series": "16", 1050 "snap-id": "snap-id-1", 1051 "approved-snap-id": "snap-id-2", 1052 "approved-snap-revision": "42", 1053 "revision": "1", 1054 "timestamp": time.Now().Format(time.RFC3339), 1055 } 1056 for k, v := range overrides { 1057 headers[k] = v 1058 } 1059 return headers 1060 } 1061 1062 func (vs *validationSuite) TestDecodeOK(c *C) { 1063 encoded := vs.makeValidEncoded() 1064 a, err := asserts.Decode([]byte(encoded)) 1065 c.Assert(err, IsNil) 1066 c.Check(a.Type(), Equals, asserts.ValidationType) 1067 validation := a.(*asserts.Validation) 1068 c.Check(validation.AuthorityID(), Equals, "dev-id1") 1069 c.Check(validation.Timestamp(), Equals, vs.ts) 1070 c.Check(validation.Series(), Equals, "16") 1071 c.Check(validation.SnapID(), Equals, "snap-id-1") 1072 c.Check(validation.ApprovedSnapID(), Equals, "snap-id-2") 1073 c.Check(validation.ApprovedSnapRevision(), Equals, 42) 1074 c.Check(validation.Revoked(), Equals, false) 1075 c.Check(validation.Revision(), Equals, 1) 1076 } 1077 1078 const ( 1079 validationErrPrefix = "assertion validation: " 1080 ) 1081 1082 func (vs *validationSuite) TestDecodeInvalid(c *C) { 1083 encoded := vs.makeValidEncoded() 1084 1085 invalidTests := []struct{ original, invalid, expectedErr string }{ 1086 {"series: 16\n", "", `"series" header is mandatory`}, 1087 {"series: 16\n", "series: \n", `"series" header should not be empty`}, 1088 {"snap-id: snap-id-1\n", "", `"snap-id" header is mandatory`}, 1089 {"snap-id: snap-id-1\n", "snap-id: \n", `"snap-id" header should not be empty`}, 1090 {"approved-snap-id: snap-id-2\n", "", `"approved-snap-id" header is mandatory`}, 1091 {"approved-snap-id: snap-id-2\n", "approved-snap-id: \n", `"approved-snap-id" header should not be empty`}, 1092 {"approved-snap-revision: 42\n", "", `"approved-snap-revision" header is mandatory`}, 1093 {"approved-snap-revision: 42\n", "approved-snap-revision: z\n", `"approved-snap-revision" header is not an integer: z`}, 1094 {"approved-snap-revision: 42\n", "approved-snap-revision: 0\n", `"approved-snap-revision" header must be >=1: 0`}, 1095 {"approved-snap-revision: 42\n", "approved-snap-revision: -1\n", `"approved-snap-revision" header must be >=1: -1`}, 1096 {vs.tsLine, "", `"timestamp" header is mandatory`}, 1097 {vs.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 1098 {vs.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 1099 } 1100 1101 for _, test := range invalidTests { 1102 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 1103 _, err := asserts.Decode([]byte(invalid)) 1104 c.Check(err, ErrorMatches, validationErrPrefix+test.expectedErr) 1105 } 1106 } 1107 1108 func prereqSnapDecl2(c *C, storeDB assertstest.SignerDB, db *asserts.Database) { 1109 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1110 "series": "16", 1111 "snap-id": "snap-id-2", 1112 "snap-name": "bar", 1113 "publisher-id": "dev-id1", 1114 "timestamp": time.Now().Format(time.RFC3339), 1115 }, nil, "") 1116 c.Assert(err, IsNil) 1117 err = db.Add(snapDecl) 1118 c.Assert(err, IsNil) 1119 } 1120 1121 func (vs *validationSuite) TestValidationCheck(c *C) { 1122 storeDB, db := makeStoreAndCheckDB(c) 1123 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1124 1125 prereqSnapDecl(c, storeDB, db) 1126 prereqSnapDecl2(c, storeDB, db) 1127 1128 headers := vs.makeHeaders(nil) 1129 validation, err := devDB.Sign(asserts.ValidationType, headers, nil, "") 1130 c.Assert(err, IsNil) 1131 1132 err = db.Check(validation) 1133 c.Assert(err, IsNil) 1134 } 1135 1136 func (vs *validationSuite) TestValidationCheckWrongAuthority(c *C) { 1137 storeDB, db := makeStoreAndCheckDB(c) 1138 1139 prereqDevAccount(c, storeDB, db) 1140 prereqSnapDecl(c, storeDB, db) 1141 prereqSnapDecl2(c, storeDB, db) 1142 1143 headers := vs.makeHeaders(map[string]interface{}{ 1144 "authority-id": "canonical", // not the publisher 1145 }) 1146 validation, err := storeDB.Sign(asserts.ValidationType, headers, nil, "") 1147 c.Assert(err, IsNil) 1148 1149 err = db.Check(validation) 1150 c.Assert(err, ErrorMatches, `validation assertion by snap "foo" \(id "snap-id-1"\) not signed by its publisher`) 1151 } 1152 1153 func (vs *validationSuite) TestRevocation(c *C) { 1154 encoded := "type: validation\n" + 1155 "authority-id: dev-id1\n" + 1156 "series: 16\n" + 1157 "snap-id: snap-id-1\n" + 1158 "approved-snap-id: snap-id-2\n" + 1159 "approved-snap-revision: 42\n" + 1160 "revoked: true\n" + 1161 "revision: 1\n" + 1162 vs.tsLine + 1163 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 1164 "\n\n" + 1165 "AXNpZw==" 1166 a, err := asserts.Decode([]byte(encoded)) 1167 c.Assert(err, IsNil) 1168 validation := a.(*asserts.Validation) 1169 c.Check(validation.Revoked(), Equals, true) 1170 } 1171 1172 func (vs *validationSuite) TestRevokedFalse(c *C) { 1173 encoded := "type: validation\n" + 1174 "authority-id: dev-id1\n" + 1175 "series: 16\n" + 1176 "snap-id: snap-id-1\n" + 1177 "approved-snap-id: snap-id-2\n" + 1178 "approved-snap-revision: 42\n" + 1179 "revoked: false\n" + 1180 "revision: 1\n" + 1181 vs.tsLine + 1182 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 1183 "\n\n" + 1184 "AXNpZw==" 1185 a, err := asserts.Decode([]byte(encoded)) 1186 c.Assert(err, IsNil) 1187 validation := a.(*asserts.Validation) 1188 c.Check(validation.Revoked(), Equals, false) 1189 } 1190 1191 func (vs *validationSuite) TestRevokedInvalid(c *C) { 1192 encoded := "type: validation\n" + 1193 "authority-id: dev-id1\n" + 1194 "series: 16\n" + 1195 "snap-id: snap-id-1\n" + 1196 "approved-snap-id: snap-id-2\n" + 1197 "approved-snap-revision: 42\n" + 1198 "revoked: foo\n" + 1199 "revision: 1\n" + 1200 vs.tsLine + 1201 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 1202 "\n\n" + 1203 "AXNpZw==" 1204 _, err := asserts.Decode([]byte(encoded)) 1205 c.Check(err, ErrorMatches, `.*: "revoked" header must be 'true' or 'false'`) 1206 } 1207 1208 func (vs *validationSuite) TestMissingGatedSnapDeclaration(c *C) { 1209 storeDB, db := makeStoreAndCheckDB(c) 1210 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1211 1212 headers := vs.makeHeaders(nil) 1213 a, err := devDB.Sign(asserts.ValidationType, headers, nil, "") 1214 c.Assert(err, IsNil) 1215 1216 err = db.Check(a) 1217 c.Assert(err, ErrorMatches, `validation assertion by snap-id "snap-id-1" does not have a matching snap-declaration assertion for approved-snap-id "snap-id-2"`) 1218 } 1219 1220 func (vs *validationSuite) TestMissingGatingSnapDeclaration(c *C) { 1221 storeDB, db := makeStoreAndCheckDB(c) 1222 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1223 1224 prereqSnapDecl2(c, storeDB, db) 1225 1226 headers := vs.makeHeaders(nil) 1227 a, err := devDB.Sign(asserts.ValidationType, headers, nil, "") 1228 c.Assert(err, IsNil) 1229 1230 err = db.Check(a) 1231 c.Assert(err, ErrorMatches, `validation assertion by snap-id "snap-id-1" does not have a matching snap-declaration assertion`) 1232 } 1233 1234 func (vs *validationSuite) TestPrerequisites(c *C) { 1235 encoded := vs.makeValidEncoded() 1236 a, err := asserts.Decode([]byte(encoded)) 1237 c.Assert(err, IsNil) 1238 1239 prereqs := a.Prerequisites() 1240 c.Assert(prereqs, HasLen, 2) 1241 c.Check(prereqs[0], DeepEquals, &asserts.Ref{ 1242 Type: asserts.SnapDeclarationType, 1243 PrimaryKey: []string{"16", "snap-id-1"}, 1244 }) 1245 c.Check(prereqs[1], DeepEquals, &asserts.Ref{ 1246 Type: asserts.SnapDeclarationType, 1247 PrimaryKey: []string{"16", "snap-id-2"}, 1248 }) 1249 } 1250 1251 type baseDeclSuite struct{} 1252 1253 func (s *baseDeclSuite) TestDecodeOK(c *C) { 1254 encoded := `type: base-declaration 1255 authority-id: canonical 1256 series: 16 1257 plugs: 1258 interface1: 1259 deny-installation: false 1260 allow-auto-connection: 1261 slot-snap-type: 1262 - app 1263 slot-publisher-id: 1264 - acme 1265 slot-attributes: 1266 a1: /foo/.* 1267 plug-attributes: 1268 b1: B1 1269 deny-auto-connection: 1270 slot-attributes: 1271 a1: !A1 1272 plug-attributes: 1273 b1: !B1 1274 interface2: 1275 allow-installation: true 1276 allow-connection: 1277 plug-attributes: 1278 a2: A2 1279 slot-attributes: 1280 b2: B2 1281 deny-connection: 1282 slot-snap-id: 1283 - snapidsnapidsnapidsnapidsnapid01 1284 - snapidsnapidsnapidsnapidsnapid02 1285 plug-attributes: 1286 a2: !A2 1287 slot-attributes: 1288 b2: !B2 1289 slots: 1290 interface3: 1291 deny-installation: false 1292 allow-auto-connection: 1293 plug-snap-type: 1294 - app 1295 plug-publisher-id: 1296 - acme 1297 slot-attributes: 1298 c1: /foo/.* 1299 plug-attributes: 1300 d1: C1 1301 deny-auto-connection: 1302 slot-attributes: 1303 c1: !C1 1304 plug-attributes: 1305 d1: !D1 1306 interface4: 1307 allow-connection: 1308 plug-attributes: 1309 c2: C2 1310 slot-attributes: 1311 d2: D2 1312 deny-connection: 1313 plug-snap-id: 1314 - snapidsnapidsnapidsnapidsnapid01 1315 - snapidsnapidsnapidsnapidsnapid02 1316 plug-attributes: 1317 c2: !D2 1318 slot-attributes: 1319 d2: !D2 1320 allow-installation: 1321 slot-snap-type: 1322 - app 1323 slot-attributes: 1324 e1: E1 1325 timestamp: 2016-09-29T19:50:49Z 1326 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 1327 1328 AXNpZw==` 1329 a, err := asserts.Decode([]byte(encoded)) 1330 c.Assert(err, IsNil) 1331 baseDecl := a.(*asserts.BaseDeclaration) 1332 c.Check(baseDecl.Series(), Equals, "16") 1333 ts, err := time.Parse(time.RFC3339, "2016-09-29T19:50:49Z") 1334 c.Assert(err, IsNil) 1335 c.Check(baseDecl.Timestamp().Equal(ts), Equals, true) 1336 1337 c.Check(baseDecl.PlugRule("interfaceX"), IsNil) 1338 c.Check(baseDecl.SlotRule("interfaceX"), IsNil) 1339 1340 plug := emptyAttrerObject{} 1341 slot := emptyAttrerObject{} 1342 1343 plugRule1 := baseDecl.PlugRule("interface1") 1344 c.Assert(plugRule1, NotNil) 1345 c.Assert(plugRule1.DenyInstallation, HasLen, 1) 1346 c.Check(plugRule1.DenyInstallation[0].PlugAttributes, Equals, asserts.NeverMatchAttributes) 1347 c.Assert(plugRule1.AllowAutoConnection, HasLen, 1) 1348 c.Check(plugRule1.AllowAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "a1".*`) 1349 c.Check(plugRule1.AllowAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "b1".*`) 1350 c.Check(plugRule1.AllowAutoConnection[0].SlotSnapTypes, DeepEquals, []string{"app"}) 1351 c.Check(plugRule1.AllowAutoConnection[0].SlotPublisherIDs, DeepEquals, []string{"acme"}) 1352 c.Assert(plugRule1.DenyAutoConnection, HasLen, 1) 1353 c.Check(plugRule1.DenyAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "a1".*`) 1354 c.Check(plugRule1.DenyAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "b1".*`) 1355 plugRule2 := baseDecl.PlugRule("interface2") 1356 c.Assert(plugRule2, NotNil) 1357 c.Assert(plugRule2.AllowInstallation, HasLen, 1) 1358 c.Check(plugRule2.AllowInstallation[0].PlugAttributes, Equals, asserts.AlwaysMatchAttributes) 1359 c.Assert(plugRule2.AllowConnection, HasLen, 1) 1360 c.Check(plugRule2.AllowConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "a2".*`) 1361 c.Check(plugRule2.AllowConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "b2".*`) 1362 c.Assert(plugRule2.DenyConnection, HasLen, 1) 1363 c.Check(plugRule2.DenyConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "a2".*`) 1364 c.Check(plugRule2.DenyConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "b2".*`) 1365 c.Check(plugRule2.DenyConnection[0].SlotSnapIDs, DeepEquals, []string{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}) 1366 1367 slotRule3 := baseDecl.SlotRule("interface3") 1368 c.Assert(slotRule3, NotNil) 1369 c.Assert(slotRule3.DenyInstallation, HasLen, 1) 1370 c.Check(slotRule3.DenyInstallation[0].SlotAttributes, Equals, asserts.NeverMatchAttributes) 1371 c.Assert(slotRule3.AllowAutoConnection, HasLen, 1) 1372 c.Check(slotRule3.AllowAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "c1".*`) 1373 c.Check(slotRule3.AllowAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "d1".*`) 1374 c.Check(slotRule3.AllowAutoConnection[0].PlugSnapTypes, DeepEquals, []string{"app"}) 1375 c.Check(slotRule3.AllowAutoConnection[0].PlugPublisherIDs, DeepEquals, []string{"acme"}) 1376 c.Assert(slotRule3.DenyAutoConnection, HasLen, 1) 1377 c.Check(slotRule3.DenyAutoConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "c1".*`) 1378 c.Check(slotRule3.DenyAutoConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "d1".*`) 1379 slotRule4 := baseDecl.SlotRule("interface4") 1380 c.Assert(slotRule4, NotNil) 1381 c.Assert(slotRule4.AllowConnection, HasLen, 1) 1382 c.Check(slotRule4.AllowConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "c2".*`) 1383 c.Check(slotRule4.AllowConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "d2".*`) 1384 c.Assert(slotRule4.DenyConnection, HasLen, 1) 1385 c.Check(slotRule4.DenyConnection[0].PlugAttributes.Check(plug, nil), ErrorMatches, `attribute "c2".*`) 1386 c.Check(slotRule4.DenyConnection[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "d2".*`) 1387 c.Check(slotRule4.DenyConnection[0].PlugSnapIDs, DeepEquals, []string{"snapidsnapidsnapidsnapidsnapid01", "snapidsnapidsnapidsnapidsnapid02"}) 1388 c.Assert(slotRule4.AllowInstallation, HasLen, 1) 1389 c.Check(slotRule4.AllowInstallation[0].SlotAttributes.Check(slot, nil), ErrorMatches, `attribute "e1".*`) 1390 c.Check(slotRule4.AllowInstallation[0].SlotSnapTypes, DeepEquals, []string{"app"}) 1391 1392 } 1393 1394 func (s *baseDeclSuite) TestBaseDeclarationCheckUntrustedAuthority(c *C) { 1395 storeDB, db := makeStoreAndCheckDB(c) 1396 1397 otherDB := setup3rdPartySigning(c, "other", storeDB, db) 1398 1399 headers := map[string]interface{}{ 1400 "series": "16", 1401 "timestamp": time.Now().Format(time.RFC3339), 1402 } 1403 baseDecl, err := otherDB.Sign(asserts.BaseDeclarationType, headers, nil, "") 1404 c.Assert(err, IsNil) 1405 1406 err = db.Check(baseDecl) 1407 c.Assert(err, ErrorMatches, `base-declaration assertion for series 16 is not signed by a directly trusted authority: other`) 1408 } 1409 1410 const ( 1411 baseDeclErrPrefix = "assertion base-declaration: " 1412 ) 1413 1414 func (s *baseDeclSuite) TestDecodeInvalid(c *C) { 1415 tsLine := "timestamp: 2016-09-29T19:50:49Z\n" 1416 1417 encoded := "type: base-declaration\n" + 1418 "authority-id: canonical\n" + 1419 "series: 16\n" + 1420 "plugs:\n interface1: true\n" + 1421 "slots:\n interface2: true\n" + 1422 tsLine + 1423 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 1424 "\n\n" + 1425 "AXNpZw==" 1426 1427 invalidTests := []struct{ original, invalid, expectedErr string }{ 1428 {"series: 16\n", "", `"series" header is mandatory`}, 1429 {"series: 16\n", "series: \n", `"series" header should not be empty`}, 1430 {"plugs:\n interface1: true\n", "plugs: \n", `"plugs" header must be a map`}, 1431 {"plugs:\n interface1: true\n", "plugs:\n intf1:\n foo: bar\n", `plug rule for interface "intf1" must specify at least one of.*`}, 1432 {"slots:\n interface2: true\n", "slots: \n", `"slots" header must be a map`}, 1433 {"slots:\n interface2: true\n", "slots:\n intf1:\n foo: bar\n", `slot rule for interface "intf1" must specify at least one of.*`}, 1434 {tsLine, "", `"timestamp" header is mandatory`}, 1435 {tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 1436 } 1437 1438 for _, test := range invalidTests { 1439 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 1440 _, err := asserts.Decode([]byte(invalid)) 1441 c.Check(err, ErrorMatches, baseDeclErrPrefix+test.expectedErr) 1442 } 1443 1444 } 1445 1446 func (s *baseDeclSuite) TestBuiltin(c *C) { 1447 baseDecl := asserts.BuiltinBaseDeclaration() 1448 c.Check(baseDecl, IsNil) 1449 1450 defer asserts.InitBuiltinBaseDeclaration(nil) 1451 1452 const headers = ` 1453 type: base-declaration 1454 authority-id: canonical 1455 series: 16 1456 revision: 0 1457 plugs: 1458 network: true 1459 slots: 1460 network: 1461 allow-installation: 1462 slot-snap-type: 1463 - core 1464 ` 1465 1466 err := asserts.InitBuiltinBaseDeclaration([]byte(headers)) 1467 c.Assert(err, IsNil) 1468 1469 baseDecl = asserts.BuiltinBaseDeclaration() 1470 c.Assert(baseDecl, NotNil) 1471 1472 cont, _ := baseDecl.Signature() 1473 c.Check(string(cont), Equals, strings.TrimSpace(headers)) 1474 1475 c.Check(baseDecl.AuthorityID(), Equals, "canonical") 1476 c.Check(baseDecl.Series(), Equals, "16") 1477 c.Check(baseDecl.PlugRule("network").AllowAutoConnection[0].SlotAttributes, Equals, asserts.AlwaysMatchAttributes) 1478 c.Check(baseDecl.SlotRule("network").AllowInstallation[0].SlotSnapTypes, DeepEquals, []string{"core"}) 1479 1480 enc := asserts.Encode(baseDecl) 1481 // it's expected that it cannot be decoded 1482 _, err = asserts.Decode(enc) 1483 c.Check(err, NotNil) 1484 } 1485 1486 func (s *baseDeclSuite) TestBuiltinInitErrors(c *C) { 1487 defer asserts.InitBuiltinBaseDeclaration(nil) 1488 1489 tests := []struct { 1490 headers string 1491 err string 1492 }{ 1493 {"", `header entry missing ':' separator: ""`}, 1494 {"type: foo\n", `the builtin base-declaration "type" header is not set to expected value "base-declaration"`}, 1495 {"type: base-declaration", `the builtin base-declaration "authority-id" header is not set to expected value "canonical"`}, 1496 {"type: base-declaration\nauthority-id: canonical", `the builtin base-declaration "series" header is not set to expected value "16"`}, 1497 {"type: base-declaration\nauthority-id: canonical\nseries: 16\nrevision: zzz", `cannot assemble the builtin-base declaration: "revision" header is not an integer: zzz`}, 1498 {"type: base-declaration\nauthority-id: canonical\nseries: 16\nplugs: foo", `cannot assemble the builtin base-declaration: "plugs" header must be a map`}, 1499 } 1500 1501 for _, t := range tests { 1502 err := asserts.InitBuiltinBaseDeclaration([]byte(t.headers)) 1503 c.Check(err, ErrorMatches, t.err, Commentf(t.headers)) 1504 } 1505 } 1506 1507 type snapDevSuite struct { 1508 developersLines string 1509 validEncoded string 1510 } 1511 1512 func (sds *snapDevSuite) SetUpSuite(c *C) { 1513 sds.developersLines = "developers:\n -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: 2017-02-01T00:00:00.0Z\n" 1514 sds.validEncoded = "type: snap-developer\n" + 1515 "authority-id: dev-id1\n" + 1516 "snap-id: snap-id-1\n" + 1517 "publisher-id: dev-id1\n" + 1518 sds.developersLines + 1519 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 1520 "\n\n" + 1521 "AXNpZw==" 1522 } 1523 1524 func (sds *snapDevSuite) TestDecodeOK(c *C) { 1525 encoded := sds.validEncoded 1526 a, err := asserts.Decode([]byte(encoded)) 1527 c.Assert(err, IsNil) 1528 c.Check(a.Type(), Equals, asserts.SnapDeveloperType) 1529 snapDev := a.(*asserts.SnapDeveloper) 1530 c.Check(snapDev.AuthorityID(), Equals, "dev-id1") 1531 c.Check(snapDev.PublisherID(), Equals, "dev-id1") 1532 c.Check(snapDev.SnapID(), Equals, "snap-id-1") 1533 } 1534 1535 func (sds *snapDevSuite) TestDevelopersOptional(c *C) { 1536 encoded := strings.Replace(sds.validEncoded, sds.developersLines, "", 1) 1537 _, err := asserts.Decode([]byte(encoded)) 1538 c.Check(err, IsNil) 1539 } 1540 1541 func (sds *snapDevSuite) TestDevelopersUntilOptional(c *C) { 1542 encoded := strings.Replace( 1543 sds.validEncoded, sds.developersLines, 1544 "developers:\n -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n", 1) 1545 _, err := asserts.Decode([]byte(encoded)) 1546 c.Check(err, IsNil) 1547 } 1548 1549 func (sds *snapDevSuite) TestDevelopersRevoked(c *C) { 1550 encoded := sds.validEncoded 1551 encoded = strings.Replace( 1552 encoded, sds.developersLines, 1553 "developers:\n -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: 2017-01-01T00:00:00.0Z\n", 1) 1554 _, err := asserts.Decode([]byte(encoded)) 1555 c.Check(err, IsNil) 1556 // TODO(matt): check actually revoked rather than just parsed 1557 } 1558 1559 const ( 1560 snapDevErrPrefix = "assertion snap-developer: " 1561 ) 1562 1563 func (sds *snapDevSuite) TestDecodeInvalid(c *C) { 1564 encoded := sds.validEncoded 1565 1566 invalidTests := []struct{ original, invalid, expectedErr string }{ 1567 {"publisher-id: dev-id1\n", "", `"publisher-id" header is mandatory`}, 1568 {"publisher-id: dev-id1\n", "publisher-id: \n", `"publisher-id" header should not be empty`}, 1569 {"snap-id: snap-id-1\n", "", `"snap-id" header is mandatory`}, 1570 {"snap-id: snap-id-1\n", "snap-id: \n", `"snap-id" header should not be empty`}, 1571 {sds.developersLines, "developers: \n", `"developers" must be a list of developer maps`}, 1572 {sds.developersLines, "developers: foo\n", `"developers" must be a list of developer maps`}, 1573 {sds.developersLines, "developers:\n foo: bar\n", `"developers" must be a list of developer maps`}, 1574 {sds.developersLines, "developers:\n - foo\n", `"developers" must be a list of developer maps`}, 1575 {sds.developersLines, "developers:\n -\n foo: bar\n", `"developer-id" in "developers" item 1 is mandatory`}, 1576 {sds.developersLines, "developers:\n -\n developer-id: a\n", 1577 `"developer-id" in "developers" item 1 contains invalid characters: "a"`}, 1578 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n", 1579 `"since" in "developers" item 1 for developer "dev-id2" is mandatory`}, 1580 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n since: \n", 1581 `"since" in "developers" item 1 for developer "dev-id2" should not be empty`}, 1582 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n since: foo\n", 1583 `"since" in "developers" item 1 for developer "dev-id2" is not a RFC3339 date.*`}, 1584 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: \n", 1585 `"until" in "developers" item 1 for developer "dev-id2" is not a RFC3339 date.*`}, 1586 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: foo\n", 1587 `"until" in "developers" item 1 for developer "dev-id2" is not a RFC3339 date.*`}, 1588 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n -\n foo: bar\n", 1589 `"developer-id" in "developers" item 2 is mandatory`}, 1590 {sds.developersLines, "developers:\n -\n developer-id: dev-id2\n since: 2017-01-02T00:00:00.0Z\n until: 2017-01-01T00:00:00.0Z\n", 1591 `"since" in "developers" item 1 for developer "dev-id2" must be less than or equal to "until"`}, 1592 } 1593 1594 for _, test := range invalidTests { 1595 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 1596 _, err := asserts.Decode([]byte(invalid)) 1597 c.Check(err, ErrorMatches, snapDevErrPrefix+test.expectedErr) 1598 } 1599 } 1600 1601 func (sds *snapDevSuite) TestRevokedValidation(c *C) { 1602 // Multiple non-revoking items are fine. 1603 encoded := strings.Replace(sds.validEncoded, sds.developersLines, 1604 "developers:\n"+ 1605 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: 2017-02-01T00:00:00.0Z\n"+ 1606 " -\n developer-id: dev-id2\n since: 2017-03-01T00:00:00.0Z\n", 1607 1) 1608 _, err := asserts.Decode([]byte(encoded)) 1609 c.Check(err, IsNil) 1610 1611 // Multiple revocations for different developers are fine. 1612 encoded = strings.Replace(sds.validEncoded, sds.developersLines, 1613 "developers:\n"+ 1614 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: 2017-01-01T00:00:00.0Z\n"+ 1615 " -\n developer-id: dev-id3\n since: 2017-02-01T00:00:00.0Z\n until: 2017-02-01T00:00:00.0Z\n", 1616 1) 1617 _, err = asserts.Decode([]byte(encoded)) 1618 c.Check(err, IsNil) 1619 1620 invalidTests := []string{ 1621 // Multiple revocations. 1622 "developers:\n" + 1623 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: 2017-01-01T00:00:00.0Z\n" + 1624 " -\n developer-id: dev-id2\n since: 2017-02-01T00:00:00.0Z\n until: 2017-02-01T00:00:00.0Z\n", 1625 // Revocation after non-revoking. 1626 "developers:\n" + 1627 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n" + 1628 " -\n developer-id: dev-id2\n since: 2017-03-01T00:00:00.0Z\n until: 2017-03-01T00:00:00.0Z\n", 1629 // Non-revoking after revocation. 1630 "developers:\n" + 1631 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n until: 2017-01-01T00:00:00.0Z\n" + 1632 " -\n developer-id: dev-id2\n since: 2017-02-01T00:00:00.0Z\n", 1633 } 1634 for _, test := range invalidTests { 1635 encoded := strings.Replace(sds.validEncoded, sds.developersLines, test, 1) 1636 _, err := asserts.Decode([]byte(encoded)) 1637 c.Check(err, ErrorMatches, snapDevErrPrefix+`revocation for developer "dev-id2" must be standalone but found other "developers" items`) 1638 } 1639 } 1640 1641 func (sds *snapDevSuite) TestAuthorityIsPublisher(c *C) { 1642 storeDB, db := makeStoreAndCheckDB(c) 1643 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1644 1645 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1646 "series": "16", 1647 "snap-id": "snap-id-1", 1648 "snap-name": "snap-name-1", 1649 "publisher-id": "dev-id1", 1650 "timestamp": time.Now().UTC().Format(time.RFC3339), 1651 }, nil, "") 1652 c.Assert(err, IsNil) 1653 err = db.Add(snapDecl) 1654 c.Assert(err, IsNil) 1655 1656 snapDev, err := devDB.Sign(asserts.SnapDeveloperType, map[string]interface{}{ 1657 "snap-id": "snap-id-1", 1658 "publisher-id": "dev-id1", 1659 }, nil, "") 1660 c.Assert(err, IsNil) 1661 // Just to be super sure ... 1662 c.Assert(snapDev.HeaderString("authority-id"), Equals, "dev-id1") 1663 c.Assert(snapDev.HeaderString("publisher-id"), Equals, "dev-id1") 1664 1665 err = db.Check(snapDev) 1666 c.Assert(err, IsNil) 1667 } 1668 1669 func (sds *snapDevSuite) TestAuthorityIsNotPublisher(c *C) { 1670 storeDB, db := makeStoreAndCheckDB(c) 1671 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1672 1673 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1674 "series": "16", 1675 "snap-id": "snap-id-1", 1676 "snap-name": "snap-name-1", 1677 "publisher-id": "dev-id1", 1678 "timestamp": time.Now().UTC().Format(time.RFC3339), 1679 }, nil, "") 1680 c.Assert(err, IsNil) 1681 err = db.Add(snapDecl) 1682 c.Assert(err, IsNil) 1683 1684 snapDev, err := devDB.Sign(asserts.SnapDeveloperType, map[string]interface{}{ 1685 "authority-id": "dev-id1", 1686 "snap-id": "snap-id-1", 1687 "publisher-id": "dev-id2", 1688 }, nil, "") 1689 c.Assert(err, IsNil) 1690 // Just to be super sure ... 1691 c.Assert(snapDev.HeaderString("authority-id"), Equals, "dev-id1") 1692 c.Assert(snapDev.HeaderString("publisher-id"), Equals, "dev-id2") 1693 1694 err = db.Check(snapDev) 1695 c.Assert(err, ErrorMatches, `snap-developer must be signed by the publisher or a trusted authority but got authority "dev-id1" and publisher "dev-id2"`) 1696 } 1697 1698 func (sds *snapDevSuite) TestAuthorityIsNotPublisherButIsTrusted(c *C) { 1699 storeDB, db := makeStoreAndCheckDB(c) 1700 1701 account, err := storeDB.Sign(asserts.AccountType, map[string]interface{}{ 1702 "account-id": "dev-id1", 1703 "display-name": "dev-id1", 1704 "validation": "unknown", 1705 "timestamp": time.Now().UTC().Format(time.RFC3339), 1706 }, nil, "") 1707 c.Assert(err, IsNil) 1708 err = db.Add(account) 1709 c.Assert(err, IsNil) 1710 1711 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1712 "series": "16", 1713 "snap-id": "snap-id-1", 1714 "snap-name": "snap-name-1", 1715 "publisher-id": "dev-id1", 1716 "timestamp": time.Now().UTC().Format(time.RFC3339), 1717 }, nil, "") 1718 c.Assert(err, IsNil) 1719 err = db.Add(snapDecl) 1720 c.Assert(err, IsNil) 1721 1722 snapDev, err := storeDB.Sign(asserts.SnapDeveloperType, map[string]interface{}{ 1723 "snap-id": "snap-id-1", 1724 "publisher-id": "dev-id1", 1725 }, nil, "") 1726 c.Assert(err, IsNil) 1727 // Just to be super sure ... 1728 c.Assert(snapDev.HeaderString("authority-id"), Equals, "canonical") 1729 c.Assert(snapDev.HeaderString("publisher-id"), Equals, "dev-id1") 1730 1731 err = db.Check(snapDev) 1732 c.Assert(err, IsNil) 1733 } 1734 1735 func (sds *snapDevSuite) TestCheckNewPublisherAccountExists(c *C) { 1736 storeDB, db := makeStoreAndCheckDB(c) 1737 1738 account, err := storeDB.Sign(asserts.AccountType, map[string]interface{}{ 1739 "account-id": "dev-id1", 1740 "display-name": "dev-id1", 1741 "validation": "unknown", 1742 "timestamp": time.Now().UTC().Format(time.RFC3339), 1743 }, nil, "") 1744 c.Assert(err, IsNil) 1745 err = db.Add(account) 1746 c.Assert(err, IsNil) 1747 1748 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1749 "series": "16", 1750 "snap-id": "snap-id-1", 1751 "snap-name": "snap-name-1", 1752 "publisher-id": "dev-id1", 1753 "timestamp": time.Now().UTC().Format(time.RFC3339), 1754 }, nil, "") 1755 c.Assert(err, IsNil) 1756 err = db.Add(snapDecl) 1757 c.Assert(err, IsNil) 1758 1759 snapDev, err := storeDB.Sign(asserts.SnapDeveloperType, map[string]interface{}{ 1760 "snap-id": "snap-id-1", 1761 "publisher-id": "dev-id2", 1762 }, nil, "") 1763 c.Assert(err, IsNil) 1764 // Just to be super sure ... 1765 c.Assert(snapDev.HeaderString("authority-id"), Equals, "canonical") 1766 c.Assert(snapDev.HeaderString("publisher-id"), Equals, "dev-id2") 1767 1768 // There's no account for dev-id2 yet so it should fail. 1769 err = db.Check(snapDev) 1770 c.Assert(err, ErrorMatches, `snap-developer assertion for snap-id "snap-id-1" does not have a matching account assertion for the publisher "dev-id2"`) 1771 1772 // But once the dev-id2 account is added the snap-developer is ok. 1773 account, err = storeDB.Sign(asserts.AccountType, map[string]interface{}{ 1774 "account-id": "dev-id2", 1775 "display-name": "dev-id2", 1776 "validation": "unknown", 1777 "timestamp": time.Now().UTC().Format(time.RFC3339), 1778 }, nil, "") 1779 c.Assert(err, IsNil) 1780 err = db.Add(account) 1781 c.Assert(err, IsNil) 1782 1783 err = db.Check(snapDev) 1784 c.Assert(err, IsNil) 1785 } 1786 1787 func (sds *snapDevSuite) TestCheckDeveloperAccountExists(c *C) { 1788 storeDB, db := makeStoreAndCheckDB(c) 1789 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1790 1791 snapDecl, err := storeDB.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1792 "series": "16", 1793 "snap-id": "snap-id-1", 1794 "snap-name": "snap-name-1", 1795 "publisher-id": "dev-id1", 1796 "timestamp": time.Now().UTC().Format(time.RFC3339), 1797 }, nil, "") 1798 c.Assert(err, IsNil) 1799 err = db.Add(snapDecl) 1800 c.Assert(err, IsNil) 1801 1802 snapDev, err := devDB.Sign(asserts.SnapDeveloperType, map[string]interface{}{ 1803 "snap-id": "snap-id-1", 1804 "publisher-id": "dev-id1", 1805 "developers": []interface{}{ 1806 map[string]interface{}{ 1807 "developer-id": "dev-id2", 1808 "since": "2017-01-01T00:00:00.0Z", 1809 }, 1810 }, 1811 }, nil, "") 1812 c.Assert(err, IsNil) 1813 err = db.Check(snapDev) 1814 c.Assert(err, ErrorMatches, `snap-developer assertion for snap-id "snap-id-1" does not have a matching account assertion for the developer "dev-id2"`) 1815 } 1816 1817 func (sds *snapDevSuite) TestCheckMissingDeclaration(c *C) { 1818 storeDB, db := makeStoreAndCheckDB(c) 1819 devDB := setup3rdPartySigning(c, "dev-id1", storeDB, db) 1820 1821 headers := map[string]interface{}{ 1822 "authority-id": "dev-id1", 1823 "snap-id": "snap-id-1", 1824 "publisher-id": "dev-id1", 1825 } 1826 snapDev, err := devDB.Sign(asserts.SnapDeveloperType, headers, nil, "") 1827 c.Assert(err, IsNil) 1828 1829 err = db.Check(snapDev) 1830 c.Assert(err, ErrorMatches, `snap-developer assertion for snap id "snap-id-1" does not have a matching snap-declaration assertion`) 1831 } 1832 1833 func (sds *snapDevSuite) TestPrerequisitesNoDevelopers(c *C) { 1834 encoded := strings.Replace(sds.validEncoded, sds.developersLines, "", 1) 1835 assert, err := asserts.Decode([]byte(encoded)) 1836 c.Assert(err, IsNil) 1837 prereqs := assert.Prerequisites() 1838 sort.Sort(RefSlice(prereqs)) 1839 c.Assert(prereqs, DeepEquals, []*asserts.Ref{ 1840 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id1"}}, 1841 {Type: asserts.SnapDeclarationType, PrimaryKey: []string{"16", "snap-id-1"}}, 1842 }) 1843 } 1844 1845 func (sds *snapDevSuite) TestPrerequisitesWithDevelopers(c *C) { 1846 encoded := strings.Replace( 1847 sds.validEncoded, sds.developersLines, 1848 "developers:\n"+ 1849 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n"+ 1850 " -\n developer-id: dev-id3\n since: 2017-01-01T00:00:00.0Z\n", 1851 1) 1852 assert, err := asserts.Decode([]byte(encoded)) 1853 c.Assert(err, IsNil) 1854 prereqs := assert.Prerequisites() 1855 sort.Sort(RefSlice(prereqs)) 1856 c.Assert(prereqs, DeepEquals, []*asserts.Ref{ 1857 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id1"}}, 1858 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id2"}}, 1859 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id3"}}, 1860 {Type: asserts.SnapDeclarationType, PrimaryKey: []string{"16", "snap-id-1"}}, 1861 }) 1862 } 1863 1864 func (sds *snapDevSuite) TestPrerequisitesWithDeveloperRepeated(c *C) { 1865 encoded := strings.Replace( 1866 sds.validEncoded, sds.developersLines, 1867 "developers:\n"+ 1868 " -\n developer-id: dev-id2\n since: 2015-01-01T00:00:00.0Z\n until: 2016-01-01T00:00:00.0Z\n"+ 1869 " -\n developer-id: dev-id2\n since: 2017-01-01T00:00:00.0Z\n", 1870 1) 1871 assert, err := asserts.Decode([]byte(encoded)) 1872 c.Assert(err, IsNil) 1873 prereqs := assert.Prerequisites() 1874 sort.Sort(RefSlice(prereqs)) 1875 c.Assert(prereqs, DeepEquals, []*asserts.Ref{ 1876 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id1"}}, 1877 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id2"}}, 1878 {Type: asserts.SnapDeclarationType, PrimaryKey: []string{"16", "snap-id-1"}}, 1879 }) 1880 } 1881 1882 func (sds *snapDevSuite) TestPrerequisitesWithPublisherAsDeveloper(c *C) { 1883 encoded := strings.Replace( 1884 sds.validEncoded, sds.developersLines, 1885 "developers:\n -\n developer-id: dev-id1\n since: 2017-01-01T00:00:00.0Z\n", 1886 1) 1887 assert, err := asserts.Decode([]byte(encoded)) 1888 c.Assert(err, IsNil) 1889 prereqs := assert.Prerequisites() 1890 sort.Sort(RefSlice(prereqs)) 1891 c.Assert(prereqs, DeepEquals, []*asserts.Ref{ 1892 {Type: asserts.AccountType, PrimaryKey: []string{"dev-id1"}}, 1893 {Type: asserts.SnapDeclarationType, PrimaryKey: []string{"16", "snap-id-1"}}, 1894 }) 1895 } 1896 1897 type RefSlice []*asserts.Ref 1898 1899 func (s RefSlice) Len() int { 1900 return len(s) 1901 } 1902 1903 func (s RefSlice) Less(i, j int) bool { 1904 iref, jref := s[i], s[j] 1905 if v := strings.Compare(iref.Type.Name, jref.Type.Name); v != 0 { 1906 return v == -1 1907 } 1908 for n, ipk := range iref.PrimaryKey { 1909 jpk := jref.PrimaryKey[n] 1910 if v := strings.Compare(ipk, jpk); v != 0 { 1911 return v == -1 1912 } 1913 } 1914 return false 1915 } 1916 1917 func (s RefSlice) Swap(i, j int) { 1918 s[i], s[j] = s[j], s[i] 1919 }