github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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  }