github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/asserts/asserts_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2020 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  	"bytes"
    24  	"io"
    25  	"strings"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/asserts"
    30  )
    31  
    32  type assertsSuite struct{}
    33  
    34  var _ = Suite(&assertsSuite{})
    35  
    36  func (as *assertsSuite) TestType(c *C) {
    37  	c.Check(asserts.Type("test-only"), Equals, asserts.TestOnlyType)
    38  }
    39  
    40  func (as *assertsSuite) TestUnknown(c *C) {
    41  	c.Check(asserts.Type(""), IsNil)
    42  	c.Check(asserts.Type("unknown"), IsNil)
    43  }
    44  
    45  func (as *assertsSuite) TestTypeMaxSupportedFormat(c *C) {
    46  	c.Check(asserts.Type("test-only").MaxSupportedFormat(), Equals, 1)
    47  }
    48  
    49  func (as *assertsSuite) TestTypeNames(c *C) {
    50  	c.Check(asserts.TypeNames(), DeepEquals, []string{
    51  		"account",
    52  		"account-key",
    53  		"account-key-request",
    54  		"base-declaration",
    55  		"device-session-request",
    56  		"model",
    57  		"repair",
    58  		"serial",
    59  		"serial-request",
    60  		"snap-build",
    61  		"snap-declaration",
    62  		"snap-developer",
    63  		"snap-revision",
    64  		"store",
    65  		"system-user",
    66  		"test-only",
    67  		"test-only-2",
    68  		"test-only-decl",
    69  		"test-only-no-authority",
    70  		"test-only-no-authority-pk",
    71  		"test-only-rev",
    72  		"test-only-seq",
    73  		"validation",
    74  		"validation-set",
    75  	})
    76  }
    77  
    78  func (as *assertsSuite) TestMaxSupportedFormats(c *C) {
    79  	snapDeclMaxFormat := asserts.SnapDeclarationType.MaxSupportedFormat()
    80  	systemUserMaxFormat := asserts.SystemUserType.MaxSupportedFormat()
    81  	// sanity
    82  	c.Check(snapDeclMaxFormat >= 4, Equals, true)
    83  	c.Check(systemUserMaxFormat >= 1, Equals, true)
    84  	c.Check(asserts.MaxSupportedFormats(1), DeepEquals, map[string]int{
    85  		"snap-declaration": snapDeclMaxFormat,
    86  		"system-user":      systemUserMaxFormat,
    87  		"test-only":        1,
    88  		"test-only-seq":    2,
    89  	})
    90  
    91  	// all
    92  	maxFormats := asserts.MaxSupportedFormats(0)
    93  	c.Assert(maxFormats, HasLen, len(asserts.TypeNames()))
    94  	c.Check(maxFormats["test-only"], Equals, 1)
    95  	c.Check(maxFormats["test-only-2"], Equals, 0)
    96  	c.Check(maxFormats["snap-declaration"], Equals, snapDeclMaxFormat)
    97  }
    98  
    99  func (as *assertsSuite) TestSuggestFormat(c *C) {
   100  	fmtnum, err := asserts.SuggestFormat(asserts.Type("test-only-2"), nil, nil)
   101  	c.Assert(err, IsNil)
   102  	c.Check(fmtnum, Equals, 0)
   103  }
   104  
   105  func (as *assertsSuite) TestPrimaryKeyHelpers(c *C) {
   106  	headers, err := asserts.HeadersFromPrimaryKey(asserts.TestOnlyType, []string{"one"})
   107  	c.Assert(err, IsNil)
   108  	c.Check(headers, DeepEquals, map[string]string{
   109  		"primary-key": "one",
   110  	})
   111  
   112  	headers, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"bar", "baz"})
   113  	c.Assert(err, IsNil)
   114  	c.Check(headers, DeepEquals, map[string]string{
   115  		"pk1": "bar",
   116  		"pk2": "baz",
   117  	})
   118  
   119  	_, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"bar"})
   120  	c.Check(err, ErrorMatches, `primary key has wrong length for "test-only-2" assertion`)
   121  
   122  	_, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"", "baz"})
   123  	c.Check(err, ErrorMatches, `primary key "pk1" header cannot be empty`)
   124  
   125  	pk, err := asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers)
   126  	c.Assert(err, IsNil)
   127  	c.Check(pk, DeepEquals, []string{"bar", "baz"})
   128  
   129  	headers["other"] = "foo"
   130  	pk1, err := asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers)
   131  	c.Assert(err, IsNil)
   132  	c.Check(pk1, DeepEquals, pk)
   133  
   134  	delete(headers, "pk2")
   135  	_, err = asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers)
   136  	c.Check(err, ErrorMatches, `must provide primary key: pk2`)
   137  }
   138  
   139  func (as *assertsSuite) TestRef(c *C) {
   140  	ref := &asserts.Ref{
   141  		Type:       asserts.TestOnly2Type,
   142  		PrimaryKey: []string{"abc", "xyz"},
   143  	}
   144  	c.Check(ref.Unique(), Equals, "test-only-2/abc/xyz")
   145  }
   146  
   147  func (as *assertsSuite) TestRefString(c *C) {
   148  	ref := &asserts.Ref{
   149  		Type:       asserts.AccountType,
   150  		PrimaryKey: []string{"canonical"},
   151  	}
   152  
   153  	c.Check(ref.String(), Equals, "account (canonical)")
   154  
   155  	ref = &asserts.Ref{
   156  		Type:       asserts.SnapDeclarationType,
   157  		PrimaryKey: []string{"18", "SNAPID"},
   158  	}
   159  
   160  	c.Check(ref.String(), Equals, "snap-declaration (SNAPID; series:18)")
   161  
   162  	ref = &asserts.Ref{
   163  		Type:       asserts.ModelType,
   164  		PrimaryKey: []string{"18", "BRAND", "baz-3000"},
   165  	}
   166  
   167  	c.Check(ref.String(), Equals, "model (baz-3000; series:18 brand-id:BRAND)")
   168  
   169  	// broken primary key
   170  	ref = &asserts.Ref{
   171  		Type:       asserts.ModelType,
   172  		PrimaryKey: []string{"18"},
   173  	}
   174  	c.Check(ref.String(), Equals, "model (???)")
   175  
   176  	ref = &asserts.Ref{
   177  		Type: asserts.TestOnlyNoAuthorityType,
   178  	}
   179  	c.Check(ref.String(), Equals, "test-only-no-authority (-)")
   180  }
   181  
   182  func (as *assertsSuite) TestRefResolveError(c *C) {
   183  	ref := &asserts.Ref{
   184  		Type:       asserts.TestOnly2Type,
   185  		PrimaryKey: []string{"abc"},
   186  	}
   187  	_, err := ref.Resolve(nil)
   188  	c.Check(err, ErrorMatches, `"test-only-2" assertion reference primary key has the wrong length \(expected \[pk1 pk2\]\): \[abc\]`)
   189  }
   190  
   191  func (as *assertsSuite) TestAtRevisionString(c *C) {
   192  	ref := asserts.Ref{
   193  		Type:       asserts.AccountType,
   194  		PrimaryKey: []string{"canonical"},
   195  	}
   196  
   197  	at := &asserts.AtRevision{
   198  		Ref: ref,
   199  	}
   200  	c.Check(at.String(), Equals, "account (canonical) at revision 0")
   201  
   202  	at = &asserts.AtRevision{
   203  		Ref:      ref,
   204  		Revision: asserts.RevisionNotKnown,
   205  	}
   206  	c.Check(at.String(), Equals, "account (canonical)")
   207  }
   208  
   209  const exKeyID = "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij"
   210  
   211  const exampleEmptyBodyAllDefaults = "type: test-only\n" +
   212  	"authority-id: auth-id1\n" +
   213  	"primary-key: abc\n" +
   214  	"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
   215  	"\n\n" +
   216  	"AXNpZw=="
   217  
   218  func (as *assertsSuite) TestDecodeEmptyBodyAllDefaults(c *C) {
   219  	a, err := asserts.Decode([]byte(exampleEmptyBodyAllDefaults))
   220  	c.Assert(err, IsNil)
   221  	c.Check(a.Type(), Equals, asserts.TestOnlyType)
   222  	_, ok := a.(*asserts.TestOnly)
   223  	c.Check(ok, Equals, true)
   224  	c.Check(a.Revision(), Equals, 0)
   225  	c.Check(a.Format(), Equals, 0)
   226  	c.Check(a.Body(), IsNil)
   227  	c.Check(a.Header("header1"), IsNil)
   228  	c.Check(a.HeaderString("header1"), Equals, "")
   229  	c.Check(a.AuthorityID(), Equals, "auth-id1")
   230  	c.Check(a.SignKeyID(), Equals, exKeyID)
   231  }
   232  
   233  const exampleEmptyBody2NlNl = "type: test-only\n" +
   234  	"authority-id: auth-id1\n" +
   235  	"primary-key: xyz\n" +
   236  	"revision: 0\n" +
   237  	"body-length: 0\n" +
   238  	"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
   239  	"\n\n" +
   240  	"\n\n" +
   241  	"AXNpZw==\n"
   242  
   243  func (as *assertsSuite) TestDecodeEmptyBodyNormalize2NlNl(c *C) {
   244  	a, err := asserts.Decode([]byte(exampleEmptyBody2NlNl))
   245  	c.Assert(err, IsNil)
   246  	c.Check(a.Type(), Equals, asserts.TestOnlyType)
   247  	c.Check(a.Revision(), Equals, 0)
   248  	c.Check(a.Format(), Equals, 0)
   249  	c.Check(a.Body(), IsNil)
   250  }
   251  
   252  const exampleBodyAndExtraHeaders = "type: test-only\n" +
   253  	"format: 1\n" +
   254  	"authority-id: auth-id2\n" +
   255  	"primary-key: abc\n" +
   256  	"revision: 5\n" +
   257  	"header1: value1\n" +
   258  	"header2: value2\n" +
   259  	"body-length: 8\n" +
   260  	"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   261  	"THE-BODY" +
   262  	"\n\n" +
   263  	"AXNpZw==\n"
   264  
   265  func (as *assertsSuite) TestDecodeWithABodyAndExtraHeaders(c *C) {
   266  	a, err := asserts.Decode([]byte(exampleBodyAndExtraHeaders))
   267  	c.Assert(err, IsNil)
   268  	c.Check(a.Type(), Equals, asserts.TestOnlyType)
   269  	c.Check(a.AuthorityID(), Equals, "auth-id2")
   270  	c.Check(a.SignKeyID(), Equals, exKeyID)
   271  	c.Check(a.Header("primary-key"), Equals, "abc")
   272  	c.Check(a.Revision(), Equals, 5)
   273  	c.Check(a.Format(), Equals, 1)
   274  	c.Check(a.SupportedFormat(), Equals, true)
   275  	c.Check(a.Header("header1"), Equals, "value1")
   276  	c.Check(a.Header("header2"), Equals, "value2")
   277  	c.Check(a.Body(), DeepEquals, []byte("THE-BODY"))
   278  
   279  }
   280  
   281  const exampleUnsupportedFormat = "type: test-only\n" +
   282  	"format: 77\n" +
   283  	"authority-id: auth-id2\n" +
   284  	"primary-key: abc\n" +
   285  	"revision: 5\n" +
   286  	"header1: value1\n" +
   287  	"header2: value2\n" +
   288  	"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   289  	"AXNpZw==\n"
   290  
   291  func (as *assertsSuite) TestDecodeUnsupportedFormat(c *C) {
   292  	a, err := asserts.Decode([]byte(exampleUnsupportedFormat))
   293  	c.Assert(err, IsNil)
   294  	c.Check(a.Type(), Equals, asserts.TestOnlyType)
   295  	c.Check(a.AuthorityID(), Equals, "auth-id2")
   296  	c.Check(a.SignKeyID(), Equals, exKeyID)
   297  	c.Check(a.Header("primary-key"), Equals, "abc")
   298  	c.Check(a.Revision(), Equals, 5)
   299  	c.Check(a.Format(), Equals, 77)
   300  	c.Check(a.SupportedFormat(), Equals, false)
   301  }
   302  
   303  func (as *assertsSuite) TestDecodeGetSignatureBits(c *C) {
   304  	content := "type: test-only\n" +
   305  		"authority-id: auth-id1\n" +
   306  		"primary-key: xyz\n" +
   307  		"revision: 5\n" +
   308  		"header1: value1\n" +
   309  		"body-length: 8\n" +
   310  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   311  		"THE-BODY"
   312  	encoded := content +
   313  		"\n\n" +
   314  		"AXNpZw=="
   315  	a, err := asserts.Decode([]byte(encoded))
   316  	c.Assert(err, IsNil)
   317  	c.Check(a.Type(), Equals, asserts.TestOnlyType)
   318  	c.Check(a.AuthorityID(), Equals, "auth-id1")
   319  	c.Check(a.SignKeyID(), Equals, exKeyID)
   320  	cont, signature := a.Signature()
   321  	c.Check(signature, DeepEquals, []byte("AXNpZw=="))
   322  	c.Check(cont, DeepEquals, []byte(content))
   323  }
   324  
   325  func (as *assertsSuite) TestDecodeNoSignatureSplit(c *C) {
   326  	for _, encoded := range []string{"", "foo"} {
   327  		_, err := asserts.Decode([]byte(encoded))
   328  		c.Check(err, ErrorMatches, "assertion content/signature separator not found")
   329  	}
   330  }
   331  
   332  func (as *assertsSuite) TestDecodeHeaderParsingErrors(c *C) {
   333  	headerParsingErrorsTests := []struct{ encoded, expectedErr string }{
   334  		{string([]byte{255, '\n', '\n'}), "header is not utf8"},
   335  		{"foo: a\nbar\n\n", `header entry missing ':' separator: "bar"`},
   336  		{"TYPE: foo\n\n", `invalid header name: "TYPE"`},
   337  		{"foo: a\nbar:>\n\n", `header entry should have a space or newline \(for multiline\) before value: "bar:>"`},
   338  		{"foo: a\nbar:\n\n", `expected 4 chars nesting prefix after multiline introduction "bar:": EOF`},
   339  		{"foo: a\nbar:\nbaz: x\n\n", `expected 4 chars nesting prefix after multiline introduction "bar:": "baz: x"`},
   340  		{"foo: a:\nbar: b\nfoo: x\n\n", `repeated header: "foo"`},
   341  	}
   342  
   343  	for _, test := range headerParsingErrorsTests {
   344  		_, err := asserts.Decode([]byte(test.encoded))
   345  		c.Check(err, ErrorMatches, "parsing assertion headers: "+test.expectedErr)
   346  	}
   347  }
   348  
   349  func (as *assertsSuite) TestDecodeInvalid(c *C) {
   350  	keyIDHdr := "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n"
   351  	encoded := "type: test-only\n" +
   352  		"format: 0\n" +
   353  		"authority-id: auth-id\n" +
   354  		"primary-key: abc\n" +
   355  		"revision: 0\n" +
   356  		"body-length: 5\n" +
   357  		keyIDHdr +
   358  		"\n" +
   359  		"abcde" +
   360  		"\n\n" +
   361  		"AXNpZw=="
   362  
   363  	invalidAssertTests := []struct{ original, invalid, expectedErr string }{
   364  		{"body-length: 5", "body-length: z", `assertion: "body-length" header is not an integer: z`},
   365  		{"body-length: 5", "body-length: 3", "assertion body length and declared body-length don't match: 5 != 3"},
   366  		{"authority-id: auth-id\n", "", `assertion: "authority-id" header is mandatory`},
   367  		{"authority-id: auth-id\n", "authority-id: \n", `assertion: "authority-id" header should not be empty`},
   368  		{keyIDHdr, "", `assertion: "sign-key-sha3-384" header is mandatory`},
   369  		{keyIDHdr, "sign-key-sha3-384: \n", `assertion: "sign-key-sha3-384" header should not be empty`},
   370  		{keyIDHdr, "sign-key-sha3-384: $\n", `assertion: "sign-key-sha3-384" header cannot be decoded: .*`},
   371  		{keyIDHdr, "sign-key-sha3-384: eHl6\n", `assertion: "sign-key-sha3-384" header does not have the expected bit length: 24`},
   372  		{"AXNpZw==", "", "empty assertion signature"},
   373  		{"type: test-only\n", "", `assertion: "type" header is mandatory`},
   374  		{"type: test-only\n", "type: unknown\n", `unknown assertion type: "unknown"`},
   375  		{"revision: 0\n", "revision: Z\n", `assertion: "revision" header is not an integer: Z`},
   376  		{"revision: 0\n", "revision:\n  - 1\n", `assertion: "revision" header is not an integer: \[1\]`},
   377  		{"revision: 0\n", "revision: 00\n", `assertion: "revision" header has invalid prefix zeros: 00`},
   378  		{"revision: 0\n", "revision: -10\n", "assertion: revision should be positive: -10"},
   379  		{"revision: 0\n", "revision: 99999999999999999999\n", `assertion: "revision" header is out of range: 99999999999999999999`},
   380  		{"format: 0\n", "format: Z\n", `assertion: "format" header is not an integer: Z`},
   381  		{"format: 0\n", "format: -10\n", "assertion: format should be positive: -10"},
   382  		{"primary-key: abc\n", "", `assertion test-only: "primary-key" header is mandatory`},
   383  		{"primary-key: abc\n", "primary-key:\n  - abc\n", `assertion test-only: "primary-key" header must be a string`},
   384  		{"primary-key: abc\n", "primary-key: a/c\n", `assertion test-only: "primary-key" primary key header cannot contain '/'`},
   385  		{"abcde", "ab\xffde", "body is not utf8"},
   386  	}
   387  
   388  	for _, test := range invalidAssertTests {
   389  		invalid := strings.Replace(encoded, test.original, test.invalid, 1)
   390  		_, err := asserts.Decode([]byte(invalid))
   391  		c.Check(err, ErrorMatches, test.expectedErr)
   392  	}
   393  }
   394  
   395  func (as *assertsSuite) TestDecodeNoAuthorityInvalid(c *C) {
   396  	invalid := "type: test-only-no-authority\n" +
   397  		"authority-id: auth-id1\n" +
   398  		"hdr: FOO\n" +
   399  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" +
   400  		"\n\n" +
   401  		"openpgp c2ln"
   402  
   403  	_, err := asserts.Decode([]byte(invalid))
   404  	c.Check(err, ErrorMatches, `"test-only-no-authority" assertion cannot have authority-id set`)
   405  }
   406  
   407  func checkContent(c *C, a asserts.Assertion, encoded string) {
   408  	expected, err := asserts.Decode([]byte(encoded))
   409  	c.Assert(err, IsNil)
   410  	expectedCont, _ := expected.Signature()
   411  
   412  	cont, _ := a.Signature()
   413  	c.Check(cont, DeepEquals, expectedCont)
   414  }
   415  
   416  func (as *assertsSuite) TestEncoderDecoderHappy(c *C) {
   417  	stream := new(bytes.Buffer)
   418  	enc := asserts.NewEncoder(stream)
   419  	enc.WriteEncoded([]byte(exampleEmptyBody2NlNl))
   420  	enc.WriteEncoded([]byte(exampleBodyAndExtraHeaders))
   421  	enc.WriteEncoded([]byte(exampleEmptyBodyAllDefaults))
   422  
   423  	decoder := asserts.NewDecoder(stream)
   424  	a, err := decoder.Decode()
   425  	c.Assert(err, IsNil)
   426  	c.Check(a.Type(), Equals, asserts.TestOnlyType)
   427  	_, ok := a.(*asserts.TestOnly)
   428  	c.Check(ok, Equals, true)
   429  	checkContent(c, a, exampleEmptyBody2NlNl)
   430  
   431  	a, err = decoder.Decode()
   432  	c.Assert(err, IsNil)
   433  	checkContent(c, a, exampleBodyAndExtraHeaders)
   434  
   435  	a, err = decoder.Decode()
   436  	c.Assert(err, IsNil)
   437  	checkContent(c, a, exampleEmptyBodyAllDefaults)
   438  
   439  	a, err = decoder.Decode()
   440  	c.Assert(err, Equals, io.EOF)
   441  }
   442  
   443  func (as *assertsSuite) TestDecodeEmptyStream(c *C) {
   444  	stream := new(bytes.Buffer)
   445  	decoder := asserts.NewDecoder(stream)
   446  	_, err := decoder.Decode()
   447  	c.Check(err, Equals, io.EOF)
   448  }
   449  
   450  func (as *assertsSuite) TestDecoderHappyWithSeparatorsVariations(c *C) {
   451  	streams := []string{
   452  		exampleBodyAndExtraHeaders,
   453  		exampleEmptyBody2NlNl,
   454  		exampleEmptyBodyAllDefaults,
   455  	}
   456  
   457  	for _, streamData := range streams {
   458  		stream := bytes.NewBufferString(streamData)
   459  		decoder := asserts.NewDecoderStressed(stream, 16, 1024, 1024, 1024)
   460  		a, err := decoder.Decode()
   461  		c.Assert(err, IsNil, Commentf("stream: %q", streamData))
   462  
   463  		checkContent(c, a, streamData)
   464  
   465  		a, err = decoder.Decode()
   466  		c.Check(a, IsNil)
   467  		c.Check(err, Equals, io.EOF, Commentf("stream: %q", streamData))
   468  	}
   469  }
   470  
   471  func (as *assertsSuite) TestDecoderHappyWithTrailerDoubleNewlines(c *C) {
   472  	streams := []string{
   473  		exampleBodyAndExtraHeaders,
   474  		exampleEmptyBody2NlNl,
   475  		exampleEmptyBodyAllDefaults,
   476  	}
   477  
   478  	for _, streamData := range streams {
   479  		stream := bytes.NewBufferString(streamData)
   480  		if strings.HasSuffix(streamData, "\n") {
   481  			stream.WriteString("\n")
   482  		} else {
   483  			stream.WriteString("\n\n")
   484  		}
   485  
   486  		decoder := asserts.NewDecoderStressed(stream, 16, 1024, 1024, 1024)
   487  		a, err := decoder.Decode()
   488  		c.Assert(err, IsNil, Commentf("stream: %q", streamData))
   489  
   490  		checkContent(c, a, streamData)
   491  
   492  		a, err = decoder.Decode()
   493  		c.Check(a, IsNil)
   494  		c.Check(err, Equals, io.EOF, Commentf("stream: %q", streamData))
   495  	}
   496  }
   497  
   498  func (as *assertsSuite) TestDecoderUnexpectedEOF(c *C) {
   499  	streamData := exampleBodyAndExtraHeaders + "\n" + exampleEmptyBodyAllDefaults
   500  	fstHeadEnd := strings.Index(exampleBodyAndExtraHeaders, "\n\n")
   501  	sndHeadEnd := len(exampleBodyAndExtraHeaders) + 1 + strings.Index(exampleEmptyBodyAllDefaults, "\n\n")
   502  
   503  	for _, brk := range []int{1, fstHeadEnd / 2, fstHeadEnd, fstHeadEnd + 1, fstHeadEnd + 2, fstHeadEnd + 6} {
   504  		stream := bytes.NewBufferString(streamData[:brk])
   505  		decoder := asserts.NewDecoderStressed(stream, 16, 1024, 1024, 1024)
   506  		_, err := decoder.Decode()
   507  		c.Check(err, Equals, io.ErrUnexpectedEOF, Commentf("brk: %d", brk))
   508  	}
   509  
   510  	for _, brk := range []int{sndHeadEnd, sndHeadEnd + 1} {
   511  		stream := bytes.NewBufferString(streamData[:brk])
   512  		decoder := asserts.NewDecoder(stream)
   513  		_, err := decoder.Decode()
   514  		c.Assert(err, IsNil)
   515  
   516  		_, err = decoder.Decode()
   517  		c.Check(err, Equals, io.ErrUnexpectedEOF, Commentf("brk: %d", brk))
   518  	}
   519  }
   520  
   521  func (as *assertsSuite) TestDecoderBrokenBodySeparation(c *C) {
   522  	streamData := strings.Replace(exampleBodyAndExtraHeaders, "THE-BODY\n\n", "THE-BODY", 1)
   523  	decoder := asserts.NewDecoder(bytes.NewBufferString(streamData))
   524  	_, err := decoder.Decode()
   525  	c.Assert(err, ErrorMatches, "missing content/signature separator")
   526  
   527  	streamData = strings.Replace(exampleBodyAndExtraHeaders, "THE-BODY\n\n", "THE-BODY\n", 1)
   528  	decoder = asserts.NewDecoder(bytes.NewBufferString(streamData))
   529  	_, err = decoder.Decode()
   530  	c.Assert(err, ErrorMatches, "missing content/signature separator")
   531  }
   532  
   533  func (as *assertsSuite) TestDecoderHeadTooBig(c *C) {
   534  	decoder := asserts.NewDecoderStressed(bytes.NewBufferString(exampleBodyAndExtraHeaders), 4, 4, 1024, 1024)
   535  	_, err := decoder.Decode()
   536  	c.Assert(err, ErrorMatches, `error reading assertion headers: maximum size exceeded while looking for delimiter "\\n\\n"`)
   537  }
   538  
   539  func (as *assertsSuite) TestDecoderBodyTooBig(c *C) {
   540  	decoder := asserts.NewDecoderStressed(bytes.NewBufferString(exampleBodyAndExtraHeaders), 1024, 1024, 5, 1024)
   541  	_, err := decoder.Decode()
   542  	c.Assert(err, ErrorMatches, "assertion body length 8 exceeds maximum body size")
   543  }
   544  
   545  func (as *assertsSuite) TestDecoderSignatureTooBig(c *C) {
   546  	decoder := asserts.NewDecoderStressed(bytes.NewBufferString(exampleBodyAndExtraHeaders), 4, 1024, 1024, 7)
   547  	_, err := decoder.Decode()
   548  	c.Assert(err, ErrorMatches, `error reading assertion signature: maximum size exceeded while looking for delimiter "\\n\\n"`)
   549  }
   550  
   551  func (as *assertsSuite) TestDecoderDefaultMaxBodySize(c *C) {
   552  	enc := strings.Replace(exampleBodyAndExtraHeaders, "body-length: 8", "body-length: 2097153", 1)
   553  	decoder := asserts.NewDecoder(bytes.NewBufferString(enc))
   554  	_, err := decoder.Decode()
   555  	c.Assert(err, ErrorMatches, "assertion body length 2097153 exceeds maximum body size")
   556  }
   557  
   558  func (as *assertsSuite) TestDecoderWithTypeMaxBodySize(c *C) {
   559  	ex1 := strings.Replace(exampleBodyAndExtraHeaders, "body-length: 8", "body-length: 2097152", 1)
   560  	ex1 = strings.Replace(ex1, "THE-BODY", strings.Repeat("B", 2*1024*1024), 1)
   561  	ex1toobig := strings.Replace(exampleBodyAndExtraHeaders, "body-length: 8", "body-length: 2097153", 1)
   562  	ex1toobig = strings.Replace(ex1toobig, "THE-BODY", strings.Repeat("B", 2*1024*1024+1), 1)
   563  	const ex2 = `type: test-only-2
   564  authority-id: auth-id1
   565  pk1: foo
   566  pk2: bar
   567  body-length: 3
   568  sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij
   569  
   570  XYZ
   571  
   572  AXNpZw==`
   573  
   574  	decoder := asserts.NewDecoderWithTypeMaxBodySize(bytes.NewBufferString(ex1+"\n"+ex2), map[*asserts.AssertionType]int{
   575  		asserts.TestOnly2Type: 3,
   576  	})
   577  	a1, err := decoder.Decode()
   578  	c.Assert(err, IsNil)
   579  	c.Check(a1.Body(), HasLen, 2*1024*1024)
   580  	a2, err := decoder.Decode()
   581  	c.Assert(err, IsNil)
   582  	c.Check(a2.Body(), DeepEquals, []byte("XYZ"))
   583  
   584  	decoder = asserts.NewDecoderWithTypeMaxBodySize(bytes.NewBufferString(ex1+"\n"+ex2), map[*asserts.AssertionType]int{
   585  		asserts.TestOnly2Type: 2,
   586  	})
   587  	a1, err = decoder.Decode()
   588  	c.Assert(err, IsNil)
   589  	c.Check(a1.Body(), HasLen, 2*1024*1024)
   590  	_, err = decoder.Decode()
   591  	c.Assert(err, ErrorMatches, `assertion body length 3 exceeds maximum body size 2 for "test-only-2" assertions`)
   592  
   593  	decoder = asserts.NewDecoderWithTypeMaxBodySize(bytes.NewBufferString(ex2+"\n\n"+ex1toobig), map[*asserts.AssertionType]int{
   594  		asserts.TestOnly2Type: 3,
   595  	})
   596  	a2, err = decoder.Decode()
   597  	c.Assert(err, IsNil)
   598  	c.Check(a2.Body(), DeepEquals, []byte("XYZ"))
   599  	_, err = decoder.Decode()
   600  	c.Assert(err, ErrorMatches, "assertion body length 2097153 exceeds maximum body size")
   601  }
   602  
   603  func (as *assertsSuite) TestEncode(c *C) {
   604  	encoded := []byte("type: test-only\n" +
   605  		"authority-id: auth-id2\n" +
   606  		"primary-key: xyz\n" +
   607  		"revision: 5\n" +
   608  		"header1: value1\n" +
   609  		"header2: value2\n" +
   610  		"body-length: 8\n" +
   611  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   612  		"THE-BODY" +
   613  		"\n\n" +
   614  		"AXNpZw==")
   615  	a, err := asserts.Decode(encoded)
   616  	c.Assert(err, IsNil)
   617  	encodeRes := asserts.Encode(a)
   618  	c.Check(encodeRes, DeepEquals, encoded)
   619  }
   620  
   621  func (as *assertsSuite) TestEncoderOK(c *C) {
   622  	encoded := []byte("type: test-only\n" +
   623  		"authority-id: auth-id2\n" +
   624  		"primary-key: xyzyz\n" +
   625  		"revision: 5\n" +
   626  		"header1: value1\n" +
   627  		"header2: value2\n" +
   628  		"body-length: 8\n" +
   629  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   630  		"THE-BODY" +
   631  		"\n\n" +
   632  		"AXNpZw==")
   633  	a0, err := asserts.Decode(encoded)
   634  	c.Assert(err, IsNil)
   635  	cont0, _ := a0.Signature()
   636  
   637  	stream := new(bytes.Buffer)
   638  	enc := asserts.NewEncoder(stream)
   639  	enc.Encode(a0)
   640  
   641  	c.Check(bytes.HasSuffix(stream.Bytes(), []byte{'\n'}), Equals, true)
   642  
   643  	dec := asserts.NewDecoder(stream)
   644  	a1, err := dec.Decode()
   645  	c.Assert(err, IsNil)
   646  
   647  	cont1, _ := a1.Signature()
   648  	c.Check(cont1, DeepEquals, cont0)
   649  }
   650  
   651  func (as *assertsSuite) TestEncoderSingleDecodeOK(c *C) {
   652  	encoded := []byte("type: test-only\n" +
   653  		"authority-id: auth-id2\n" +
   654  		"primary-key: abc\n" +
   655  		"revision: 5\n" +
   656  		"header1: value1\n" +
   657  		"header2: value2\n" +
   658  		"body-length: 8\n" +
   659  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   660  		"THE-BODY" +
   661  		"\n\n" +
   662  		"AXNpZw==")
   663  	a0, err := asserts.Decode(encoded)
   664  	c.Assert(err, IsNil)
   665  	cont0, _ := a0.Signature()
   666  
   667  	stream := new(bytes.Buffer)
   668  	enc := asserts.NewEncoder(stream)
   669  	enc.Encode(a0)
   670  
   671  	a1, err := asserts.Decode(stream.Bytes())
   672  	c.Assert(err, IsNil)
   673  
   674  	cont1, _ := a1.Signature()
   675  	c.Check(cont1, DeepEquals, cont0)
   676  }
   677  
   678  func (as *assertsSuite) TestSignFormatSanityEmptyBody(c *C) {
   679  	headers := map[string]interface{}{
   680  		"authority-id": "auth-id1",
   681  		"primary-key":  "0",
   682  	}
   683  	a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1)
   684  	c.Assert(err, IsNil)
   685  
   686  	_, err = asserts.Decode(asserts.Encode(a))
   687  	c.Check(err, IsNil)
   688  }
   689  
   690  func (as *assertsSuite) TestSignFormatSanityNonEmptyBody(c *C) {
   691  	headers := map[string]interface{}{
   692  		"authority-id": "auth-id1",
   693  		"primary-key":  "0",
   694  	}
   695  	body := []byte("THE-BODY")
   696  	a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, body, testPrivKey1)
   697  	c.Assert(err, IsNil)
   698  	c.Check(a.Body(), DeepEquals, body)
   699  
   700  	decoded, err := asserts.Decode(asserts.Encode(a))
   701  	c.Assert(err, IsNil)
   702  	c.Check(decoded.Body(), DeepEquals, body)
   703  }
   704  
   705  func (as *assertsSuite) TestSignFormatSanitySupportMultilineHeaderValues(c *C) {
   706  	headers := map[string]interface{}{
   707  		"authority-id": "auth-id1",
   708  		"primary-key":  "0",
   709  	}
   710  
   711  	multilineVals := []string{
   712  		"a\n",
   713  		"\na",
   714  		"a\n\b\nc",
   715  		"a\n\b\nc\n",
   716  		"\na\n",
   717  		"\n\na\n\nb\n\nc",
   718  	}
   719  
   720  	for _, multilineVal := range multilineVals {
   721  		headers["multiline"] = multilineVal
   722  		if len(multilineVal)%2 == 1 {
   723  			headers["odd"] = "true"
   724  		}
   725  
   726  		a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1)
   727  		c.Assert(err, IsNil)
   728  
   729  		decoded, err := asserts.Decode(asserts.Encode(a))
   730  		c.Assert(err, IsNil)
   731  
   732  		c.Check(decoded.Header("multiline"), Equals, multilineVal)
   733  	}
   734  }
   735  
   736  func (as *assertsSuite) TestSignFormatAndRevision(c *C) {
   737  	headers := map[string]interface{}{
   738  		"authority-id": "auth-id1",
   739  		"primary-key":  "0",
   740  		"format":       "1",
   741  		"revision":     "11",
   742  	}
   743  
   744  	a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1)
   745  	c.Assert(err, IsNil)
   746  
   747  	c.Check(a.Revision(), Equals, 11)
   748  	c.Check(a.Format(), Equals, 1)
   749  	c.Check(a.SupportedFormat(), Equals, true)
   750  
   751  	a1, err := asserts.Decode(asserts.Encode(a))
   752  	c.Assert(err, IsNil)
   753  
   754  	c.Check(a1.Revision(), Equals, 11)
   755  	c.Check(a1.Format(), Equals, 1)
   756  	c.Check(a1.SupportedFormat(), Equals, true)
   757  }
   758  
   759  func (as *assertsSuite) TestSignBodyIsUTF8Text(c *C) {
   760  	headers := map[string]interface{}{
   761  		"authority-id": "auth-id1",
   762  		"primary-key":  "0",
   763  	}
   764  	_, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, []byte{'\xff'}, testPrivKey1)
   765  	c.Assert(err, ErrorMatches, "assertion body is not utf8")
   766  }
   767  
   768  func (as *assertsSuite) TestHeaders(c *C) {
   769  	encoded := []byte("type: test-only\n" +
   770  		"authority-id: auth-id2\n" +
   771  		"primary-key: abc\n" +
   772  		"revision: 5\n" +
   773  		"header1: value1\n" +
   774  		"header2: value2\n" +
   775  		"body-length: 8\n" +
   776  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   777  		"THE-BODY" +
   778  		"\n\n" +
   779  		"AXNpZw==")
   780  	a, err := asserts.Decode(encoded)
   781  	c.Assert(err, IsNil)
   782  
   783  	hs := a.Headers()
   784  	c.Check(hs, DeepEquals, map[string]interface{}{
   785  		"type":              "test-only",
   786  		"authority-id":      "auth-id2",
   787  		"primary-key":       "abc",
   788  		"revision":          "5",
   789  		"header1":           "value1",
   790  		"header2":           "value2",
   791  		"body-length":       "8",
   792  		"sign-key-sha3-384": exKeyID,
   793  	})
   794  }
   795  
   796  func (as *assertsSuite) TestHeadersReturnsCopy(c *C) {
   797  	encoded := []byte("type: test-only\n" +
   798  		"authority-id: auth-id2\n" +
   799  		"primary-key: xyz\n" +
   800  		"revision: 5\n" +
   801  		"header1: value1\n" +
   802  		"header2: value2\n" +
   803  		"body-length: 8\n" +
   804  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   805  		"THE-BODY" +
   806  		"\n\n" +
   807  		"AXNpZw==")
   808  	a, err := asserts.Decode(encoded)
   809  	c.Assert(err, IsNil)
   810  
   811  	hs := a.Headers()
   812  	// casual later result mutation doesn't trip us
   813  	delete(hs, "primary-key")
   814  	c.Check(a.Header("primary-key"), Equals, "xyz")
   815  }
   816  
   817  func (as *assertsSuite) TestAssembleRoundtrip(c *C) {
   818  	encoded := []byte("type: test-only\n" +
   819  		"format: 1\n" +
   820  		"authority-id: auth-id2\n" +
   821  		"primary-key: abc\n" +
   822  		"revision: 5\n" +
   823  		"header1: value1\n" +
   824  		"header2: value2\n" +
   825  		"body-length: 8\n" +
   826  		"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
   827  		"THE-BODY" +
   828  		"\n\n" +
   829  		"AXNpZw==")
   830  	a, err := asserts.Decode(encoded)
   831  	c.Assert(err, IsNil)
   832  
   833  	cont, sig := a.Signature()
   834  	reassembled, err := asserts.Assemble(a.Headers(), a.Body(), cont, sig)
   835  	c.Assert(err, IsNil)
   836  
   837  	c.Check(reassembled.Headers(), DeepEquals, a.Headers())
   838  	c.Check(reassembled.Body(), DeepEquals, a.Body())
   839  
   840  	reassembledEncoded := asserts.Encode(reassembled)
   841  	c.Check(reassembledEncoded, DeepEquals, encoded)
   842  }
   843  
   844  func (as *assertsSuite) TestSignKeyID(c *C) {
   845  	headers := map[string]interface{}{
   846  		"authority-id": "auth-id1",
   847  		"primary-key":  "0",
   848  	}
   849  	a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1)
   850  	c.Assert(err, IsNil)
   851  
   852  	keyID := a.SignKeyID()
   853  	c.Check(keyID, Equals, testPrivKey1.PublicKey().ID())
   854  }
   855  
   856  func (as *assertsSuite) TestSelfRef(c *C) {
   857  	headers := map[string]interface{}{
   858  		"authority-id": "auth-id1",
   859  		"primary-key":  "0",
   860  	}
   861  	a1, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1)
   862  	c.Assert(err, IsNil)
   863  
   864  	c.Check(a1.Ref(), DeepEquals, &asserts.Ref{
   865  		Type:       asserts.TestOnlyType,
   866  		PrimaryKey: []string{"0"},
   867  	})
   868  
   869  	c.Check(a1.At(), DeepEquals, &asserts.AtRevision{
   870  		Ref: asserts.Ref{
   871  			Type:       asserts.TestOnlyType,
   872  			PrimaryKey: []string{"0"},
   873  		},
   874  		Revision: 0,
   875  	})
   876  
   877  	headers = map[string]interface{}{
   878  		"authority-id": "auth-id1",
   879  		"pk1":          "a",
   880  		"pk2":          "b",
   881  		"revision":     "1",
   882  	}
   883  	a2, err := asserts.AssembleAndSignInTest(asserts.TestOnly2Type, headers, nil, testPrivKey1)
   884  	c.Assert(err, IsNil)
   885  
   886  	c.Check(a2.Ref(), DeepEquals, &asserts.Ref{
   887  		Type:       asserts.TestOnly2Type,
   888  		PrimaryKey: []string{"a", "b"},
   889  	})
   890  
   891  	c.Check(a2.At(), DeepEquals, &asserts.AtRevision{
   892  		Ref: asserts.Ref{
   893  			Type:       asserts.TestOnly2Type,
   894  			PrimaryKey: []string{"a", "b"},
   895  		},
   896  		Revision: 1,
   897  	})
   898  }
   899  
   900  func (as *assertsSuite) TestAssembleHeadersCheck(c *C) {
   901  	cont := []byte("type: test-only\n" +
   902  		"authority-id: auth-id2\n" +
   903  		"primary-key: abc\n" +
   904  		"revision: 5")
   905  	headers := map[string]interface{}{
   906  		"type":         "test-only",
   907  		"authority-id": "auth-id2",
   908  		"primary-key":  "abc",
   909  		"revision":     5, // must be a string actually!
   910  	}
   911  
   912  	_, err := asserts.Assemble(headers, nil, cont, nil)
   913  	c.Check(err, ErrorMatches, `header "revision": header values must be strings or nested lists or maps with strings as the only scalars: 5`)
   914  }
   915  
   916  func (as *assertsSuite) TestSignWithoutAuthorityMisuse(c *C) {
   917  	_, err := asserts.SignWithoutAuthority(asserts.TestOnlyType, nil, nil, testPrivKey1)
   918  	c.Check(err, ErrorMatches, `cannot sign assertions needing a definite authority with SignWithoutAuthority`)
   919  
   920  	_, err = asserts.SignWithoutAuthority(asserts.TestOnlyNoAuthorityType,
   921  		map[string]interface{}{
   922  			"authority-id": "auth-id1",
   923  			"hdr":          "FOO",
   924  		}, nil, testPrivKey1)
   925  	c.Check(err, ErrorMatches, `"test-only-no-authority" assertion cannot have authority-id set`)
   926  }
   927  
   928  func (ss *serialSuite) TestSignatureCheckError(c *C) {
   929  	sreq, err := asserts.SignWithoutAuthority(asserts.TestOnlyNoAuthorityType,
   930  		map[string]interface{}{
   931  			"hdr": "FOO",
   932  		}, nil, testPrivKey1)
   933  	c.Assert(err, IsNil)
   934  
   935  	err = asserts.SignatureCheck(sreq, testPrivKey2.PublicKey())
   936  	c.Check(err, ErrorMatches, `failed signature verification:.*`)
   937  }
   938  
   939  func (as *assertsSuite) TestWithAuthority(c *C) {
   940  	withAuthority := []string{
   941  		"account",
   942  		"account-key",
   943  		"base-declaration",
   944  		"store",
   945  		"snap-declaration",
   946  		"snap-build",
   947  		"snap-revision",
   948  		"snap-developer",
   949  		"model",
   950  		"serial",
   951  		"system-user",
   952  		"validation",
   953  		"validation-set",
   954  		"repair",
   955  	}
   956  	c.Check(withAuthority, HasLen, asserts.NumAssertionType-3) // excluding device-session-request, serial-request, account-key-request
   957  	for _, name := range withAuthority {
   958  		typ := asserts.Type(name)
   959  		_, err := asserts.AssembleAndSignInTest(typ, nil, nil, testPrivKey1)
   960  		c.Check(err, ErrorMatches, `"authority-id" header is mandatory`)
   961  	}
   962  }
   963  
   964  func (as *assertsSuite) TestSequenceForming(c *C) {
   965  	sequenceForming := []string{
   966  		"repair",
   967  		"validation-set",
   968  	}
   969  	for _, name := range sequenceForming {
   970  		typ := asserts.Type(name)
   971  		c.Check(typ.SequenceForming(), Equals, true)
   972  	}
   973  
   974  	c.Check(asserts.SnapDeclarationType.SequenceForming(), Equals, false)
   975  }
   976  
   977  func (as *assertsSuite) TestHeadersFromSequenceKey(c *C) {
   978  	headers, err := asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{"one"})
   979  	c.Assert(err, IsNil)
   980  	c.Check(headers, DeepEquals, map[string]string{"n": "one"})
   981  
   982  	_, err = asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{"one", "two"})
   983  	c.Check(err, ErrorMatches, `sequence key has wrong length for "test-only-seq" assertion`)
   984  
   985  	_, err = asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{})
   986  	c.Check(err, ErrorMatches, `sequence key has wrong length for "test-only-seq" assertion`)
   987  
   988  	_, err = asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{""})
   989  	c.Check(err, ErrorMatches, `sequence key "n" header cannot be empty`)
   990  }
   991  
   992  func (as *assertsSuite) TestAtSequenceString(c *C) {
   993  	atSeq := asserts.AtSequence{
   994  		Type:        asserts.ValidationSetType,
   995  		SequenceKey: []string{"16", "canonical", "foo"},
   996  		Sequence:    8,
   997  		Revision:    2,
   998  	}
   999  	c.Check(atSeq.String(), Equals, "validation-set canonical/foo/8 at revision 2")
  1000  
  1001  	// Sequence number not set
  1002  	atSeq = asserts.AtSequence{
  1003  		Type:        asserts.ValidationSetType,
  1004  		SequenceKey: []string{"16", "canonical", "foo"},
  1005  		Revision:    asserts.RevisionNotKnown,
  1006  	}
  1007  	c.Check(atSeq.String(), Equals, "validation-set canonical/foo")
  1008  
  1009  	atSeq = asserts.AtSequence{
  1010  		Type:        asserts.ValidationSetType,
  1011  		SequenceKey: []string{"16", "canonical", "foo"},
  1012  		Sequence:    8,
  1013  		Pinned:      true,
  1014  		Revision:    2,
  1015  	}
  1016  	c.Check(atSeq.String(), Equals, "validation-set canonical/foo=8 at revision 2")
  1017  
  1018  	atSeq = asserts.AtSequence{
  1019  		Type:        asserts.ValidationSetType,
  1020  		SequenceKey: []string{"16", "canonical"},
  1021  		Revision:    2,
  1022  	}
  1023  	c.Check(atSeq.String(), Equals, "validation-set ??? at revision 2")
  1024  }
  1025  
  1026  func (as *assertsSuite) TestAtSequenceUnique(c *C) {
  1027  	atSeq := asserts.AtSequence{
  1028  		Type:        asserts.ValidationSetType,
  1029  		SequenceKey: []string{"16", "canonical", "foo"},
  1030  		Sequence:    8,
  1031  		Revision:    2,
  1032  	}
  1033  	c.Check(atSeq.Unique(), Equals, "validation-set/16/canonical/foo")
  1034  
  1035  	// not a valid sequence-key (but Unique() doesn't care).
  1036  	atSeq = asserts.AtSequence{
  1037  		Type:        asserts.ValidationSetType,
  1038  		SequenceKey: []string{"16", "canonical"},
  1039  	}
  1040  	c.Check(atSeq.Unique(), Equals, "validation-set/16/canonical")
  1041  }
  1042  
  1043  func (as *assertsSuite) TestAtSequenceResolveError(c *C) {
  1044  	atSeq := asserts.AtSequence{
  1045  		Type:        asserts.ValidationSetType,
  1046  		SequenceKey: []string{"abc"},
  1047  		Sequence:    1,
  1048  	}
  1049  	_, err := atSeq.Resolve(nil)
  1050  	c.Check(err, ErrorMatches, `"validation-set" assertion reference primary key has the wrong length \(expected \[series account-id name sequence\]\): \[abc 1\]`)
  1051  
  1052  	atSeq = asserts.AtSequence{
  1053  		Type:        asserts.ValidationSetType,
  1054  		SequenceKey: []string{"16", "canonical", "foo"},
  1055  	}
  1056  	_, err = atSeq.Resolve(nil)
  1057  	c.Assert(err, DeepEquals, &asserts.NotFoundError{
  1058  		Type: asserts.ValidationSetType,
  1059  		Headers: map[string]string{
  1060  			"series":     "16",
  1061  			"account-id": "canonical",
  1062  			"name":       "foo",
  1063  		},
  1064  	})
  1065  }
  1066  
  1067  func (as *assertsSuite) TestAtSequenceResolve(c *C) {
  1068  	atSeq := asserts.AtSequence{
  1069  		Type:        asserts.TestOnlySeqType,
  1070  		SequenceKey: []string{"foo"},
  1071  		Sequence:    3,
  1072  	}
  1073  	a, err := atSeq.Resolve(func(atype *asserts.AssertionType, hdrs map[string]string) (asserts.Assertion, error) {
  1074  		c.Assert(atype, Equals, asserts.TestOnlySeqType)
  1075  		c.Assert(hdrs, DeepEquals, map[string]string{
  1076  			"n":        "foo",
  1077  			"sequence": "3",
  1078  		})
  1079  		encoded := []byte("type: test-only-seq\n" +
  1080  			"format: 1\n" +
  1081  			"authority-id: auth-id2\n" +
  1082  			"n: abc\n" +
  1083  			"revision: 5\n" +
  1084  			"sequence: 3\n" +
  1085  			"sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" +
  1086  			"\n\n" +
  1087  			"AXNpZw==")
  1088  		a, err := asserts.Decode(encoded)
  1089  		return a, err
  1090  	})
  1091  	c.Assert(err, IsNil)
  1092  	c.Assert(a, NotNil)
  1093  	c.Check(a.Type().Name, Equals, "test-only-seq")
  1094  }