github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/boot/bootchain_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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 boot_test
    21  
    22  import (
    23  	"encoding/json"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"sort"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/boot"
    32  	"github.com/snapcore/snapd/bootloader"
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/secboot"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  type bootchainSuite struct {
    39  	testutil.BaseTest
    40  
    41  	rootDir string
    42  }
    43  
    44  var _ = Suite(&bootchainSuite{})
    45  
    46  func (s *bootchainSuite) SetUpTest(c *C) {
    47  	s.BaseTest.SetUpTest(c)
    48  	s.rootDir = c.MkDir()
    49  	s.AddCleanup(func() { dirs.SetRootDir("/") })
    50  	dirs.SetRootDir(s.rootDir)
    51  
    52  	c.Assert(os.MkdirAll(filepath.Join(dirs.SnapBootAssetsDir), 0755), IsNil)
    53  }
    54  
    55  func (s *bootchainSuite) TestBootAssetsSort(c *C) {
    56  	// by role
    57  	d := []boot.BootAsset{
    58  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"b", "c"}},
    59  		{Role: bootloader.RoleRecovery, Name: "1ist", Hashes: []string{"b", "c"}},
    60  	}
    61  	sort.Sort(boot.ByBootAssetOrder(d))
    62  	c.Check(d, DeepEquals, []boot.BootAsset{
    63  		{Role: bootloader.RoleRecovery, Name: "1ist", Hashes: []string{"b", "c"}},
    64  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"b", "c"}},
    65  	})
    66  
    67  	// by name
    68  	d = []boot.BootAsset{
    69  		{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"d", "e"}},
    70  		{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d", "e"}},
    71  	}
    72  	sort.Sort(boot.ByBootAssetOrder(d))
    73  	c.Check(d, DeepEquals, []boot.BootAsset{
    74  		{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d", "e"}},
    75  		{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"d", "e"}},
    76  	})
    77  
    78  	// by hash list length
    79  	d = []boot.BootAsset{
    80  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"a", "f"}},
    81  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"d"}},
    82  	}
    83  	sort.Sort(boot.ByBootAssetOrder(d))
    84  	c.Check(d, DeepEquals, []boot.BootAsset{
    85  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"d"}},
    86  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"a", "f"}},
    87  	})
    88  
    89  	// hash list entries
    90  	d = []boot.BootAsset{
    91  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"b", "d"}},
    92  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"b", "c"}},
    93  	}
    94  	sort.Sort(boot.ByBootAssetOrder(d))
    95  	c.Check(d, DeepEquals, []boot.BootAsset{
    96  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"b", "c"}},
    97  		{Role: bootloader.RoleRunMode, Name: "1ist", Hashes: []string{"b", "d"}},
    98  	})
    99  
   100  	d = []boot.BootAsset{
   101  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   102  		{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b"}},
   103  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   104  		{Role: bootloader.RoleRunMode, Name: "1oader", Hashes: []string{"d", "e"}},
   105  		{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d", "e"}},
   106  		{Role: bootloader.RoleRunMode, Name: "0oader", Hashes: []string{"x", "z"}},
   107  	}
   108  	sort.Sort(boot.ByBootAssetOrder(d))
   109  	c.Check(d, DeepEquals, []boot.BootAsset{
   110  		{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d", "e"}},
   111  		{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b"}},
   112  		{Role: bootloader.RoleRunMode, Name: "0oader", Hashes: []string{"x", "z"}},
   113  		{Role: bootloader.RoleRunMode, Name: "1oader", Hashes: []string{"d", "e"}},
   114  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   115  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   116  	})
   117  
   118  	// d is already sorted, sort it again
   119  	sort.Sort(boot.ByBootAssetOrder(d))
   120  	// still the same
   121  	c.Check(d, DeepEquals, []boot.BootAsset{
   122  		{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d", "e"}},
   123  		{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b"}},
   124  		{Role: bootloader.RoleRunMode, Name: "0oader", Hashes: []string{"x", "z"}},
   125  		{Role: bootloader.RoleRunMode, Name: "1oader", Hashes: []string{"d", "e"}},
   126  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   127  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   128  	})
   129  
   130  	// 2 identical entries
   131  	d = []boot.BootAsset{
   132  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"x", "z"}},
   133  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"x", "z"}},
   134  	}
   135  	sort.Sort(boot.ByBootAssetOrder(d))
   136  	c.Check(d, DeepEquals, []boot.BootAsset{
   137  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"x", "z"}},
   138  		{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"x", "z"}},
   139  	})
   140  
   141  }
   142  
   143  func (s *bootchainSuite) TestBootAssetsPredictable(c *C) {
   144  	// by role
   145  	ba := boot.BootAsset{
   146  		Role: bootloader.RoleRunMode, Name: "list", Hashes: []string{"b", "a"},
   147  	}
   148  	pred := boot.ToPredictableBootAsset(&ba)
   149  	c.Check(pred, DeepEquals, &boot.BootAsset{
   150  		Role: bootloader.RoleRunMode, Name: "list", Hashes: []string{"a", "b"},
   151  	})
   152  	// original structure is not changed
   153  	c.Check(ba, DeepEquals, boot.BootAsset{
   154  		Role: bootloader.RoleRunMode, Name: "list", Hashes: []string{"b", "a"},
   155  	})
   156  
   157  	// try to make a predictable struct predictable once more
   158  	predAgain := boot.ToPredictableBootAsset(pred)
   159  	c.Check(predAgain, DeepEquals, pred)
   160  
   161  	baNil := boot.ToPredictableBootAsset(nil)
   162  	c.Check(baNil, IsNil)
   163  }
   164  
   165  func (s *bootchainSuite) TestBootChainMarshalOnlyAssets(c *C) {
   166  	pbNil := boot.ToPredictableBootChain(nil)
   167  	c.Check(pbNil, IsNil)
   168  
   169  	bc := &boot.BootChain{
   170  		AssetChain: []boot.BootAsset{
   171  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   172  			{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b"}},
   173  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"d", "c"}},
   174  			{Role: bootloader.RoleRunMode, Name: "1oader", Hashes: []string{"e", "d"}},
   175  			{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"e", "d"}},
   176  			{Role: bootloader.RoleRunMode, Name: "0oader", Hashes: []string{"z", "x"}},
   177  		},
   178  	}
   179  
   180  	predictableBc := boot.ToPredictableBootChain(bc)
   181  
   182  	c.Check(predictableBc, DeepEquals, &boot.BootChain{
   183  		// assets are sorted
   184  		AssetChain: []boot.BootAsset{
   185  			// hash lists are sorted
   186  			{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d", "e"}},
   187  			{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b"}},
   188  			{Role: bootloader.RoleRunMode, Name: "0oader", Hashes: []string{"x", "z"}},
   189  			{Role: bootloader.RoleRunMode, Name: "1oader", Hashes: []string{"d", "e"}},
   190  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   191  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   192  		},
   193  	})
   194  
   195  	d, err := json.Marshal(predictableBc)
   196  	c.Assert(err, IsNil)
   197  	c.Check(string(d), Equals, `{"brand-id":"","model":"","grade":"","model-sign-key-id":"","asset-chain":[{"role":"recovery","name":"loader","hashes":["d","e"]},{"role":"recovery","name":"shim","hashes":["b"]},{"role":"run-mode","name":"0oader","hashes":["x","z"]},{"role":"run-mode","name":"1oader","hashes":["d","e"]},{"role":"run-mode","name":"loader","hashes":["z"]},{"role":"run-mode","name":"loader","hashes":["c","d"]}],"kernel":"","kernel-revision":"","kernel-cmdlines":null}`)
   198  
   199  	// already predictable, but try again
   200  	alreadySortedBc := boot.ToPredictableBootChain(predictableBc)
   201  	c.Check(alreadySortedBc, DeepEquals, predictableBc)
   202  
   203  	// boot chain with 2 identical assets
   204  	bcIdenticalAssets := &boot.BootChain{
   205  		AssetChain: []boot.BootAsset{
   206  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   207  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z"}},
   208  		},
   209  	}
   210  	sortedBcIdentical := boot.ToPredictableBootChain(bcIdenticalAssets)
   211  	c.Check(sortedBcIdentical, DeepEquals, bcIdenticalAssets)
   212  }
   213  
   214  func (s *bootchainSuite) TestBootChainMarshalFull(c *C) {
   215  	bc := &boot.BootChain{
   216  		BrandID:        "mybrand",
   217  		Model:          "foo",
   218  		Grade:          "dangerous",
   219  		ModelSignKeyID: "my-key-id",
   220  		// asset chain will get sorted when marshaling
   221  		AssetChain: []boot.BootAsset{
   222  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   223  			// hash list will get sorted
   224  			{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b", "a"}},
   225  			{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d"}},
   226  		},
   227  		Kernel:         "pc-kernel",
   228  		KernelRevision: "1234",
   229  		KernelCmdlines: []string{`foo=bar baz=0x123`, `a=1`},
   230  	}
   231  
   232  	uc20model := makeMockUC20Model()
   233  	bc.SetModelAssertion(uc20model)
   234  	kernelBootFile := bootloader.NewBootFile("pc-kernel", "/foo", bootloader.RoleRecovery)
   235  	bc.SetKernelBootFile(kernelBootFile)
   236  
   237  	expectedPredictableBc := &boot.BootChain{
   238  		BrandID:        "mybrand",
   239  		Model:          "foo",
   240  		Grade:          "dangerous",
   241  		ModelSignKeyID: "my-key-id",
   242  		// assets are sorted
   243  		AssetChain: []boot.BootAsset{
   244  			{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d"}},
   245  			// hash lists are sorted
   246  			{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"a", "b"}},
   247  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   248  		},
   249  		Kernel:         "pc-kernel",
   250  		KernelRevision: "1234",
   251  		KernelCmdlines: []string{`a=1`, `foo=bar baz=0x123`},
   252  	}
   253  	// those can't be set directly, but are copied as well
   254  	expectedPredictableBc.SetModelAssertion(uc20model)
   255  	expectedPredictableBc.SetKernelBootFile(kernelBootFile)
   256  
   257  	predictableBc := boot.ToPredictableBootChain(bc)
   258  	c.Check(predictableBc, DeepEquals, expectedPredictableBc)
   259  
   260  	d, err := json.Marshal(predictableBc)
   261  	c.Assert(err, IsNil)
   262  	c.Check(string(d), Equals, `{"brand-id":"mybrand","model":"foo","grade":"dangerous","model-sign-key-id":"my-key-id","asset-chain":[{"role":"recovery","name":"loader","hashes":["d"]},{"role":"recovery","name":"shim","hashes":["a","b"]},{"role":"run-mode","name":"loader","hashes":["c","d"]}],"kernel":"pc-kernel","kernel-revision":"1234","kernel-cmdlines":["a=1","foo=bar baz=0x123"]}`)
   263  
   264  	expectedOriginal := &boot.BootChain{
   265  		BrandID:        "mybrand",
   266  		Model:          "foo",
   267  		Grade:          "dangerous",
   268  		ModelSignKeyID: "my-key-id",
   269  		// asset chain will get sorted when marshaling
   270  		AssetChain: []boot.BootAsset{
   271  			{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   272  			// hash list will get sorted
   273  			{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b", "a"}},
   274  			{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d"}},
   275  		},
   276  		Kernel:         "pc-kernel",
   277  		KernelRevision: "1234",
   278  		KernelCmdlines: []string{`foo=bar baz=0x123`, `a=1`},
   279  	}
   280  	expectedOriginal.SetModelAssertion(uc20model)
   281  	expectedOriginal.SetKernelBootFile(kernelBootFile)
   282  	// original structure has not been modified
   283  	c.Check(bc, DeepEquals, expectedOriginal)
   284  }
   285  
   286  func (s *bootchainSuite) TestBootChainEqualForResealComplex(c *C) {
   287  	bc := []boot.BootChain{
   288  		{
   289  			BrandID:        "mybrand",
   290  			Model:          "foo",
   291  			Grade:          "dangerous",
   292  			ModelSignKeyID: "my-key-id",
   293  			AssetChain: []boot.BootAsset{
   294  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   295  				// hash list will get sorted
   296  				{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"b", "a"}},
   297  				{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d"}},
   298  			},
   299  			Kernel:         "pc-kernel",
   300  			KernelRevision: "1234",
   301  			KernelCmdlines: []string{`foo=bar baz=0x123`},
   302  		},
   303  	}
   304  	pb := boot.ToPredictableBootChains(bc)
   305  	// sorted variant
   306  	pbOther := boot.PredictableBootChains{
   307  		{
   308  			BrandID:        "mybrand",
   309  			Model:          "foo",
   310  			Grade:          "dangerous",
   311  			ModelSignKeyID: "my-key-id",
   312  			AssetChain: []boot.BootAsset{
   313  				{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"d"}},
   314  				{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"a", "b"}},
   315  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   316  			},
   317  			Kernel:         "pc-kernel",
   318  			KernelRevision: "1234",
   319  			KernelCmdlines: []string{`foo=bar baz=0x123`},
   320  		},
   321  	}
   322  	eq := boot.PredictableBootChainsEqualForReseal(pb, pbOther)
   323  	c.Check(eq, Equals, true, Commentf("not equal\none: %v\nother: %v", pb, pbOther))
   324  }
   325  
   326  func (s *bootchainSuite) TestPredictableBootChainsEqualForResealSimple(c *C) {
   327  	var pbNil boot.PredictableBootChains
   328  
   329  	c.Check(boot.PredictableBootChainsEqualForReseal(pbNil, pbNil), Equals, true)
   330  
   331  	bcJustOne := []boot.BootChain{
   332  		{
   333  			BrandID:        "mybrand",
   334  			Model:          "foo",
   335  			Grade:          "dangerous",
   336  			ModelSignKeyID: "my-key-id",
   337  			AssetChain: []boot.BootAsset{
   338  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   339  			},
   340  			Kernel:         "pc-kernel-other",
   341  			KernelRevision: "1234",
   342  			KernelCmdlines: []string{`foo`},
   343  		},
   344  	}
   345  	pbJustOne := boot.ToPredictableBootChains(bcJustOne)
   346  	// equal with self
   347  	c.Check(boot.PredictableBootChainsEqualForReseal(pbJustOne, pbJustOne), Equals, true)
   348  
   349  	// equal with nil?
   350  	c.Check(boot.PredictableBootChainsEqualForReseal(pbJustOne, pbNil), Equals, false)
   351  
   352  	bcMoreAssets := []boot.BootChain{
   353  		{
   354  			BrandID:        "mybrand",
   355  			Model:          "foo",
   356  			Grade:          "dangerous",
   357  			ModelSignKeyID: "my-key-id",
   358  			AssetChain: []boot.BootAsset{
   359  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"c", "d"}},
   360  			},
   361  			Kernel:         "pc-kernel-other",
   362  			KernelRevision: "1234",
   363  			KernelCmdlines: []string{`foo`},
   364  		}, {
   365  			BrandID:        "mybrand",
   366  			Model:          "foo",
   367  			Grade:          "dangerous",
   368  			ModelSignKeyID: "my-key-id",
   369  			AssetChain: []boot.BootAsset{
   370  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"d", "e"}},
   371  			},
   372  			Kernel:         "pc-kernel-other",
   373  			KernelRevision: "1234",
   374  			KernelCmdlines: []string{`foo`},
   375  		},
   376  	}
   377  
   378  	pbMoreAssets := boot.ToPredictableBootChains(bcMoreAssets)
   379  
   380  	c.Check(boot.PredictableBootChainsEqualForReseal(pbMoreAssets, pbJustOne), Equals, false)
   381  	// with self
   382  	c.Check(boot.PredictableBootChainsEqualForReseal(pbMoreAssets, pbMoreAssets), Equals, true)
   383  }
   384  
   385  func (s *bootchainSuite) TestPredictableBootChainsFullMarshal(c *C) {
   386  	// chains will be sorted
   387  	chains := []boot.BootChain{
   388  		{
   389  			BrandID:        "mybrand",
   390  			Model:          "foo",
   391  			Grade:          "signed",
   392  			ModelSignKeyID: "my-key-id",
   393  			// assets will be sorted
   394  			AssetChain: []boot.BootAsset{
   395  				// hashes will be sorted
   396  				{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"x", "y"}},
   397  				{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"c", "d"}},
   398  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"z", "x"}},
   399  			},
   400  			Kernel:         "pc-kernel-other",
   401  			KernelRevision: "2345",
   402  			KernelCmdlines: []string{`snapd_recovery_mode=run foo`},
   403  		}, {
   404  			BrandID:        "mybrand",
   405  			Model:          "foo",
   406  			Grade:          "dangerous",
   407  			ModelSignKeyID: "my-key-id",
   408  			// assets will be sorted
   409  			AssetChain: []boot.BootAsset{
   410  				// hashes will be sorted
   411  				{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"y", "x"}},
   412  				{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"c", "d"}},
   413  				{Role: bootloader.RoleRunMode, Name: "loader", Hashes: []string{"b", "a"}},
   414  			},
   415  			Kernel:         "pc-kernel-other",
   416  			KernelRevision: "1234",
   417  			KernelCmdlines: []string{`snapd_recovery_mode=run foo`},
   418  		}, {
   419  			// recovery system
   420  			BrandID:        "mybrand",
   421  			Model:          "foo",
   422  			Grade:          "dangerous",
   423  			ModelSignKeyID: "my-key-id",
   424  			// will be sorted
   425  			AssetChain: []boot.BootAsset{
   426  				{Role: bootloader.RoleRecovery, Name: "shim", Hashes: []string{"y", "x"}},
   427  				{Role: bootloader.RoleRecovery, Name: "loader", Hashes: []string{"c", "d"}},
   428  			},
   429  			Kernel:         "pc-kernel-other",
   430  			KernelRevision: "12",
   431  			KernelCmdlines: []string{
   432  				// will be sorted
   433  				`snapd_recovery_mode=recover snapd_recovery_system=23 foo`,
   434  				`snapd_recovery_mode=recover snapd_recovery_system=12 foo`,
   435  			},
   436  		},
   437  	}
   438  
   439  	predictableChains := boot.ToPredictableBootChains(chains)
   440  	d, err := json.Marshal(predictableChains)
   441  	c.Assert(err, IsNil)
   442  
   443  	var data []map[string]interface{}
   444  	err = json.Unmarshal(d, &data)
   445  	c.Assert(err, IsNil)
   446  	c.Check(data, DeepEquals, []map[string]interface{}{
   447  		{
   448  			"model":             "foo",
   449  			"brand-id":          "mybrand",
   450  			"grade":             "dangerous",
   451  			"model-sign-key-id": "my-key-id",
   452  			"kernel":            "pc-kernel-other",
   453  			"kernel-revision":   "12",
   454  			"kernel-cmdlines": []interface{}{
   455  				`snapd_recovery_mode=recover snapd_recovery_system=12 foo`,
   456  				`snapd_recovery_mode=recover snapd_recovery_system=23 foo`,
   457  			},
   458  			"asset-chain": []interface{}{
   459  				map[string]interface{}{"role": "recovery", "name": "loader", "hashes": []interface{}{"c", "d"}},
   460  				map[string]interface{}{"role": "recovery", "name": "shim", "hashes": []interface{}{"x", "y"}},
   461  			},
   462  		}, {
   463  			"model":             "foo",
   464  			"brand-id":          "mybrand",
   465  			"grade":             "dangerous",
   466  			"model-sign-key-id": "my-key-id",
   467  			"kernel":            "pc-kernel-other",
   468  			"kernel-revision":   "1234",
   469  			"kernel-cmdlines":   []interface{}{"snapd_recovery_mode=run foo"},
   470  			"asset-chain": []interface{}{
   471  				map[string]interface{}{"role": "recovery", "name": "loader", "hashes": []interface{}{"c", "d"}},
   472  				map[string]interface{}{"role": "recovery", "name": "shim", "hashes": []interface{}{"x", "y"}},
   473  				map[string]interface{}{"role": "run-mode", "name": "loader", "hashes": []interface{}{"a", "b"}},
   474  			},
   475  		}, {
   476  			"model":             "foo",
   477  			"brand-id":          "mybrand",
   478  			"grade":             "signed",
   479  			"model-sign-key-id": "my-key-id",
   480  			"kernel":            "pc-kernel-other",
   481  			"kernel-revision":   "2345",
   482  			"kernel-cmdlines":   []interface{}{"snapd_recovery_mode=run foo"},
   483  			"asset-chain": []interface{}{
   484  				map[string]interface{}{"role": "recovery", "name": "loader", "hashes": []interface{}{"c", "d"}},
   485  				map[string]interface{}{"role": "recovery", "name": "shim", "hashes": []interface{}{"x", "y"}},
   486  				map[string]interface{}{"role": "run-mode", "name": "loader", "hashes": []interface{}{"x", "z"}},
   487  			},
   488  		},
   489  	})
   490  }
   491  
   492  func (s *bootchainSuite) TestPredictableBootChainsFields(c *C) {
   493  	chainsNil := boot.ToPredictableBootChains(nil)
   494  	c.Check(chainsNil, IsNil)
   495  
   496  	justOne := []boot.BootChain{
   497  		{
   498  			BrandID:        "mybrand",
   499  			Model:          "foo",
   500  			Grade:          "signed",
   501  			ModelSignKeyID: "my-key-id",
   502  			Kernel:         "pc-kernel-other",
   503  			KernelRevision: "2345",
   504  			KernelCmdlines: []string{`foo`},
   505  		},
   506  	}
   507  	predictableJustOne := boot.ToPredictableBootChains(justOne)
   508  	c.Check(predictableJustOne, DeepEquals, boot.PredictableBootChains(justOne))
   509  
   510  	chainsGrade := []boot.BootChain{
   511  		{
   512  			Grade: "signed",
   513  		}, {
   514  			Grade: "dangerous",
   515  		},
   516  	}
   517  	c.Check(boot.ToPredictableBootChains(chainsGrade), DeepEquals, boot.PredictableBootChains{
   518  		{
   519  			Grade: "dangerous",
   520  		}, {
   521  			Grade: "signed",
   522  		},
   523  	})
   524  
   525  	chainsKernel := []boot.BootChain{
   526  		{
   527  			Grade:  "dangerous",
   528  			Kernel: "foo",
   529  		}, {
   530  			Grade:  "dangerous",
   531  			Kernel: "bar",
   532  		},
   533  	}
   534  	c.Check(boot.ToPredictableBootChains(chainsKernel), DeepEquals, boot.PredictableBootChains{
   535  		{
   536  			Grade:  "dangerous",
   537  			Kernel: "bar",
   538  		}, {
   539  			Grade:  "dangerous",
   540  			Kernel: "foo",
   541  		},
   542  	})
   543  
   544  	chainsCmdline := []boot.BootChain{
   545  		{
   546  			Grade:          "dangerous",
   547  			Kernel:         "foo",
   548  			KernelCmdlines: []string{`panic=1`},
   549  		}, {
   550  			Grade:          "dangerous",
   551  			Kernel:         "foo",
   552  			KernelCmdlines: []string{`a`},
   553  		},
   554  	}
   555  	c.Check(boot.ToPredictableBootChains(chainsCmdline), DeepEquals, boot.PredictableBootChains{
   556  		{
   557  			Grade:          "dangerous",
   558  			Kernel:         "foo",
   559  			KernelCmdlines: []string{`a`},
   560  		}, {
   561  			Grade:          "dangerous",
   562  			Kernel:         "foo",
   563  			KernelCmdlines: []string{`panic=1`},
   564  		},
   565  	})
   566  
   567  	chainsModel := []boot.BootChain{
   568  		{
   569  			Model:          "fridge",
   570  			Grade:          "dangerous",
   571  			Kernel:         "foo",
   572  			KernelCmdlines: []string{`panic=1`},
   573  		}, {
   574  			Model:          "box",
   575  			Grade:          "dangerous",
   576  			Kernel:         "foo",
   577  			KernelCmdlines: []string{`panic=1`},
   578  		},
   579  	}
   580  	c.Check(boot.ToPredictableBootChains(chainsModel), DeepEquals, boot.PredictableBootChains{
   581  		{
   582  			Model:          "box",
   583  			Grade:          "dangerous",
   584  			Kernel:         "foo",
   585  			KernelCmdlines: []string{`panic=1`},
   586  		}, {
   587  			Model:          "fridge",
   588  			Grade:          "dangerous",
   589  			Kernel:         "foo",
   590  			KernelCmdlines: []string{`panic=1`},
   591  		},
   592  	})
   593  
   594  	chainsBrand := []boot.BootChain{
   595  		{
   596  			BrandID:        "foo",
   597  			Model:          "box",
   598  			Grade:          "dangerous",
   599  			Kernel:         "foo",
   600  			KernelCmdlines: []string{`panic=1`},
   601  		}, {
   602  			BrandID:        "acme",
   603  			Model:          "box",
   604  			Grade:          "dangerous",
   605  			Kernel:         "foo",
   606  			KernelCmdlines: []string{`panic=1`},
   607  		},
   608  	}
   609  	c.Check(boot.ToPredictableBootChains(chainsBrand), DeepEquals, boot.PredictableBootChains{
   610  		{
   611  			BrandID:        "acme",
   612  			Model:          "box",
   613  			Grade:          "dangerous",
   614  			Kernel:         "foo",
   615  			KernelCmdlines: []string{`panic=1`},
   616  		}, {
   617  			BrandID:        "foo",
   618  			Model:          "box",
   619  			Grade:          "dangerous",
   620  			Kernel:         "foo",
   621  			KernelCmdlines: []string{`panic=1`},
   622  		},
   623  	})
   624  
   625  	chainsKeyID := []boot.BootChain{
   626  		{
   627  			BrandID:        "foo",
   628  			Model:          "box",
   629  			Grade:          "dangerous",
   630  			Kernel:         "foo",
   631  			KernelCmdlines: []string{`panic=1`},
   632  			ModelSignKeyID: "key-2",
   633  		}, {
   634  			BrandID:        "foo",
   635  			Model:          "box",
   636  			Grade:          "dangerous",
   637  			Kernel:         "foo",
   638  			KernelCmdlines: []string{`panic=1`},
   639  			ModelSignKeyID: "key-1",
   640  		},
   641  	}
   642  	c.Check(boot.ToPredictableBootChains(chainsKeyID), DeepEquals, boot.PredictableBootChains{
   643  		{
   644  			BrandID:        "foo",
   645  			Model:          "box",
   646  			Grade:          "dangerous",
   647  			Kernel:         "foo",
   648  			KernelCmdlines: []string{`panic=1`},
   649  			ModelSignKeyID: "key-1",
   650  		}, {
   651  			BrandID:        "foo",
   652  			Model:          "box",
   653  			Grade:          "dangerous",
   654  			Kernel:         "foo",
   655  			KernelCmdlines: []string{`panic=1`},
   656  			ModelSignKeyID: "key-2",
   657  		},
   658  	})
   659  
   660  	chainsAssets := []boot.BootChain{
   661  		{
   662  			BrandID:        "foo",
   663  			Model:          "box",
   664  			Grade:          "dangerous",
   665  			ModelSignKeyID: "key-1",
   666  			AssetChain: []boot.BootAsset{
   667  				// will be sorted
   668  				{Hashes: []string{"b", "a"}},
   669  			},
   670  			Kernel:         "foo",
   671  			KernelCmdlines: []string{`panic=1`},
   672  		}, {
   673  			BrandID:        "foo",
   674  			Model:          "box",
   675  			Grade:          "dangerous",
   676  			ModelSignKeyID: "key-1",
   677  			AssetChain: []boot.BootAsset{
   678  				{Hashes: []string{"b"}},
   679  			},
   680  			Kernel:         "foo",
   681  			KernelCmdlines: []string{`panic=1`},
   682  		},
   683  	}
   684  	c.Check(boot.ToPredictableBootChains(chainsAssets), DeepEquals, boot.PredictableBootChains{
   685  		{
   686  			BrandID:        "foo",
   687  			Model:          "box",
   688  			Grade:          "dangerous",
   689  			ModelSignKeyID: "key-1",
   690  			AssetChain: []boot.BootAsset{
   691  				{Hashes: []string{"b"}},
   692  			},
   693  			Kernel:         "foo",
   694  			KernelCmdlines: []string{`panic=1`},
   695  		}, {
   696  			BrandID:        "foo",
   697  			Model:          "box",
   698  			Grade:          "dangerous",
   699  			ModelSignKeyID: "key-1",
   700  			AssetChain: []boot.BootAsset{
   701  				{Hashes: []string{"a", "b"}},
   702  			},
   703  			Kernel:         "foo",
   704  			KernelCmdlines: []string{`panic=1`},
   705  		},
   706  	})
   707  
   708  	chainsFewerAssets := []boot.BootChain{
   709  		{
   710  			AssetChain: []boot.BootAsset{
   711  				{Hashes: []string{"b", "a"}},
   712  				{Hashes: []string{"c", "d"}},
   713  			},
   714  		}, {
   715  			AssetChain: []boot.BootAsset{
   716  				{Hashes: []string{"b"}},
   717  			},
   718  		},
   719  	}
   720  	c.Check(boot.ToPredictableBootChains(chainsFewerAssets), DeepEquals, boot.PredictableBootChains{
   721  		{
   722  			AssetChain: []boot.BootAsset{
   723  				{Hashes: []string{"b"}},
   724  			},
   725  		}, {
   726  			AssetChain: []boot.BootAsset{
   727  				{Hashes: []string{"a", "b"}},
   728  				{Hashes: []string{"c", "d"}},
   729  			},
   730  		},
   731  	})
   732  
   733  	// not confused if 2 chains are identical
   734  	chainsIdenticalAssets := []boot.BootChain{
   735  		{
   736  			BrandID:        "foo",
   737  			Model:          "box",
   738  			ModelSignKeyID: "key-1",
   739  			AssetChain: []boot.BootAsset{
   740  				{Name: "asset", Hashes: []string{"a", "b"}},
   741  				{Name: "asset", Hashes: []string{"a", "b"}},
   742  			},
   743  			Grade:          "dangerous",
   744  			Kernel:         "foo",
   745  			KernelCmdlines: []string{`panic=1`},
   746  		}, {
   747  			BrandID:        "foo",
   748  			Model:          "box",
   749  			Grade:          "dangerous",
   750  			ModelSignKeyID: "key-1",
   751  			AssetChain: []boot.BootAsset{
   752  				{Name: "asset", Hashes: []string{"a", "b"}},
   753  				{Name: "asset", Hashes: []string{"a", "b"}},
   754  			},
   755  			Kernel:         "foo",
   756  			KernelCmdlines: []string{`panic=1`},
   757  		},
   758  	}
   759  	c.Check(boot.ToPredictableBootChains(chainsIdenticalAssets), DeepEquals, boot.PredictableBootChains(chainsIdenticalAssets))
   760  }
   761  
   762  func (s *bootchainSuite) TestPredictableBootChainsSortOrder(c *C) {
   763  	// check that sort order is model info, assets, kernel, kernel cmdline
   764  
   765  	chains := []boot.BootChain{
   766  		{
   767  			Model: "b",
   768  			AssetChain: []boot.BootAsset{
   769  				{Name: "asset", Hashes: []string{"y"}},
   770  			},
   771  			Kernel:         "k1",
   772  			KernelCmdlines: []string{"cm=1"},
   773  		},
   774  		{
   775  			Model: "b",
   776  			AssetChain: []boot.BootAsset{
   777  				{Name: "asset", Hashes: []string{"y"}},
   778  			},
   779  			Kernel:         "k2",
   780  			KernelCmdlines: []string{"cm=1"},
   781  		},
   782  		{
   783  			Model: "a",
   784  			AssetChain: []boot.BootAsset{
   785  				{Name: "asset", Hashes: []string{"y"}},
   786  			},
   787  			Kernel:         "k1",
   788  			KernelCmdlines: []string{"cm=1"},
   789  		},
   790  		{
   791  			Model: "a",
   792  			AssetChain: []boot.BootAsset{
   793  				{Name: "asset", Hashes: []string{"y"}},
   794  			},
   795  			Kernel:         "k2",
   796  			KernelCmdlines: []string{"cm=1"},
   797  		},
   798  		{
   799  			Model: "b",
   800  			AssetChain: []boot.BootAsset{
   801  				{Name: "asset", Hashes: []string{"y"}},
   802  			},
   803  			Kernel:         "k1",
   804  			KernelCmdlines: []string{"cm=2"},
   805  		},
   806  		{
   807  			Model: "b",
   808  			AssetChain: []boot.BootAsset{
   809  				{Name: "asset", Hashes: []string{"y"}},
   810  			},
   811  			Kernel:         "k2",
   812  			KernelCmdlines: []string{"cm=2"},
   813  		},
   814  		{
   815  			Model: "a",
   816  			AssetChain: []boot.BootAsset{
   817  				{Name: "asset", Hashes: []string{"y"}},
   818  			},
   819  			Kernel:         "k1",
   820  			KernelCmdlines: []string{"cm=2"},
   821  		},
   822  		{
   823  			Model: "a",
   824  			AssetChain: []boot.BootAsset{
   825  				{Name: "asset", Hashes: []string{"y"}},
   826  			},
   827  			Kernel:         "k2",
   828  			KernelCmdlines: []string{"cm=2"},
   829  		},
   830  		{
   831  			Model: "b",
   832  			AssetChain: []boot.BootAsset{
   833  				{Name: "asset", Hashes: []string{"x"}},
   834  			},
   835  			Kernel:         "k1",
   836  			KernelCmdlines: []string{"cm=1"},
   837  		},
   838  		{
   839  			Model: "b",
   840  			AssetChain: []boot.BootAsset{
   841  				{Name: "asset", Hashes: []string{"x"}},
   842  			},
   843  			Kernel:         "k2",
   844  			KernelCmdlines: []string{"cm=1"},
   845  		},
   846  		{
   847  			Model: "a",
   848  			AssetChain: []boot.BootAsset{
   849  				{Name: "asset", Hashes: []string{"x"}},
   850  			},
   851  			Kernel:         "k1",
   852  			KernelCmdlines: []string{"cm=1"},
   853  		},
   854  		{
   855  			Model: "a",
   856  			AssetChain: []boot.BootAsset{
   857  				{Name: "asset", Hashes: []string{"x"}},
   858  			},
   859  			Kernel:         "k2",
   860  			KernelCmdlines: []string{"cm=1"},
   861  		},
   862  		{
   863  			Model: "b",
   864  			AssetChain: []boot.BootAsset{
   865  				{Name: "asset", Hashes: []string{"x"}},
   866  			},
   867  			Kernel:         "k1",
   868  			KernelCmdlines: []string{"cm=2"},
   869  		},
   870  		{
   871  			Model: "b",
   872  			AssetChain: []boot.BootAsset{
   873  				{Name: "asset", Hashes: []string{"x"}},
   874  			},
   875  			Kernel:         "k2",
   876  			KernelCmdlines: []string{"cm=2"},
   877  		},
   878  		{
   879  			Model: "a",
   880  			AssetChain: []boot.BootAsset{
   881  				{Name: "asset", Hashes: []string{"x"}},
   882  			},
   883  			Kernel:         "k1",
   884  			KernelCmdlines: []string{"cm=2"},
   885  		},
   886  		{
   887  			Model: "a",
   888  			AssetChain: []boot.BootAsset{
   889  				{Name: "asset", Hashes: []string{"x"}},
   890  			},
   891  			Kernel:         "k2",
   892  			KernelCmdlines: []string{"cm=2"},
   893  		},
   894  		{
   895  			Model: "a",
   896  			AssetChain: []boot.BootAsset{
   897  				{Name: "asset", Hashes: []string{"y"}},
   898  			},
   899  			Kernel:         "k2",
   900  			KernelCmdlines: []string{"cm=1", "cm=2"},
   901  		},
   902  		{
   903  			Model: "a",
   904  			AssetChain: []boot.BootAsset{
   905  				{Name: "asset", Hashes: []string{"y"}},
   906  			},
   907  			Kernel:         "k1",
   908  			KernelCmdlines: []string{"cm=1", "cm=2"},
   909  		},
   910  	}
   911  	predictable := boot.ToPredictableBootChains(chains)
   912  	c.Check(predictable, DeepEquals, boot.PredictableBootChains{
   913  		{
   914  			Model: "a",
   915  			AssetChain: []boot.BootAsset{
   916  				{Name: "asset", Hashes: []string{"x"}},
   917  			},
   918  			Kernel:         "k1",
   919  			KernelCmdlines: []string{"cm=1"},
   920  		},
   921  		{
   922  			Model: "a",
   923  			AssetChain: []boot.BootAsset{
   924  				{Name: "asset", Hashes: []string{"x"}},
   925  			},
   926  			Kernel:         "k1",
   927  			KernelCmdlines: []string{"cm=2"},
   928  		},
   929  		{
   930  			Model: "a",
   931  			AssetChain: []boot.BootAsset{
   932  				{Name: "asset", Hashes: []string{"x"}},
   933  			},
   934  			Kernel:         "k2",
   935  			KernelCmdlines: []string{"cm=1"},
   936  		},
   937  		{
   938  			Model: "a",
   939  			AssetChain: []boot.BootAsset{
   940  				{Name: "asset", Hashes: []string{"x"}},
   941  			},
   942  			Kernel:         "k2",
   943  			KernelCmdlines: []string{"cm=2"},
   944  		},
   945  		{
   946  			Model: "a",
   947  			AssetChain: []boot.BootAsset{
   948  				{Name: "asset", Hashes: []string{"y"}},
   949  			},
   950  			Kernel:         "k1",
   951  			KernelCmdlines: []string{"cm=1"},
   952  		},
   953  		{
   954  			Model: "a",
   955  			AssetChain: []boot.BootAsset{
   956  				{Name: "asset", Hashes: []string{"y"}},
   957  			},
   958  			Kernel:         "k1",
   959  			KernelCmdlines: []string{"cm=2"},
   960  		},
   961  		{
   962  			Model: "a",
   963  			AssetChain: []boot.BootAsset{
   964  				{Name: "asset", Hashes: []string{"y"}},
   965  			},
   966  			Kernel:         "k1",
   967  			KernelCmdlines: []string{"cm=1", "cm=2"},
   968  		},
   969  		{
   970  			Model: "a",
   971  			AssetChain: []boot.BootAsset{
   972  				{Name: "asset", Hashes: []string{"y"}},
   973  			},
   974  			Kernel:         "k2",
   975  			KernelCmdlines: []string{"cm=1"},
   976  		},
   977  		{
   978  			Model: "a",
   979  			AssetChain: []boot.BootAsset{
   980  				{Name: "asset", Hashes: []string{"y"}},
   981  			},
   982  			Kernel:         "k2",
   983  			KernelCmdlines: []string{"cm=2"},
   984  		},
   985  		{
   986  			Model: "a",
   987  			AssetChain: []boot.BootAsset{
   988  				{Name: "asset", Hashes: []string{"y"}},
   989  			},
   990  			Kernel:         "k2",
   991  			KernelCmdlines: []string{"cm=1", "cm=2"},
   992  		},
   993  		{
   994  			Model: "b",
   995  			AssetChain: []boot.BootAsset{
   996  				{Name: "asset", Hashes: []string{"x"}},
   997  			},
   998  			Kernel:         "k1",
   999  			KernelCmdlines: []string{"cm=1"},
  1000  		},
  1001  		{
  1002  			Model: "b",
  1003  			AssetChain: []boot.BootAsset{
  1004  				{Name: "asset", Hashes: []string{"x"}},
  1005  			},
  1006  			Kernel:         "k1",
  1007  			KernelCmdlines: []string{"cm=2"},
  1008  		},
  1009  		{
  1010  			Model: "b",
  1011  			AssetChain: []boot.BootAsset{
  1012  				{Name: "asset", Hashes: []string{"x"}},
  1013  			},
  1014  			Kernel:         "k2",
  1015  			KernelCmdlines: []string{"cm=1"},
  1016  		},
  1017  		{
  1018  			Model: "b",
  1019  			AssetChain: []boot.BootAsset{
  1020  				{Name: "asset", Hashes: []string{"x"}},
  1021  			},
  1022  			Kernel:         "k2",
  1023  			KernelCmdlines: []string{"cm=2"},
  1024  		},
  1025  		{
  1026  			Model: "b",
  1027  			AssetChain: []boot.BootAsset{
  1028  				{Name: "asset", Hashes: []string{"y"}},
  1029  			},
  1030  			Kernel:         "k1",
  1031  			KernelCmdlines: []string{"cm=1"},
  1032  		},
  1033  		{
  1034  			Model: "b",
  1035  			AssetChain: []boot.BootAsset{
  1036  				{Name: "asset", Hashes: []string{"y"}},
  1037  			},
  1038  			Kernel:         "k1",
  1039  			KernelCmdlines: []string{"cm=2"},
  1040  		},
  1041  		{
  1042  			Model: "b",
  1043  			AssetChain: []boot.BootAsset{
  1044  				{Name: "asset", Hashes: []string{"y"}},
  1045  			},
  1046  			Kernel:         "k2",
  1047  			KernelCmdlines: []string{"cm=1"},
  1048  		},
  1049  		{
  1050  			Model: "b",
  1051  			AssetChain: []boot.BootAsset{
  1052  				{Name: "asset", Hashes: []string{"y"}},
  1053  			},
  1054  			Kernel:         "k2",
  1055  			KernelCmdlines: []string{"cm=2"},
  1056  		},
  1057  	})
  1058  }
  1059  
  1060  func printChain(c *C, chain *secboot.LoadChain, prefix string) {
  1061  	c.Logf("%v %v", prefix, chain.BootFile)
  1062  	for _, n := range chain.Next {
  1063  		printChain(c, n, prefix+"-")
  1064  	}
  1065  }
  1066  
  1067  // cPath returns a path under boot assets cache directory
  1068  func cPath(p string) string {
  1069  	return filepath.Join(dirs.SnapBootAssetsDir, p)
  1070  }
  1071  
  1072  // nbf is bootloader.NewBootFile but shorter
  1073  var nbf = bootloader.NewBootFile
  1074  
  1075  func (s *bootchainSuite) TestBootAssetsToLoadChainTrivialKernel(c *C) {
  1076  	kbl := bootloader.NewBootFile("pc-kernel", "kernel.efi", bootloader.RoleRunMode)
  1077  
  1078  	chains, err := boot.BootAssetsToLoadChains(nil, kbl, nil)
  1079  	c.Assert(err, IsNil)
  1080  
  1081  	c.Check(chains, DeepEquals, []*secboot.LoadChain{
  1082  		secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode)),
  1083  	})
  1084  }
  1085  
  1086  func (s *bootchainSuite) TestBootAssetsToLoadChainErr(c *C) {
  1087  	kbl := bootloader.NewBootFile("pc-kernel", "kernel.efi", bootloader.RoleRunMode)
  1088  
  1089  	assets := []boot.BootAsset{
  1090  		{Name: "shim", Hashes: []string{"hash0"}, Role: bootloader.RoleRecovery},
  1091  		{Name: "loader-recovery", Hashes: []string{"hash0"}, Role: bootloader.RoleRecovery},
  1092  		{Name: "loader-run", Hashes: []string{"hash0"}, Role: bootloader.RoleRunMode},
  1093  	}
  1094  
  1095  	blNames := map[bootloader.Role]string{
  1096  		bootloader.RoleRecovery: "recovery-bl",
  1097  		// missing bootloader name for role "run-mode"
  1098  	}
  1099  	// fails when probing the shim asset in the cache
  1100  	chains, err := boot.BootAssetsToLoadChains(assets, kbl, blNames)
  1101  	c.Assert(err, ErrorMatches, "file .*/recovery-bl/shim-hash0 not found in boot assets cache")
  1102  	c.Check(chains, IsNil)
  1103  	// make it work now
  1104  	c.Assert(os.MkdirAll(filepath.Dir(cPath("recovery-bl/shim-hash0")), 0755), IsNil)
  1105  	c.Assert(ioutil.WriteFile(cPath("recovery-bl/shim-hash0"), nil, 0644), IsNil)
  1106  
  1107  	// nested error bubbled up
  1108  	chains, err = boot.BootAssetsToLoadChains(assets, kbl, blNames)
  1109  	c.Assert(err, ErrorMatches, "file .*/recovery-bl/loader-recovery-hash0 not found in boot assets cache")
  1110  	c.Check(chains, IsNil)
  1111  	// again, make it work
  1112  	c.Assert(os.MkdirAll(filepath.Dir(cPath("recovery-bl/loader-recovery-hash0")), 0755), IsNil)
  1113  	c.Assert(ioutil.WriteFile(cPath("recovery-bl/loader-recovery-hash0"), nil, 0644), IsNil)
  1114  
  1115  	// fails on missing bootloader name for role "run-mode"
  1116  	chains, err = boot.BootAssetsToLoadChains(assets, kbl, blNames)
  1117  	c.Assert(err, ErrorMatches, `internal error: no bootloader name for boot asset role "run-mode"`)
  1118  	c.Check(chains, IsNil)
  1119  }
  1120  
  1121  func (s *bootchainSuite) TestBootAssetsToLoadChainSimpleChain(c *C) {
  1122  	kbl := bootloader.NewBootFile("pc-kernel", "kernel.efi", bootloader.RoleRunMode)
  1123  
  1124  	assets := []boot.BootAsset{
  1125  		{Name: "shim", Hashes: []string{"hash0"}, Role: bootloader.RoleRecovery},
  1126  		{Name: "loader-recovery", Hashes: []string{"hash0"}, Role: bootloader.RoleRecovery},
  1127  		{Name: "loader-run", Hashes: []string{"hash0"}, Role: bootloader.RoleRunMode},
  1128  	}
  1129  
  1130  	// mock relevant files in cache
  1131  	for _, name := range []string{
  1132  		"recovery-bl/shim-hash0",
  1133  		"recovery-bl/loader-recovery-hash0",
  1134  		"run-bl/loader-run-hash0",
  1135  	} {
  1136  		p := filepath.Join(dirs.SnapBootAssetsDir, name)
  1137  		c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
  1138  		c.Assert(ioutil.WriteFile(p, nil, 0644), IsNil)
  1139  	}
  1140  
  1141  	blNames := map[bootloader.Role]string{
  1142  		bootloader.RoleRecovery: "recovery-bl",
  1143  		bootloader.RoleRunMode:  "run-bl",
  1144  	}
  1145  
  1146  	chains, err := boot.BootAssetsToLoadChains(assets, kbl, blNames)
  1147  	c.Assert(err, IsNil)
  1148  
  1149  	c.Logf("got:")
  1150  	for _, ch := range chains {
  1151  		printChain(c, ch, "-")
  1152  	}
  1153  
  1154  	expected := []*secboot.LoadChain{
  1155  		secboot.NewLoadChain(nbf("", cPath("recovery-bl/shim-hash0"), bootloader.RoleRecovery),
  1156  			secboot.NewLoadChain(nbf("", cPath("recovery-bl/loader-recovery-hash0"), bootloader.RoleRecovery),
  1157  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash0"), bootloader.RoleRunMode),
  1158  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))))),
  1159  	}
  1160  	c.Check(chains, DeepEquals, expected)
  1161  }
  1162  
  1163  func (s *bootchainSuite) TestBootAssetsToLoadChainWithAlternativeChains(c *C) {
  1164  	kbl := bootloader.NewBootFile("pc-kernel", "kernel.efi", bootloader.RoleRunMode)
  1165  
  1166  	assets := []boot.BootAsset{
  1167  		{Name: "shim", Hashes: []string{"hash0", "hash1"}, Role: bootloader.RoleRecovery},
  1168  		{Name: "loader-recovery", Hashes: []string{"hash0", "hash1"}, Role: bootloader.RoleRecovery},
  1169  		{Name: "loader-run", Hashes: []string{"hash0", "hash1"}, Role: bootloader.RoleRunMode},
  1170  	}
  1171  
  1172  	// mock relevant files in cache
  1173  	for _, name := range []string{
  1174  		"recovery-bl/shim-hash0", "recovery-bl/shim-hash1",
  1175  		"recovery-bl/loader-recovery-hash0",
  1176  		"recovery-bl/loader-recovery-hash1",
  1177  		"run-bl/loader-run-hash0",
  1178  		"run-bl/loader-run-hash1",
  1179  	} {
  1180  		p := filepath.Join(dirs.SnapBootAssetsDir, name)
  1181  		c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
  1182  		c.Assert(ioutil.WriteFile(p, nil, 0644), IsNil)
  1183  	}
  1184  
  1185  	blNames := map[bootloader.Role]string{
  1186  		bootloader.RoleRecovery: "recovery-bl",
  1187  		bootloader.RoleRunMode:  "run-bl",
  1188  	}
  1189  	chains, err := boot.BootAssetsToLoadChains(assets, kbl, blNames)
  1190  	c.Assert(err, IsNil)
  1191  
  1192  	c.Logf("got:")
  1193  	for _, ch := range chains {
  1194  		printChain(c, ch, "-")
  1195  	}
  1196  
  1197  	expected := []*secboot.LoadChain{
  1198  		secboot.NewLoadChain(nbf("", cPath("recovery-bl/shim-hash0"), bootloader.RoleRecovery),
  1199  			secboot.NewLoadChain(nbf("", cPath("recovery-bl/loader-recovery-hash0"), bootloader.RoleRecovery),
  1200  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash0"), bootloader.RoleRunMode),
  1201  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))),
  1202  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash1"), bootloader.RoleRunMode),
  1203  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode)))),
  1204  			secboot.NewLoadChain(nbf("", cPath("recovery-bl/loader-recovery-hash1"), bootloader.RoleRecovery),
  1205  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash0"), bootloader.RoleRunMode),
  1206  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))),
  1207  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash1"), bootloader.RoleRunMode),
  1208  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))))),
  1209  		secboot.NewLoadChain(nbf("", cPath("recovery-bl/shim-hash1"), bootloader.RoleRecovery),
  1210  			secboot.NewLoadChain(nbf("", cPath("recovery-bl/loader-recovery-hash0"), bootloader.RoleRecovery),
  1211  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash0"), bootloader.RoleRunMode),
  1212  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))),
  1213  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash1"), bootloader.RoleRunMode),
  1214  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode)))),
  1215  			secboot.NewLoadChain(nbf("", cPath("recovery-bl/loader-recovery-hash1"), bootloader.RoleRecovery),
  1216  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash0"), bootloader.RoleRunMode),
  1217  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))),
  1218  				secboot.NewLoadChain(nbf("", cPath("run-bl/loader-run-hash1"), bootloader.RoleRunMode),
  1219  					secboot.NewLoadChain(nbf("pc-kernel", "kernel.efi", bootloader.RoleRunMode))))),
  1220  	}
  1221  	c.Check(chains, DeepEquals, expected)
  1222  }