github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/asserts/signtool/sign_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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 signtool_test
    21  
    22  import (
    23  	"encoding/json"
    24  	"testing"
    25  
    26  	. "gopkg.in/check.v1"
    27  
    28  	"github.com/snapcore/snapd/asserts"
    29  	"github.com/snapcore/snapd/asserts/assertstest"
    30  	"github.com/snapcore/snapd/asserts/signtool"
    31  )
    32  
    33  func TestSigntool(t *testing.T) { TestingT(t) }
    34  
    35  type signSuite struct {
    36  	keypairMgr asserts.KeypairManager
    37  	testKeyID  string
    38  }
    39  
    40  var _ = Suite(&signSuite{})
    41  
    42  func (s *signSuite) SetUpSuite(c *C) {
    43  	testKey, _ := assertstest.GenerateKey(752)
    44  
    45  	s.keypairMgr = asserts.NewMemoryKeypairManager()
    46  	s.keypairMgr.Put(testKey)
    47  	s.testKeyID = testKey.PublicKey().ID()
    48  }
    49  
    50  func expectedModelHeaders(a asserts.Assertion) map[string]interface{} {
    51  	m := map[string]interface{}{
    52  		"type":           "model",
    53  		"authority-id":   "user-id1",
    54  		"series":         "16",
    55  		"brand-id":       "user-id1",
    56  		"model":          "baz-3000",
    57  		"architecture":   "amd64",
    58  		"gadget":         "brand-gadget",
    59  		"kernel":         "baz-linux",
    60  		"store":          "brand-store",
    61  		"required-snaps": []interface{}{"foo", "bar"},
    62  		"timestamp":      "2015-11-25T20:00:00Z",
    63  	}
    64  	if a != nil {
    65  		m["sign-key-sha3-384"] = a.SignKeyID()
    66  	}
    67  	return m
    68  }
    69  
    70  func exampleJSON(overrides map[string]interface{}) []byte {
    71  	m := expectedModelHeaders(nil)
    72  	for k, v := range overrides {
    73  		if v == nil {
    74  			delete(m, k)
    75  		} else {
    76  			m[k] = v
    77  		}
    78  	}
    79  	b, err := json.Marshal(m)
    80  	if err != nil {
    81  		panic(err)
    82  	}
    83  	return b
    84  }
    85  
    86  func (s *signSuite) TestSignJSON(c *C) {
    87  	opts := signtool.Options{
    88  		KeyID: s.testKeyID,
    89  
    90  		Statement: exampleJSON(nil),
    91  	}
    92  
    93  	assertText, err := signtool.Sign(&opts, s.keypairMgr)
    94  	c.Assert(err, IsNil)
    95  
    96  	a, err := asserts.Decode(assertText)
    97  	c.Assert(err, IsNil)
    98  
    99  	c.Check(a.Type(), Equals, asserts.ModelType)
   100  	c.Check(a.Revision(), Equals, 0)
   101  	expectedHeaders := expectedModelHeaders(a)
   102  	c.Check(a.Headers(), DeepEquals, expectedHeaders)
   103  
   104  	for n, v := range a.Headers() {
   105  		c.Check(v, DeepEquals, expectedHeaders[n], Commentf(n))
   106  	}
   107  
   108  	c.Check(a.Body(), IsNil)
   109  }
   110  
   111  func (s *signSuite) TestSignJSONWithBodyAndRevision(c *C) {
   112  	statement := exampleJSON(map[string]interface{}{
   113  		"body":     "BODY",
   114  		"revision": "11",
   115  	})
   116  	opts := signtool.Options{
   117  		KeyID: s.testKeyID,
   118  
   119  		Statement: statement,
   120  	}
   121  
   122  	assertText, err := signtool.Sign(&opts, s.keypairMgr)
   123  	c.Assert(err, IsNil)
   124  
   125  	a, err := asserts.Decode(assertText)
   126  	c.Assert(err, IsNil)
   127  
   128  	c.Check(a.Type(), Equals, asserts.ModelType)
   129  	c.Check(a.Revision(), Equals, 11)
   130  
   131  	expectedHeaders := expectedModelHeaders(a)
   132  	expectedHeaders["revision"] = "11"
   133  	expectedHeaders["body-length"] = "4"
   134  
   135  	c.Check(a.Headers(), DeepEquals, expectedHeaders)
   136  
   137  	c.Check(a.Body(), DeepEquals, []byte("BODY"))
   138  }
   139  
   140  func (s *signSuite) TestSignJSONWithBodyAndComplementRevision(c *C) {
   141  	statement := exampleJSON(map[string]interface{}{
   142  		"body": "BODY",
   143  	})
   144  	opts := signtool.Options{
   145  		KeyID: s.testKeyID,
   146  
   147  		Statement: statement,
   148  		Complement: map[string]interface{}{
   149  			"revision": "11",
   150  		},
   151  	}
   152  
   153  	assertText, err := signtool.Sign(&opts, s.keypairMgr)
   154  	c.Assert(err, IsNil)
   155  
   156  	a, err := asserts.Decode(assertText)
   157  	c.Assert(err, IsNil)
   158  
   159  	c.Check(a.Type(), Equals, asserts.ModelType)
   160  	c.Check(a.Revision(), Equals, 11)
   161  
   162  	expectedHeaders := expectedModelHeaders(a)
   163  	expectedHeaders["revision"] = "11"
   164  	expectedHeaders["body-length"] = "4"
   165  
   166  	c.Check(a.Headers(), DeepEquals, expectedHeaders)
   167  
   168  	c.Check(a.Body(), DeepEquals, []byte("BODY"))
   169  }
   170  
   171  func (s *signSuite) TestSignJSONWithRevisionAndComplementBodyAndRepeatedType(c *C) {
   172  	statement := exampleJSON(map[string]interface{}{
   173  		"revision": "11",
   174  	})
   175  	opts := signtool.Options{
   176  		KeyID: s.testKeyID,
   177  
   178  		Statement: statement,
   179  		Complement: map[string]interface{}{
   180  			"type": "model",
   181  			"body": "BODY",
   182  		},
   183  	}
   184  
   185  	assertText, err := signtool.Sign(&opts, s.keypairMgr)
   186  	c.Assert(err, IsNil)
   187  
   188  	a, err := asserts.Decode(assertText)
   189  	c.Assert(err, IsNil)
   190  
   191  	c.Check(a.Type(), Equals, asserts.ModelType)
   192  	c.Check(a.Revision(), Equals, 11)
   193  
   194  	expectedHeaders := expectedModelHeaders(a)
   195  	expectedHeaders["revision"] = "11"
   196  	expectedHeaders["body-length"] = "4"
   197  
   198  	c.Check(a.Headers(), DeepEquals, expectedHeaders)
   199  
   200  	c.Check(a.Body(), DeepEquals, []byte("BODY"))
   201  }
   202  
   203  func (s *signSuite) TestSignErrors(c *C) {
   204  	opts := signtool.Options{
   205  		KeyID: s.testKeyID,
   206  	}
   207  
   208  	emptyList := []interface{}{}
   209  
   210  	tests := []struct {
   211  		expError        string
   212  		brokenStatement []byte
   213  		complement      map[string]interface{}
   214  	}{
   215  		{`cannot parse the assertion input as JSON:.*`,
   216  			[]byte("\x00"),
   217  			nil,
   218  		},
   219  		{`invalid assertion type: what`,
   220  			exampleJSON(map[string]interface{}{"type": "what"}),
   221  			nil,
   222  		},
   223  		{`assertion type must be a string, not: \[\]`,
   224  			exampleJSON(map[string]interface{}{"type": emptyList}),
   225  			nil,
   226  		},
   227  		{`missing assertion type header`,
   228  			exampleJSON(map[string]interface{}{"type": nil}),
   229  			nil,
   230  		},
   231  		{"revision should be positive: -10",
   232  			exampleJSON(map[string]interface{}{"revision": "-10"}),
   233  			nil,
   234  		},
   235  		{`"authority-id" header is mandatory`,
   236  			exampleJSON(map[string]interface{}{"authority-id": nil}),
   237  			nil,
   238  		},
   239  		{`body if specified must be a string`,
   240  			exampleJSON(map[string]interface{}{"body": emptyList}),
   241  			nil,
   242  		},
   243  		{`repeated assertion type does not match`,
   244  			exampleJSON(nil),
   245  			map[string]interface{}{"type": "foo"},
   246  		},
   247  		{`complementary header "kernel" clashes with assertion input`,
   248  			exampleJSON(nil),
   249  			map[string]interface{}{"kernel": "foo"},
   250  		},
   251  	}
   252  
   253  	for _, t := range tests {
   254  		fresh := opts
   255  
   256  		fresh.Statement = t.brokenStatement
   257  		fresh.Complement = t.complement
   258  
   259  		_, err := signtool.Sign(&fresh, s.keypairMgr)
   260  		c.Check(err, ErrorMatches, t.expError)
   261  	}
   262  }