github.com/in-toto/in-toto-golang@v0.9.1-0.20240517212500-990269f763cf/in_toto/model_test.go (about)

     1  package in_toto
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/x509"
     6  	"crypto/x509/pkix"
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func init() {
    20  	// Make sure all strings formatted are in tz Zulu
    21  	os.Setenv("TZ", "UTC")
    22  }
    23  
    24  func TestMatchEcdsaScheme(t *testing.T) {
    25  	curveSize := 224
    26  	scheme := "ecdsa-sha2-nistp512"
    27  	if err := matchEcdsaScheme(curveSize, scheme); err == nil {
    28  		t.Errorf("matchEcdsaScheme should have failed with curveSize: %d and scheme: %s", curveSize, scheme)
    29  	}
    30  }
    31  
    32  func TestMetablockLoad(t *testing.T) {
    33  	// Create a bunch of tmp json files with invalid format and test load errors:
    34  	// - invalid json
    35  	// - missing signatures and signed field
    36  	// - invalid signatures field
    37  	// - invalid signed field
    38  	// - invalid signed type
    39  	// - invalid signed field for type link
    40  	// - invalid signed field for type layout
    41  	invalidJSONBytes := [][]byte{
    42  		[]byte("{"),
    43  		[]byte("{}"),
    44  		[]byte(`{"signatures": null, "signed": {}}`),
    45  		[]byte(`{"signatures": "string", "signed": {}}`),
    46  		[]byte(`{"signatures": [], "signed": []}`),
    47  		[]byte(`{"signatures": [], "signed": {"_type": "something else"}}`),
    48  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    49  			"materials": "invalid", "name": "some name", "products": "invalid",
    50  			"byproducts": "invalid", "command": "some command",
    51  			"environment": "some list"}}`),
    52  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
    53  			"steps": "invalid", "inspect": "invalid", "readme": "some readme",
    54  			"keys": "some keys", "expires": "some date", "rootcas": [], "intermediatecas": []}}`),
    55  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
    56  			"inspect": "invalid", "readme": "some readme", "keys": "some keys",
    57  			"expires": "some date", "rootcas": [], "intermediatecas": []}}`),
    58  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
    59  			"steps": "invalid", "readme": "some readme", "keys": "some keys",
    60  			"expires": "some date", "rootcas": [], "intermediatecas": []}}`),
    61  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
    62  			"steps": "invalid", "inspect": "invalid", "readme": "some readme",
    63  			"expires": "some date", "rootcas": [], "intermediatecas": []}}`),
    64  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
    65  			"steps": "invalid", "inspect": "invalid", "readme": "some readme",
    66  			"keys": "some keys", "rootcas": [], "intermediatecas": []}}`),
    67  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
    68  			"steps": "invalid", "inspect": "invalid",
    69  			"keys": "some keys", "expires": "some date", "rootcas": [], "intermediatecas": []}}`),
    70  		[]byte(`{"signatures": [], "signed": {"_type": "layout", "steps": [],
    71  			"inspect": [], "readme": "some readme", "keys": {},
    72  			"expires": "some date", "foo": "bar", "rootcas": [], "intermediatecas": []}}`),
    73  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    74  			"materials": "invalid", "products": "invalid",
    75  			"byproducts": "invalid", "command": "some command",
    76  			"environment": "some list"}}`),
    77  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    78  			"name": "some name", "products": "invalid",
    79  			"byproducts": "invalid", "command": "some command",
    80  			"environment": "some list"}}`),
    81  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    82  			"materials": "invalid", "name": "some name",
    83  			"byproducts": "invalid", "command": "some command",
    84  			"environment": "some list"}}`),
    85  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    86  			"materials": "invalid", "name": "some name", "products": "invalid",
    87  			"command": "some command",
    88  			"environment": "some list"}}`),
    89  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    90  			"materials": "invalid", "name": "some name", "products": "invalid",
    91  			"byproducts": "invalid", "environment": "some list"}}`),
    92  		[]byte(`{"signatures": [], "signed": {"_type": "link",
    93  			"materials": "invalid", "name": "some name", "products": "invalid",
    94  			"byproducts": "invalid", "command": "some command"}}`),
    95  		[]byte(`{"signatures": [], "signed": {"_type": "link", "materials": {},
    96  			"name": "some name", "products": {}, "byproducts": {},
    97  			"command": [], "environment": {}, "foo": "bar"}}`),
    98  	}
    99  
   100  	expectedErrors := []string{
   101  		"unexpected end",
   102  		"requires 'signed' and 'signatures' parts",
   103  		"requires 'signed' and 'signatures' parts",
   104  		"cannot unmarshal string into Go value of type []in_toto.Signature",
   105  		"cannot unmarshal array into Go value of type map[string]interface {}",
   106  		ErrUnknownMetadataType.Error(),
   107  		"cannot unmarshal string into Go struct field Link.materials",
   108  		"cannot unmarshal string into Go struct field Layout.steps",
   109  		"required field steps missing",
   110  		"required field inspect missing",
   111  		"required field keys missing",
   112  		"required field expires missing",
   113  		"required field readme missing",
   114  		"json: unknown field \"foo\"",
   115  		"required field name missing",
   116  		"required field materials missing",
   117  		"required field products missing",
   118  		"required field byproducts missing",
   119  		"required field command missing",
   120  		"required field environment missing",
   121  		"json: unknown field \"foo\"",
   122  	}
   123  
   124  	for i := 0; i < len(invalidJSONBytes); i++ {
   125  		fn := fmt.Sprintf("invalid-metadata-%v.tmp", i)
   126  		if err := os.WriteFile(fn, invalidJSONBytes[i], 0644); err != nil {
   127  			fmt.Printf("Could not write file: %s", err)
   128  		}
   129  		var mb Metablock
   130  		err := mb.Load(fn)
   131  		if err == nil || !strings.Contains(err.Error(), expectedErrors[i]) {
   132  			t.Errorf("Metablock.Load returned '%s', expected '%s' error", err,
   133  				expectedErrors[i])
   134  		}
   135  		if err := os.Remove(fn); err != nil {
   136  			t.Errorf("unable to remove directory %s: %s", fn, err)
   137  		}
   138  	}
   139  }
   140  
   141  func TestMetablockDump(t *testing.T) {
   142  	// Test dump metablock errors:
   143  	// - invalid content
   144  	// - invalid path
   145  	mbs := []Metablock{
   146  		{Signed: TestMetablockDump},
   147  		{},
   148  	}
   149  	paths := []string{
   150  		"bad-metadata",
   151  		"bad/path",
   152  	}
   153  	expectedErrors := []string{
   154  		"json: unsupported type",
   155  		"open bad/path",
   156  	}
   157  
   158  	for i := 0; i < len(mbs); i++ {
   159  		err := mbs[i].Dump(paths[i])
   160  		fmt.Println(err)
   161  		if err == nil || !strings.Contains(err.Error(), expectedErrors[i]) {
   162  			t.Errorf("Metablock.Dump returned '%s', expected '%s'",
   163  				err, expectedErrors[i])
   164  		}
   165  	}
   166  }
   167  
   168  func TestMetablockLoadDumpLoad(t *testing.T) {
   169  	// Dump, load and compare metablock, also compare with metablock loaded
   170  	// from existing equivalent JSON file, assert that they are equal.
   171  	mbMemory := Metablock{
   172  		Signed: Link{
   173  			Type: "link",
   174  			Name: "package",
   175  			Command: []string{
   176  				"tar",
   177  				"zcvf",
   178  				"foo.tar.gz",
   179  				"foo.py",
   180  			},
   181  			Materials: map[string]HashObj{
   182  				"foo.py": {
   183  					"sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a27c013544a60502549",
   184  				},
   185  			},
   186  			Products: map[string]HashObj{
   187  				"foo.tar.gz": {
   188  					"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   189  				},
   190  			},
   191  			ByProducts: map[string]interface{}{
   192  				"return-value": float64(0),
   193  				"stderr":       "a foo.py\n",
   194  				"stdout":       "",
   195  			},
   196  			Environment: map[string]interface{}{},
   197  		},
   198  		Signatures: []Signature{
   199  			{
   200  				KeyID: "d3ffd1086938b3698618adf088bf14b13db4c8ae19e4e78d73da49ee88492710",
   201  				Sig:   "7d42ca77f6bbbb65b015ec9e31abdfa05c0daecc34b016dd7997b26c3a347cb9a3d9045c8ac7e375f017076bc04687eb870e09f76031a014d60421fa288a11a0022ab225bcfde7b22d78891eeab06b0701b5a6d00368534bf7a3f6b16dc7aaed233a3fb5ab7e98e0ed0ffca5d128dd2549f2d2fe296038cd2111e282de31a44c428498e9788f8226d454331af6f582a1e61e88846265d0cd4722a431253f40bb52c9e56feffd90aca8ec0c6970576538eef5824c91159bce7583a10ae1a38c081e3991c7a20f280430cb1eb4e828c8a0f9c8c8ca41c27b2837a88ff7aa5052b4ac45d8fd5897a71f2f488ca3f52c7a770a01f2d8ab775a328cd1d4c45bb2e92c",
   202  			},
   203  		},
   204  	}
   205  
   206  	fnExisting := "package.d3ffd108.link"
   207  	fnTmp := fnExisting + ".tmp"
   208  	if err := mbMemory.Dump(fnTmp); err != nil {
   209  		t.Errorf("JSON serialization failed: %s", err)
   210  	}
   211  	for _, fn := range []string{fnExisting, fnTmp} {
   212  		var mbFile Metablock
   213  		if err := mbFile.Load(fn); err != nil {
   214  			t.Errorf("could not parse Metablock: %s", err)
   215  		}
   216  		if !reflect.DeepEqual(mbMemory, mbFile) {
   217  			t.Errorf("dumped and Loaded Metablocks are not equal: \n%s\n\n\n%s\n",
   218  				mbMemory, mbFile)
   219  		}
   220  	}
   221  	// Remove temporary metablock file (keep other for remaining tests)
   222  	if err := os.Remove(fnTmp); err != nil {
   223  		t.Errorf("unable to remove directory %s: %s", fnTmp, err)
   224  	}
   225  }
   226  
   227  func TestMetablockGetSignableRepresentation(t *testing.T) {
   228  	// Test successful metadata canonicalization with encoding corner cases
   229  	// (unicode, escapes, non-string types, ...) and compare with reference
   230  	var mb Metablock
   231  	if err := mb.Load("canonical-test.link"); err != nil {
   232  		t.Errorf("cannot parse link file: %s", err)
   233  	}
   234  	// Use hex representation for unambiguous assignment
   235  	referenceHex := "7b225f74797065223a226c696e6b222" +
   236  		"c22627970726f6475637473223a7b7d2c22636f6d6d616e64223a5b5d2" +
   237  		"c22656e7669726f6e6d656e74223a7b2261223a22575446222c2262223" +
   238  		"a747275652c2263223a66616c73652c2264223a6e756c6c2c2265223a3" +
   239  		"12c2266223a221befbfbf465c5c6e5c22227d2c226d6174657269616c7" +
   240  		"3223a7b7d2c226e616d65223a2274657374222c2270726f64756374732" +
   241  		"23a7b7d7d"
   242  
   243  	canonical, _ := mb.GetSignableRepresentation()
   244  	if fmt.Sprintf("%x", canonical) != referenceHex {
   245  		// Convert hex representation back to string for better error message
   246  		src := []byte(referenceHex)
   247  		reference := make([]byte, hex.DecodedLen(len(src)))
   248  		n, _ := hex.Decode(reference, src)
   249  		t.Errorf("Metablock.GetSignableRepresentation returned '%s', expected '%s'",
   250  			canonical, reference[:n])
   251  	}
   252  }
   253  
   254  func TestMetablockVerifySignature(t *testing.T) {
   255  	// Test metablock signature verification errors:
   256  	// - no signature found
   257  	// - wrong signature for key
   258  	// - invalid metadata (can't canonicalize)
   259  	var key Key
   260  	if err := key.LoadKey("alice.pub", "rsassa-pss-sha256", []string{"sha256", "sha512"}); err != nil {
   261  		t.Errorf("cannot load public key file: %s", err)
   262  	}
   263  	// Test missing key, bad signature and bad metadata
   264  	mbs := []Metablock{
   265  		{},
   266  		{
   267  			Signatures: []Signature{{KeyID: key.KeyID, Sig: "bad sig"}},
   268  		},
   269  		{
   270  			Signatures: []Signature{{KeyID: key.KeyID}},
   271  			Signed:     TestMetablockVerifySignature,
   272  		},
   273  	}
   274  	expectedErrors := []string{
   275  		"no signature found",
   276  		"encoding/hex: invalid byte: U+0020 ' '",
   277  		"json: unsupported type",
   278  	}
   279  	for i := 0; i < len(mbs); i++ {
   280  		err := mbs[i].VerifySignature(key)
   281  		if err == nil || !strings.Contains(err.Error(), expectedErrors[i]) {
   282  			t.Errorf("Metablock.VerifySignature returned '%s', expected '%s'",
   283  				err, expectedErrors[i])
   284  		}
   285  	}
   286  
   287  	// Test successful metablock signature verification
   288  	var mb Metablock
   289  	if err := mb.Load("demo.layout"); err != nil {
   290  		t.Errorf("cannot parse template file: %s", err)
   291  	}
   292  	err := mb.VerifySignature(key)
   293  	if err != nil {
   294  		t.Errorf("Metablock.VerifySignature returned '%s', expected nil", err)
   295  	}
   296  }
   297  
   298  func TestValidateLink(t *testing.T) {
   299  	var mb Metablock
   300  	if err := mb.Load("package.d3ffd108.link"); err != nil {
   301  		t.Errorf("Metablock.Load returned '%s'", err)
   302  	}
   303  	if err := validateLink(mb.Signed.(Link)); err != nil {
   304  		t.Errorf("link metadata validation failed, returned '%s'", err)
   305  	}
   306  
   307  	testMb := Metablock{
   308  		Signed: Link{
   309  			Type: "invalid",
   310  			Name: "test_type",
   311  			Command: []string{
   312  				"tar",
   313  				"zcvf",
   314  				"foo.tar.gz",
   315  				"foo.py",
   316  			},
   317  			Materials: map[string]HashObj{
   318  				"foo.py": {
   319  					"sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a27c013544a60502549",
   320  				},
   321  			},
   322  			Products: map[string]HashObj{
   323  				"foo.tar.gz": {
   324  					"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   325  				},
   326  			},
   327  			ByProducts: map[string]interface{}{
   328  				"return-value": float64(0),
   329  				"stderr":       "a foo.py\n",
   330  				"stdout":       "",
   331  			},
   332  			Environment: map[string]interface{}{},
   333  		},
   334  	}
   335  
   336  	err := validateLink(testMb.Signed.(Link))
   337  	if err.Error() != "invalid type for link 'test_type': should be 'link'" {
   338  		t.Error("validateLink error - incorrect type not detected")
   339  	}
   340  
   341  	testMb = Metablock{
   342  		Signed: Link{
   343  			Type: "link",
   344  			Name: "test_material_hash",
   345  			Command: []string{
   346  				"tar",
   347  				"zcvf",
   348  				"foo.tar.gz",
   349  				"foo.py",
   350  			},
   351  			Materials: map[string]HashObj{
   352  				"foo.py": {
   353  					"sha256": "!@#$%",
   354  				},
   355  			},
   356  			Products: map[string]HashObj{
   357  				"foo.tar.gz": {
   358  					"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e69" +
   359  						"36c1e5aabb7c98514f355",
   360  				},
   361  			},
   362  			ByProducts: map[string]interface{}{
   363  				"return-value": float64(0),
   364  				"stderr":       "a foo.py\n",
   365  				"stdout":       "",
   366  			},
   367  			Environment: map[string]interface{}{},
   368  		},
   369  	}
   370  
   371  	err = validateLink(testMb.Signed.(Link))
   372  	if err.Error() != "in materials of link 'test_material_hash': in artifact"+
   373  		" 'foo.py', sha256 hash value: invalid hex string: !@#$%" {
   374  		t.Error("validateLink error - invalid hashes not detected")
   375  	}
   376  
   377  	testMb = Metablock{
   378  		Signed: Link{
   379  			Type: "link",
   380  			Name: "test_product_hash",
   381  			Command: []string{
   382  				"tar",
   383  				"zcvf",
   384  				"foo.tar.gz",
   385  				"foo.py",
   386  			},
   387  			Materials: map[string]HashObj{
   388  				"foo.py": {
   389  					"sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a27c013544a60502549",
   390  				},
   391  			},
   392  			Products: map[string]HashObj{
   393  				"foo.tar.gz": {
   394  					"sha256": "!@#$%",
   395  				},
   396  			},
   397  			ByProducts: map[string]interface{}{
   398  				"return-value": float64(0),
   399  				"stderr":       "a foo.py\n",
   400  				"stdout":       "",
   401  			},
   402  			Environment: map[string]interface{}{},
   403  		},
   404  	}
   405  
   406  	err = validateLink(testMb.Signed.(Link))
   407  	if err.Error() != "in products of link 'test_product_hash': in artifact "+
   408  		"'foo.tar.gz', sha256 hash value: invalid hex string: !@#$%" {
   409  		t.Error("validateLink error - invalid hashes not detected")
   410  	}
   411  }
   412  
   413  func TestValidateLayout(t *testing.T) {
   414  	var mb Metablock
   415  	if err := mb.Load("demo.layout"); err != nil {
   416  		t.Errorf("Metablock.Load returned '%s'", err)
   417  	}
   418  	if err := validateLayout(mb.Signed.(Layout)); err != nil {
   419  		t.Errorf("layout metadata validation failed, returned '%s'", err)
   420  	}
   421  
   422  	testMb := Metablock{
   423  		Signed: Layout{
   424  			Type:    "invalid",
   425  			Expires: "2020-11-18T16:06:36Z",
   426  			Readme:  "some readme text",
   427  			Steps:   []Step{},
   428  			Inspect: []Inspection{},
   429  			Keys:    map[string]Key{},
   430  		},
   431  	}
   432  
   433  	err := validateLayout(testMb.Signed.(Layout))
   434  	if err.Error() != "invalid Type value for layout: should be 'layout'" {
   435  		t.Error("validateLayout error - invalid type not detected")
   436  	}
   437  
   438  	testMb = Metablock{
   439  		Signed: Layout{
   440  			Type:    "layout",
   441  			Expires: "2020-02-31T18:03:43Z",
   442  			Readme:  "some readme text",
   443  			Steps:   []Step{},
   444  			Inspect: []Inspection{},
   445  			Keys:    map[string]Key{},
   446  		},
   447  	}
   448  
   449  	err = validateLayout(testMb.Signed.(Layout))
   450  	if err.Error() != "expiry time parsed incorrectly - date either invalid "+
   451  		"or of incorrect format" {
   452  		t.Error("validateLayout error - invalid date not detected")
   453  	}
   454  
   455  	testMb = Metablock{
   456  		Signed: Layout{
   457  			Type:    "layout",
   458  			Expires: "2020-02-27T18:03:43Zinvalid",
   459  			Readme:  "some readme text",
   460  			Steps:   []Step{},
   461  			Inspect: []Inspection{},
   462  			Keys:    map[string]Key{},
   463  		},
   464  	}
   465  
   466  	err = validateLayout(testMb.Signed.(Layout))
   467  	if err.Error() != "expiry time parsed incorrectly - date either invalid "+
   468  		"or of incorrect format" {
   469  		t.Error("validateLayout error - invalid date not detected")
   470  	}
   471  
   472  	testMb = Metablock{
   473  		Signed: Layout{
   474  			Type:    "layout",
   475  			Expires: "2020-02-27T18:03:43Z",
   476  			Readme:  "some readme text",
   477  			Steps: []Step{
   478  				{
   479  					Type: "step",
   480  					SupplyChainItem: SupplyChainItem{
   481  						Name: "foo",
   482  					},
   483  				},
   484  				{
   485  					Type: "step",
   486  					SupplyChainItem: SupplyChainItem{
   487  						Name: "foo",
   488  					},
   489  				},
   490  			},
   491  			Inspect: []Inspection{},
   492  			Keys:    map[string]Key{},
   493  		},
   494  	}
   495  
   496  	err = validateLayout(testMb.Signed.(Layout))
   497  	if err.Error() != "non unique step or inspection name found" {
   498  		t.Error("validateLayout error - duplicate step/inspection name not " +
   499  			"detected")
   500  	}
   501  
   502  	testMb = Metablock{
   503  		Signed: Layout{
   504  			Type:    "layout",
   505  			Expires: "2020-02-27T18:03:43Z",
   506  			Readme:  "some readme text",
   507  			Steps: []Step{
   508  				{
   509  					Type: "step",
   510  					SupplyChainItem: SupplyChainItem{
   511  						Name: "foo",
   512  					},
   513  				},
   514  			},
   515  			Inspect: []Inspection{
   516  				{
   517  					Type: "inspection",
   518  					SupplyChainItem: SupplyChainItem{
   519  						Name: "foo",
   520  					},
   521  				},
   522  			},
   523  			Keys: map[string]Key{},
   524  		},
   525  	}
   526  
   527  	err = validateLayout(testMb.Signed.(Layout))
   528  	if err.Error() != "non unique step or inspection name found" {
   529  		t.Error("validateLayout error - duplicate step/inspection name not " +
   530  			"detected")
   531  	}
   532  
   533  	testMb = Metablock{
   534  		Signed: Layout{
   535  			Type:    "layout",
   536  			Expires: "2020-02-27T18:03:43Z",
   537  			Readme:  "some readme text",
   538  			Steps:   []Step{},
   539  			Inspect: []Inspection{
   540  				{
   541  					Type: "inspection",
   542  					SupplyChainItem: SupplyChainItem{
   543  						Name: "foo",
   544  					},
   545  				},
   546  				{
   547  					Type: "inspection",
   548  					SupplyChainItem: SupplyChainItem{
   549  						Name: "foo",
   550  					},
   551  				},
   552  			},
   553  			Keys: map[string]Key{},
   554  		},
   555  	}
   556  
   557  	err = validateLayout(testMb.Signed.(Layout))
   558  	if err.Error() != "non unique step or inspection name found" {
   559  		t.Error("validateLayout error - duplicate step/inspection name not " +
   560  			"detected")
   561  	}
   562  
   563  	testMb = Metablock{
   564  		Signed: Layout{
   565  			Type:    "layout",
   566  			Expires: "2020-02-27T18:03:43Z",
   567  			Readme:  "some readme text",
   568  			Steps: []Step{
   569  				{
   570  					Type: "invalid",
   571  					SupplyChainItem: SupplyChainItem{
   572  						Name: "foo",
   573  					},
   574  				},
   575  			},
   576  			Inspect: []Inspection{},
   577  			Keys:    map[string]Key{},
   578  		},
   579  	}
   580  
   581  	err = validateLayout(testMb.Signed.(Layout))
   582  	if err.Error() != "invalid Type value for step 'foo': should be 'step'" {
   583  		t.Error("validateLayout - validateStep error - invalid step type not " +
   584  			"detected")
   585  	}
   586  
   587  	cases := map[string]struct {
   588  		Arg      Layout
   589  		Expected string
   590  	}{
   591  		"invalid key map": {
   592  			Layout{
   593  				Type:    "layout",
   594  				Expires: "2020-02-27T18:03:43Z",
   595  				Keys: map[string]Key{
   596  					"deadbeef": {KeyID: "livebeef"},
   597  				},
   598  			},
   599  			"invalid key found",
   600  		},
   601  		"invalid rsa key": {
   602  			Layout{
   603  				Type:    "layout",
   604  				Expires: "2020-02-27T18:03:43Z",
   605  				Keys: map[string]Key{
   606  					"deadbeef": {KeyID: "deadbeef"},
   607  				},
   608  			},
   609  			"empty field in key: keytype",
   610  		},
   611  	}
   612  
   613  	for name, tc := range cases {
   614  		err := validateLayout(tc.Arg)
   615  		if err == nil || !strings.Contains(err.Error(), tc.Expected) {
   616  			t.Errorf("%s: '%s' not in '%s'", name, tc.Expected, err)
   617  		}
   618  	}
   619  }
   620  
   621  func TestValidateStep(t *testing.T) {
   622  	testStep := Step{
   623  		Type: "invalid",
   624  		SupplyChainItem: SupplyChainItem{
   625  			Name: "foo",
   626  		},
   627  	}
   628  	err := validateStep(testStep)
   629  	if err.Error() != "invalid Type value for step 'foo': should be 'step'" {
   630  		t.Error("validateStep error - invalid type not detected")
   631  	}
   632  
   633  	testStep = Step{
   634  		Type: "step",
   635  		PubKeys: []string{"776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f4Z" +
   636  			"41132b345b08453f5"},
   637  		SupplyChainItem: SupplyChainItem{
   638  			Name: "foo",
   639  		},
   640  	}
   641  	err = validateStep(testStep)
   642  	if !errors.Is(err, ErrInvalidHexString) {
   643  		t.Error("validateStep - validateHexString error - invalid key ID not " +
   644  			"detected")
   645  	}
   646  
   647  	testStep = Step{
   648  		Type: "step",
   649  		SupplyChainItem: SupplyChainItem{
   650  			Name: "",
   651  		},
   652  	}
   653  	err = validateStep(testStep)
   654  	if err.Error() != "step name cannot be empty" {
   655  		t.Error("validateStep error - empty name not detected")
   656  	}
   657  }
   658  
   659  func TestValidateInspection(t *testing.T) {
   660  	testInspection := Inspection{
   661  		Type: "invalid",
   662  		SupplyChainItem: SupplyChainItem{
   663  			Name: "foo",
   664  		},
   665  	}
   666  	err := validateInspection(testInspection)
   667  	if err.Error() != "invalid Type value for inspection 'foo': should be "+
   668  		"'inspection'" {
   669  		t.Error("validateInspection error - invalid type not detected")
   670  	}
   671  	testInspection = Inspection{
   672  		Type: "inspection",
   673  		SupplyChainItem: SupplyChainItem{
   674  			Name: "",
   675  		},
   676  	}
   677  	err = validateInspection(testInspection)
   678  	if err.Error() != "inspection name cannot be empty" {
   679  		t.Error("validateInspection error - empty name not detected")
   680  	}
   681  
   682  	testInspection = Inspection{
   683  		Type: "inspection",
   684  		SupplyChainItem: SupplyChainItem{
   685  			Name: "inspect",
   686  		},
   687  	}
   688  	err = validateInspection(testInspection)
   689  	if err != nil {
   690  		t.Error("validateInspection should successfully validate an inspection")
   691  	}
   692  }
   693  
   694  func TestValidateHexSchema(t *testing.T) {
   695  	testStr := "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b" +
   696  		"08453f5"
   697  	if err := validateHexString(testStr); err != nil {
   698  		t.Errorf("validateHexString error - valid key ID flagged")
   699  	}
   700  
   701  	testStr = "Z776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b" +
   702  		"08453f5"
   703  	if err := validateHexString(testStr); err == nil {
   704  		t.Errorf("validateHexString error - invalid key ID not detected")
   705  	}
   706  }
   707  
   708  func TestValidatePubKey(t *testing.T) {
   709  	testKey := Key{
   710  		KeyID:   "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5",
   711  		KeyType: "rsa",
   712  		KeyVal: KeyVal{
   713  			Private: "",
   714  			Public: "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAO" +
   715  				"CAY8AMIIBigKCAYEAzgLBsMFSgwBiWTBmVsyW\n5KbJwLFSodAzdUhU2Bq6" +
   716  				"SdRz/W6UOBGdojZXibxupjRtAaEQW/eXDe+1CbKg6ENZ\nGt2D9HGFCQZgQ" +
   717  				"S8ONgNDQGiNxgApMA0T21AaUhru0vEofzdN1DfEF4CAGv5AkcgK\nsalhTy" +
   718  				"ONervFIjFEdXGelFZ7dVMV3Pp5WkZPG0jFQWjnmDZhUrtSxEtqbVghc3kK" +
   719  				"\nAUj9Ll/3jyi2wS92Z1j5ueN8X62hWX2xBqQ6nViOMzdujkoiYCRSwuMLR" +
   720  				"qzW2CbT\nL8hF1+S5KWKFzxl5sCVfpPe7V5HkgEHjwCILXTbCn2fCMKlaSb" +
   721  				"J/MG2lW7qSY2Ro\nwVXWkp1wDrsJ6Ii9f2dErv9vJeOVZeO9DsooQ5EuzLC" +
   722  				"fQLEU5mn7ul7bU7rFsb8J\nxYOeudkNBatnNCgVMAkmDPiNA7E33bmL5ARR" +
   723  				"wU0iZicsqLQR32pmwdap8PjofxqQ\nk7Gtvz/iYzaLrZv33cFWWTsEOqK1g" +
   724  				"KqigSqgW9T26wO9AgMBAAE=\n-----END PUBLIC KEY-----",
   725  		},
   726  		Scheme: "rsassa-pss-sha256",
   727  	}
   728  
   729  	err := validatePublicKey(testKey)
   730  	if !errors.Is(err, nil) {
   731  		t.Errorf("error validating public key: %s", err)
   732  	}
   733  
   734  	testKey = Key{
   735  		KeyID:   "Z776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5",
   736  		KeyType: "rsa",
   737  		KeyVal: KeyVal{
   738  			Private: "",
   739  			Public: "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAO" +
   740  				"CAY8AMIIBigKCAYEAzgLBsMFSgwBiWTBmVsyW\n5KbJwLFSodAzdUhU2Bq6" +
   741  				"SdRz/W6UOBGdojZXibxupjRtAaEQW/eXDe+1CbKg6ENZ\nGt2D9HGFCQZgQ" +
   742  				"S8ONgNDQGiNxgApMA0T21AaUhru0vEofzdN1DfEF4CAGv5AkcgK\nsalhTy" +
   743  				"ONervFIjFEdXGelFZ7dVMV3Pp5WkZPG0jFQWjnmDZhUrtSxEtqbVghc3kK" +
   744  				"\nAUj9Ll/3jyi2wS92Z1j5ueN8X62hWX2xBqQ6nViOMzdujkoiYCRSwuMLR" +
   745  				"qzW2CbT\nL8hF1+S5KWKFzxl5sCVfpPe7V5HkgEHjwCILXTbCn2fCMKlaSb" +
   746  				"J/MG2lW7qSY2Ro\nwVXWkp1wDrsJ6Ii9f2dErv9vJeOVZeO9DsooQ5EuzLC" +
   747  				"fQLEU5mn7ul7bU7rFsb8J\nxYOeudkNBatnNCgVMAkmDPiNA7E33bmL5ARR" +
   748  				"wU0iZicsqLQR32pmwdap8PjofxqQ\nk7Gtvz/iYzaLrZv33cFWWTsEOqK1g" +
   749  				"KqigSqgW9T26wO9AgMBAAE=\n-----END PUBLIC KEY-----",
   750  		},
   751  		Scheme: "rsassa-pss-sha256",
   752  	}
   753  
   754  	err = validateKey(testKey)
   755  	if !errors.Is(err, ErrInvalidHexString) {
   756  		t.Error("validateKey error - invalid key ID not detected")
   757  	}
   758  
   759  	testKey = Key{
   760  		KeyID:   "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5",
   761  		KeyType: "rsa",
   762  		KeyVal: KeyVal{
   763  			Private: "invalid",
   764  			Public: "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAO" +
   765  				"CAY8AMIIBigKCAYEAzgLBsMFSgwBiWTBmVsyW\n5KbJwLFSodAzdUhU2Bq6" +
   766  				"SdRz/W6UOBGdojZXibxupjRtAaEQW/eXDe+1CbKg6ENZ\nGt2D9HGFCQZgQ" +
   767  				"S8ONgNDQGiNxgApMA0T21AaUhru0vEofzdN1DfEF4CAGv5AkcgK\nsalhTy" +
   768  				"ONervFIjFEdXGelFZ7dVMV3Pp5WkZPG0jFQWjnmDZhUrtSxEtqbVghc3kK" +
   769  				"\nAUj9Ll/3jyi2wS92Z1j5ueN8X62hWX2xBqQ6nViOMzdujkoiYCRSwuMLR" +
   770  				"qzW2CbT\nL8hF1+S5KWKFzxl5sCVfpPe7V5HkgEHjwCILXTbCn2fCMKlaSb" +
   771  				"J/MG2lW7qSY2Ro\nwVXWkp1wDrsJ6Ii9f2dErv9vJeOVZeO9DsooQ5EuzLC" +
   772  				"fQLEU5mn7ul7bU7rFsb8J\nxYOeudkNBatnNCgVMAkmDPiNA7E33bmL5ARR" +
   773  				"wU0iZicsqLQR32pmwdap8PjofxqQ\nk7Gtvz/iYzaLrZv33cFWWTsEOqK1g" +
   774  				"KqigSqgW9T26wO9AgMBAAE=\n-----END PUBLIC KEY-----",
   775  		},
   776  		Scheme: "rsassa-pss-sha256",
   777  	}
   778  
   779  	err = validatePublicKey(testKey)
   780  	if !errors.Is(err, ErrNoPublicKey) {
   781  		t.Error("validateKey error - private key not detected")
   782  	}
   783  
   784  	testKey = Key{
   785  		KeyID:   "776a00e29f3559e0141b3b096f696abc6cfb0c657ab40f441132b345b08453f5",
   786  		KeyType: "rsa",
   787  		KeyVal: KeyVal{
   788  			Private: "",
   789  			Public:  "",
   790  		},
   791  		Scheme: "rsassa-pss-sha256",
   792  	}
   793  
   794  	err = validateKey(testKey)
   795  	if !errors.Is(err, ErrEmptyKeyField) {
   796  		t.Error("validateKey error - empty public key not detected")
   797  	}
   798  }
   799  
   800  func TestValidateMetablock(t *testing.T) {
   801  	testMetablock := Metablock{
   802  		Signatures: []Signature{
   803  			{
   804  				KeyID: "556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b58" +
   805  					"8f3e9cc48b35",
   806  				Sig: "02813858670c66647c17802d84f06453589f41850013a544609e9d" +
   807  					"33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af" +
   808  					"9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d5255" +
   809  					"51d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93" +
   810  					"e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb2271271" +
   811  					"78d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe59580" +
   812  					"4f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b5339" +
   813  					"58f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479" +
   814  					"d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b9" +
   815  					"2cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41d" +
   816  					"ae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef" +
   817  					"48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65a" +
   818  					"a626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63" +
   819  					"bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17",
   820  			},
   821  		},
   822  		Signed: Layout{
   823  			Type:    "layout",
   824  			Expires: "2020-11-18T16:06:36Z",
   825  			Readme:  "some readme text",
   826  			Steps:   []Step{},
   827  			Inspect: []Inspection{},
   828  			Keys:    map[string]Key{},
   829  		},
   830  	}
   831  
   832  	if err := ValidateMetablock(testMetablock); err != nil {
   833  		t.Error("ValidateMetablock error: valid metablock failed")
   834  	}
   835  
   836  	testMetablock = Metablock{
   837  		Signatures: []Signature{
   838  			{
   839  				KeyID: "556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b58" +
   840  					"8f3e9cc48b35",
   841  				Sig: "02813858670c66647c17802d84f06453589f41850013a544609e9d" +
   842  					"33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af" +
   843  					"9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d5255" +
   844  					"51d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93" +
   845  					"e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb2271271" +
   846  					"78d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe59580" +
   847  					"4f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b5339" +
   848  					"58f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479" +
   849  					"d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b9" +
   850  					"2cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41d" +
   851  					"ae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef" +
   852  					"48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65a" +
   853  					"a626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63" +
   854  					"bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17",
   855  			},
   856  		},
   857  		Signed: Link{
   858  			Type: "link",
   859  			Name: "test_type",
   860  			Command: []string{
   861  				"tar",
   862  				"zcvf",
   863  				"foo.tar.gz",
   864  				"foo.py",
   865  			},
   866  			Materials: map[string]HashObj{
   867  				"foo.py": {
   868  					"sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a" +
   869  						"27c013544a60502549",
   870  				},
   871  			},
   872  			Products: map[string]HashObj{
   873  				"foo.tar.gz": {
   874  					"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c" +
   875  						"1e5aabb7c98514f355",
   876  				},
   877  			},
   878  			ByProducts: map[string]interface{}{
   879  				"return-value": float64(0),
   880  				"stderr":       "a foo.py\n",
   881  				"stdout":       "",
   882  			},
   883  			Environment: map[string]interface{}{},
   884  		},
   885  	}
   886  
   887  	if err := ValidateMetablock(testMetablock); err != nil {
   888  		t.Error("ValidateMetablock error: valid metablock failed")
   889  	}
   890  
   891  	testMetablock = Metablock{
   892  		Signatures: []Signature{
   893  			{
   894  				KeyID: "556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b58" +
   895  					"8f3e9cc48b35",
   896  				Sig: "02813858670c66647c17802d84f06453589f41850013a544609e9d" +
   897  					"33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af" +
   898  					"9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d5255" +
   899  					"51d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93" +
   900  					"e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb2271271" +
   901  					"78d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe59580" +
   902  					"4f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b5339" +
   903  					"58f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479" +
   904  					"d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b9" +
   905  					"2cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41d" +
   906  					"ae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef" +
   907  					"48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65a" +
   908  					"a626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63" +
   909  					"bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17",
   910  			},
   911  		},
   912  		Signed: Layout{
   913  			Type:    "invalid",
   914  			Expires: "2020-11-18T16:06:36Z",
   915  			Readme:  "some readme text",
   916  			Steps:   []Step{},
   917  			Inspect: []Inspection{},
   918  			Keys:    map[string]Key{},
   919  		},
   920  	}
   921  
   922  	if err := ValidateMetablock(testMetablock); err.Error() !=
   923  		"invalid Type value for layout: should be 'layout'" {
   924  		t.Error("ValidateMetablock Error: invalid Type not detected")
   925  	}
   926  
   927  	testMetablock = Metablock{
   928  		Signatures: []Signature{
   929  			{
   930  				KeyID: "556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b58" +
   931  					"8f3e9cc48b35",
   932  				Sig: "02813858670c66647c17802d84f06453589f41850013a544609e9d" +
   933  					"33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af" +
   934  					"9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d5255" +
   935  					"51d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93" +
   936  					"e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb2271271" +
   937  					"78d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe59580" +
   938  					"4f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b5339" +
   939  					"58f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479" +
   940  					"d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b9" +
   941  					"2cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41d" +
   942  					"ae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef" +
   943  					"48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65a" +
   944  					"a626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63" +
   945  					"bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17",
   946  			},
   947  		},
   948  		Signed: Link{
   949  			Type: "invalid",
   950  			Name: "test_type",
   951  			Command: []string{
   952  				"tar",
   953  				"zcvf",
   954  				"foo.tar.gz",
   955  				"foo.py",
   956  			},
   957  			Materials: map[string]HashObj{
   958  				"foo.py": {
   959  					"sha256": "74dc3727c6e89308b39e4dfedf787e37841198b1fa165a" +
   960  						"27c013544a60502549",
   961  				},
   962  			},
   963  			Products: map[string]HashObj{
   964  				"foo.tar.gz": {
   965  					"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c" +
   966  						"1e5aabb7c98514f355",
   967  				},
   968  			},
   969  			ByProducts: map[string]interface{}{
   970  				"return-value": float64(0),
   971  				"stderr":       "a foo.py\n",
   972  				"stdout":       "",
   973  			},
   974  			Environment: map[string]interface{}{},
   975  		},
   976  	}
   977  
   978  	if err := ValidateMetablock(testMetablock); err.Error() !=
   979  		"invalid type for link 'test_type': should be 'link'" {
   980  		t.Error("ValidateMetablock Error: invalid Type not detected")
   981  	}
   982  
   983  	testMetablock = Metablock{
   984  		Signatures: []Signature{
   985  			{
   986  				KeyID: "Z556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b5" +
   987  					"8f3e9cc48b35",
   988  				Sig: "02813858670c66647c17802d84f06453589f41850013a544609e9d" +
   989  					"33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af" +
   990  					"9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d5255" +
   991  					"51d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93" +
   992  					"e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb2271271" +
   993  					"78d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe59580" +
   994  					"4f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b5339" +
   995  					"58f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479" +
   996  					"d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b9" +
   997  					"2cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41d" +
   998  					"ae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef" +
   999  					"48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65a" +
  1000  					"a626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63" +
  1001  					"bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17",
  1002  			},
  1003  		},
  1004  		Signed: Layout{
  1005  			Type:    "layout",
  1006  			Expires: "2020-11-18T16:06:36Z",
  1007  			Readme:  "some readme text",
  1008  			Steps:   []Step{},
  1009  			Inspect: []Inspection{},
  1010  			Keys:    map[string]Key{},
  1011  		},
  1012  	}
  1013  
  1014  	err := ValidateMetablock(testMetablock)
  1015  	if !errors.Is(err, ErrInvalidHexString) {
  1016  		t.Error("ValidateMetablock Error: invalid key ID not detected")
  1017  	}
  1018  
  1019  	testMetablock = Metablock{
  1020  		Signatures: []Signature{
  1021  			{
  1022  				KeyID: "556caebdc0877eed53d419b60eddb1e57fa773e4e31d70698b58" +
  1023  					"8f3e9cc48b35",
  1024  				Sig: "02813858670c66647c17802d84f06453589f41850013a544609e9z" +
  1025  					"33ba21fa19280e8371701f8274fb0c56bd95ff4f34c418456b002af" +
  1026  					"9836ca218b584f51eb0eaacbb1c9bb57448101b07d058dec04d5255" +
  1027  					"51d157f6ae5e3679701735b1b8f52430f9b771d5476db1a2053cd93" +
  1028  					"e2354f20061178a01705f2fa9ac82c7aeca4dd830e2672eb2271271" +
  1029  					"78d52328747ac819e50ec8ff52c662d7a4c58f5040d8f655fe59580" +
  1030  					"4f3e47c4fc98434c44e914445f7cb773439ebf813de8849dd1b5339" +
  1031  					"58f99f671d4e023d34c110d4b169cc02c12a3755ebe537147ff2479" +
  1032  					"d244daaf719e24cf6b2fa6f47d0410d52d67217bcf4d4d4c2c7c0b9" +
  1033  					"2cd2bcd321edc69bc1430f78a188e712b8cb1fff0c14550cd01c41d" +
  1034  					"ae377256f31211fd249c5031bfee86e638bce6aa36aca349b787cef" +
  1035  					"48255b0ef04bd0a21adb37b2a3da888d1530ca6ddeae5261e6fd65a" +
  1036  					"a626d5caebbfae2986f842bd2ce94bcefe5dd0ae9c5b2028a15bd63" +
  1037  					"bbea61be732207f0f5b58d056f118c830981747cb2b245d1377e17",
  1038  			},
  1039  		},
  1040  		Signed: Layout{
  1041  			Type:    "layout",
  1042  			Expires: "2020-11-18T16:06:36Z",
  1043  			Readme:  "some readme text",
  1044  			Steps:   []Step{},
  1045  			Inspect: []Inspection{},
  1046  			Keys:    map[string]Key{},
  1047  		},
  1048  	}
  1049  
  1050  	err = ValidateMetablock(testMetablock)
  1051  	if !errors.Is(err, ErrInvalidHexString) {
  1052  		t.Error("ValidateMetablock error: invalid signature not detected")
  1053  	}
  1054  
  1055  	cases := map[string]struct {
  1056  		Arg      Metablock
  1057  		Expected string
  1058  	}{
  1059  		"invalid type": {
  1060  			Metablock{Signed: "invalid"},
  1061  			"unknown type 'invalid', should be 'layout' or 'link'",
  1062  		},
  1063  	}
  1064  	for name, tc := range cases {
  1065  		err := ValidateMetablock(tc.Arg)
  1066  		if err == nil || !strings.Contains(err.Error(), tc.Expected) {
  1067  			t.Errorf("%s: '%s' not in '%s'", name, tc.Expected, err)
  1068  		}
  1069  	}
  1070  }
  1071  
  1072  func TestValidateSupplyChainItem(t *testing.T) {
  1073  	cases := map[string]struct {
  1074  		Arg      SupplyChainItem
  1075  		Expected string
  1076  	}{
  1077  		"empty name": {SupplyChainItem{Name: ""}, "name cannot be empty"},
  1078  		"material rule": {
  1079  			SupplyChainItem{
  1080  				Name:              "test",
  1081  				ExpectedMaterials: [][]string{{"invalid"}}},
  1082  			"invalid material rule"},
  1083  		"product rule": {
  1084  			SupplyChainItem{
  1085  				Name:             "test",
  1086  				ExpectedProducts: [][]string{{"invalid"}}},
  1087  			"invalid product rule"},
  1088  	}
  1089  
  1090  	for name, tc := range cases {
  1091  		err := validateSupplyChainItem(tc.Arg)
  1092  		if err == nil || !strings.Contains(err.Error(), tc.Expected) {
  1093  			t.Errorf("%s: '%s' not in '%s'", name, tc.Expected, err)
  1094  		}
  1095  	}
  1096  }
  1097  
  1098  func TestMetablockSignWithRSA(t *testing.T) {
  1099  	var mb Metablock
  1100  	if err := mb.Load("demo.layout"); err != nil {
  1101  		t.Errorf("cannot parse template file: %s", err)
  1102  	}
  1103  	invalidKey := Key{
  1104  		KeyID:               "test",
  1105  		KeyIDHashAlgorithms: nil,
  1106  		KeyType:             "rsa",
  1107  		KeyVal:              KeyVal{},
  1108  		Scheme:              "rsassa-pss-sha256",
  1109  	}
  1110  
  1111  	if err := mb.Sign(invalidKey); err == nil {
  1112  		t.Errorf("signing with an invalid RSA key should fail")
  1113  	}
  1114  }
  1115  
  1116  func TestMetablockSignWithEd25519(t *testing.T) {
  1117  	var mb Metablock
  1118  	if err := mb.Load("demo.layout"); err != nil {
  1119  		t.Errorf("cannot parse template file: %s", err)
  1120  	}
  1121  	invalidKey := Key{
  1122  		KeyID:               "invalid",
  1123  		KeyIDHashAlgorithms: nil,
  1124  		KeyType:             "ed25519",
  1125  		KeyVal: KeyVal{
  1126  			Private: "BAD",
  1127  			Public:  "BAD",
  1128  		},
  1129  		Scheme: "ed25519",
  1130  	}
  1131  
  1132  	if err := mb.Sign(invalidKey); err == nil {
  1133  		t.Errorf("signing with an invalid ed25519 key should fail")
  1134  	}
  1135  }
  1136  
  1137  func TestMetaBlockSignWithEcdsa(t *testing.T) {
  1138  	var mb Metablock
  1139  	if err := mb.Load("demo.layout"); err != nil {
  1140  		t.Errorf("cannot parse template file: %s", err)
  1141  	}
  1142  	invalidKey := Key{
  1143  		KeyID:               "invalid",
  1144  		KeyIDHashAlgorithms: nil,
  1145  		KeyType:             "ecdsa",
  1146  		KeyVal: KeyVal{
  1147  			Private: "BAD",
  1148  			Public:  "BAD",
  1149  		},
  1150  		Scheme: "ecdsa",
  1151  	}
  1152  	if err := mb.Sign(invalidKey); err == nil {
  1153  		t.Errorf("signing with an invalid ecdsa key should fail")
  1154  	}
  1155  }
  1156  
  1157  func TestValidateKeyErrors(t *testing.T) {
  1158  	invalidTables := []struct {
  1159  		name string
  1160  		key  Key
  1161  		err  error
  1162  	}{
  1163  		{"empty key", Key{
  1164  			KeyID:               "",
  1165  			KeyIDHashAlgorithms: nil,
  1166  			KeyType:             "",
  1167  			KeyVal:              KeyVal{},
  1168  			Scheme:              "",
  1169  		}, ErrInvalidHexString},
  1170  		{"keytype missing", Key{
  1171  			KeyID:               "bad",
  1172  			KeyIDHashAlgorithms: []string{"sha256"},
  1173  			KeyType:             "",
  1174  			KeyVal: KeyVal{
  1175  				Private: "",
  1176  				Public:  "",
  1177  			},
  1178  			Scheme: "rsassa-psa-sha256",
  1179  		}, ErrEmptyKeyField},
  1180  		{"key scheme missing", Key{
  1181  			KeyID:               "bad",
  1182  			KeyIDHashAlgorithms: []string{"sha256"},
  1183  			KeyType:             "ed25519",
  1184  			KeyVal: KeyVal{
  1185  				Private: "bad",
  1186  				Public:  "bad",
  1187  			},
  1188  			Scheme: "",
  1189  		}, ErrEmptyKeyField},
  1190  		{
  1191  			name: "invalid key type",
  1192  			key: Key{
  1193  				KeyID:               "bad",
  1194  				KeyIDHashAlgorithms: []string{"sha256"},
  1195  				KeyType:             "invalid",
  1196  				KeyVal: KeyVal{
  1197  					Private: "invalid",
  1198  					Public:  "393e671b200f964c49083d34a867f5d989ec1c69df7b66758fe471c8591b139c",
  1199  				},
  1200  				Scheme: "ed25519",
  1201  			},
  1202  			err: ErrUnsupportedKeyType,
  1203  		},
  1204  		{
  1205  			name: "keytype scheme mismatch",
  1206  			key: Key{
  1207  				KeyID:               "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
  1208  				KeyIDHashAlgorithms: []string{"sha256"},
  1209  				KeyType:             "ed25519",
  1210  				KeyVal: KeyVal{
  1211  					Private: "29ad59693fe94c9d623afbb66554b4f6bb248c47761689ada4875ebda94840ae393e671b200f964c49083d34a867f5d989ec1c69df7b66758fe471c8591b139c",
  1212  					Public:  "393e671b200f964c49083d34a867f5d989ec1c69df7b66758fe471c8591b139c",
  1213  				},
  1214  				Scheme: "rsassa-pss-sha256",
  1215  			},
  1216  			err: ErrSchemeKeyTypeMismatch,
  1217  		},
  1218  		{
  1219  			name: "unsupported KeyIDHashAlgorithms",
  1220  			key: Key{
  1221  				KeyID:               "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
  1222  				KeyIDHashAlgorithms: []string{"sha128"},
  1223  				KeyType:             "ed25519",
  1224  				KeyVal: KeyVal{
  1225  					Private: "29ad59693fe94c9d623afbb66554b4f6bb248c47761689ada4875ebda94840ae393e671b200f964c49083d34a867f5d989ec1c69df7b66758fe471c8591b139c",
  1226  					Public:  "393e671b200f964c49083d34a867f5d989ec1c69df7b66758fe471c8591b139c",
  1227  				},
  1228  				Scheme: "ed25519",
  1229  			},
  1230  			err: ErrUnsupportedKeyIDHashAlgorithms,
  1231  		},
  1232  	}
  1233  
  1234  	for _, table := range invalidTables {
  1235  		err := validateKey(table.key)
  1236  		if !errors.Is(err, table.err) {
  1237  			t.Errorf("test '%s' failed, expected error: '%s', got '%s'", table.name, table.err, err)
  1238  		}
  1239  	}
  1240  }
  1241  
  1242  func TestValidateKeyVal(t *testing.T) {
  1243  	tables := []struct {
  1244  		name string
  1245  		key  Key
  1246  		err  error
  1247  	}{
  1248  		{
  1249  			name: "invalid rsa private key",
  1250  			key: Key{
  1251  				KeyID:               "bad",
  1252  				KeyIDHashAlgorithms: []string{"sha256"},
  1253  				KeyType:             "rsa",
  1254  				KeyVal: KeyVal{
  1255  					Private: "invalid",
  1256  					Public:  "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxPX3kFs/z645x4UOC3KF\nY3V80YQtKrp6YS3qU+Jlvx/XzK53lb4sCDRU9jqBBx3We45TmFUibroMd8tQXCUS\ne8gYCBUBqBmmz0dEHJYbW0tYF7IoapMIxhRYn76YqNdl1JoRTcmzIaOJ7QrHxQrS\nGpivvTm6kQ9WLeApG1GLYJ3C3Wl4bnsI1bKSv55Zi45/JawHzTzYUAIXX9qCd3Io\nHzDucz9IAj9Ookw0va/q9FjoPGrRB80IReVxLVnbo6pYJfu/O37jvEobHFa8ckHd\nYxUIg8wvkIOy1O3M74lBDm6CVI0ZO25xPlDB/4nHAE1PbA3aF3lw8JGuxLDsetxm\nfzgAleVt4vXLQiCrZaLf+0cM97JcT7wdHcbIvRLsij9LNP+2tWZgeZ/hIAOEdaDq\ncYANPDIAxfTvbe9I0sXrCtrLer1SS7GqUmdFCdkdun8erXdNF0ls9Rp4cbYhjdf3\nyMxdI/24LUOOQ71cHW3ITIDImm6I8KmrXFM2NewTARKfAgMBAAE=\n-----END PUBLIC KEY-----",
  1257  				},
  1258  				Scheme: "rsassa-pss-sha256",
  1259  			},
  1260  			err: ErrNoPEMBlock,
  1261  		},
  1262  		{
  1263  			name: "invalid rsa pub key",
  1264  			key: Key{
  1265  				KeyID:               "bad",
  1266  				KeyIDHashAlgorithms: []string{"sha256"},
  1267  				KeyType:             "rsa",
  1268  				KeyVal: KeyVal{
  1269  					Private: "",
  1270  					Public:  "invalid",
  1271  				},
  1272  				Scheme: "rsassa-pss-sha256",
  1273  			},
  1274  			err: ErrNoPEMBlock,
  1275  		},
  1276  		{
  1277  			name: "invalid ed25519 public key",
  1278  			key: Key{
  1279  				KeyID:               "bad",
  1280  				KeyIDHashAlgorithms: []string{"sha256"},
  1281  				KeyType:             "ed25519",
  1282  				KeyVal: KeyVal{
  1283  					Private: "invalid",
  1284  					Public:  "invalid",
  1285  				},
  1286  				Scheme: "ed25519",
  1287  			},
  1288  			err: ErrInvalidHexString,
  1289  		},
  1290  		{
  1291  			name: "invalid ed25519 private key",
  1292  			key: Key{
  1293  				KeyID:               "bad",
  1294  				KeyIDHashAlgorithms: []string{"sha256"},
  1295  				KeyType:             "ed25519",
  1296  				KeyVal: KeyVal{
  1297  					Private: "invalid",
  1298  					Public:  "393e671b200f964c49083d34a867f5d989ec1c69df7b66758fe471c8591b139c",
  1299  				},
  1300  				Scheme: "ed25519",
  1301  			},
  1302  			err: ErrInvalidHexString,
  1303  		},
  1304  		{
  1305  			name: "valid rsa public, but bad private key",
  1306  			key: Key{
  1307  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1308  				KeyIDHashAlgorithms: []string{"sha256"},
  1309  				KeyType:             "rsa",
  1310  				KeyVal: KeyVal{
  1311  					Private: "-----BEGIN PRIVATE KEY-----\nMIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6fQnV71xKx6kFgJv\nYTMq0ytvWi2mDlYu6aNm1761c1OSInbBxBNb0ligpM65KyaeeRce6JR9eQW6TB6R\n+5pNzvOhgYkDgYYABAFy0CeDAyV/2mY1NqxLLgqEXSxaqM3fM8gYn/ZWzrLnO+1h\nK2QAanID3JuPff1NdhehhL/U1prXdyyaItA5X4ChkQHMTsiS/3HkWRuLR8L22SGs\nB+7KqOeO5ELkqHO5tsy4kvsNrmersCGRQGY6A5V/0JFhP1u1JUvAVVhfRbdQXuu3\nrw==\n-----END PRIVATE KEY-----\n",
  1312  					Public:  "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCTik98953hKl6+B6n5l\n8DVIDwDnvrJfpasbJ3+Rw66YcawOZinRpMxPTqWBKs7sRop7jqsQNcslUoIZLrXP\nr3foPHF455TlrqPVfCZiFQ+O4CafxWOB4mL1NddvpFXTEjmUiwFrrL7PcvQKMbYz\neUHH4tH9MNzqKWbbJoekBsDpCDIxp1NbgivGBKwjRGa281sClKgpd0Q0ebl+RTcT\nvpfZVDbXazQ7VqZkidt7geWq2BidOXZp/cjoXyVneKx/gYiOUv8x94svQMzSEhw2\nLFMQ04A1KnGn1jxO35/fd6/OW32njyWs96RKu9UQVacYHsQfsACPWwmVqgnX/sp5\nujlvSDjyfZu7c5yUQ2asYfQPLvnjG+u7QcBukGf8hAfVgsezzX9QPiK35BKDgBU/\nVk43riJs165TJGYGVuLUhIEhHgiQtwo8pUTJS5npEe5XMDuZoighNdzoWY2nfsBf\np8348k6vJtDMB093/t6V9sTGYQcSbgKPyEQo5Pk6Wd4ZAgMBAAE=\n-----END PUBLIC KEY-----",
  1313  				},
  1314  				Scheme: "rsassa-pss-sha256",
  1315  			},
  1316  			err: ErrKeyKeyTypeMismatch,
  1317  		},
  1318  		{
  1319  			name: "valid ecdsa public key, but invalid ecdsa private key",
  1320  			key: Key{
  1321  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1322  				KeyIDHashAlgorithms: []string{"sha256"},
  1323  				KeyType:             "ecdsa",
  1324  				KeyVal: KeyVal{
  1325  					Private: "-----BEGIN RSA PRIVATE KEY-----\nMIIG5QIBAAKCAYEAyCTik98953hKl6+B6n5l8DVIDwDnvrJfpasbJ3+Rw66YcawO\nZinRpMxPTqWBKs7sRop7jqsQNcslUoIZLrXPr3foPHF455TlrqPVfCZiFQ+O4Caf\nxWOB4mL1NddvpFXTEjmUiwFrrL7PcvQKMbYzeUHH4tH9MNzqKWbbJoekBsDpCDIx\np1NbgivGBKwjRGa281sClKgpd0Q0ebl+RTcTvpfZVDbXazQ7VqZkidt7geWq2Bid\nOXZp/cjoXyVneKx/gYiOUv8x94svQMzSEhw2LFMQ04A1KnGn1jxO35/fd6/OW32n\njyWs96RKu9UQVacYHsQfsACPWwmVqgnX/sp5ujlvSDjyfZu7c5yUQ2asYfQPLvnj\nG+u7QcBukGf8hAfVgsezzX9QPiK35BKDgBU/Vk43riJs165TJGYGVuLUhIEhHgiQ\ntwo8pUTJS5npEe5XMDuZoighNdzoWY2nfsBfp8348k6vJtDMB093/t6V9sTGYQcS\nbgKPyEQo5Pk6Wd4ZAgMBAAECggGBAIb8YZiMA2tfNSfy5jNqhoQo223LFYIHOf05\nVvofzwbkdcqM2bVL1SpJ5d9MPr7Jio/VDJpfg3JUjdqFBkj7tJRK0eYaPgoq4XIU\n64JtPM+pi5pgUnfFsi8mwO1MXO7AN7hd/3J1RdLfanjEYS/ADB1nIVI4gIR5KrE7\nvujQqO8pIsI1YEnTLa+wqEA0fSDACfo90pLCjBz1clL6qVAzYmy0a46h4k5ajv7V\nAI/96OHmLYDLsRa1Z60T2K17Q7se0zmHSjfssLQ+d+0zdU5BK8wFn1n2DvCc310T\na0ip+V+YNT0FBtmknTobnr9S688bR8vfBK0q0JsZ1YataGyYS0Rp0RYeEInjKie8\nDIzGuYNRzEjrYMlIOCCY5ybo9mbRiQEQvlSunFAAoKyr8svwU8/e2HV4lXxqDY9v\nKZzxeNYVvX2ZUP3D/uz74VvUWe5fz+ZYmmHVW0erbQC8Cxv2Q6SG/eylcfiNDdLG\narf+HNxcvlJ3v7I2w79tqSbHPcJc1QKBwQD6E/zRYiuJCd0ydnJXPCzZ3dhs/Nz0\ny9QJXg7QyLuHPGEV6r2nIK/Ku3d0NHi/hWglCrg2m8ik7BKaIUjvwVI7M/E3gcZu\ngknmlWjt5QY+LLfQdVgBeqwJdqLHXtw2GAJch6LGSxIcZ5F+1MmqUbfElUJ4h/To\nno6CFGfmAc2n6+PSMWxHT6Oe/rrAFQ2B25Kl9kIrfAUeWhtLm+n0ARXo7wKr63rg\nyJBXwr5Rl3U1NJGnuagQqcS7zDdZ2Glaj1cCgcEAzOIwl5Z0I42vU+2z9e+23Tyc\nHnSyp7AaHLJeuv92T8j7sF8qV1brYQqqzUAGpIGR6OZ9Vj2niPdbtdAQpgcTav+9\nBY9Nyk6YDgsTuN+bQEWsM8VfMUFVUXQAdNFJT6VPO877Fi0PnWhqxVVzr7GuUJFM\nzTUSscsqT40Ht2v1v+qYM4EziPUtUlxUbfuc0RwtfbSpALJG+rpPjvdddQ4Xsdj0\nEIoq1r/0v+vo0Dbpdy63N0iYh9r9yHioiUdCPUgPAoHBAJhKL7260NRFQ4UFiKAD\nLzUF2lSUsGIK9nc15kPS2hCC/oSATTpHt4X4H8iOY7IOJdvY6VGoEMoOUU23U1le\nGxueiBjLWPHXOfXHqvykaebXCKFTtGJCOB4TNxG+fNAcUuPSXZfwA3l0wK/CGYU0\n+nomgzIvaT93v0UL9DGni3vlNPm9yziqEPQ0H7n1mCIqeuXCT413mw5exRyIODK1\nrogJdVEIt+3Hdc9b8tZxK5lZCBJiBy0OlZXfyR1XouDZRQKBwC1++N1gio+ukcVo\nXnL5dTjxkZVtwpJcF6BRt5l8yu/yqHlE2KkmYwRckwsa8Z6sKxN1w1VYQZC3pQTd\nnCTSI2y6N2Y5qUOIalmL+igud1IxZojkhjvwzxpUURmfs9Dc25hjYPxOq03/9t21\nGQhlw1ieu1hCNdGHVPDvV0xSy/J/DKc7RI9gKl1EpXb6zZrdz/g/GtxNuldI8gvE\nQFuS8o4KqD/X/qVLYPURVNSPrQ5LMGI1W7GnXn2a1YoOadYj3wKBwQCh+crvbhDr\njb2ud3CJfdCs5sS5SEKADiUcxiJPcypxhmu+7vhG1Nr6mT0SAYWaA36GDJkU7/Oo\nvoal+uigbOt/UugS1nQYnEzDRkTidQMm1gXVNcWRTBFTKwRP/Gd6yOp9BUHJlFCu\nM2q8HYFtmSqOele6xFOAUnHhwVx4QURJYa+S5A603Jm6ETv0+Y6xdHX/02vA+pRt\nlQqaoEO7ScdRrzjgvVxXkEY3nwLcWdM61/RZTL0+be8goDw5cWt+PaA=\n-----END RSA PRIVATE KEY-----",
  1326  					Public:  "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBctAngwMlf9pmNTasSy4KhF0sWqjN\n3zPIGJ/2Vs6y5zvtYStkAGpyA9ybj339TXYXoYS/1Naa13csmiLQOV+AoZEBzE7I\nkv9x5Fkbi0fC9tkhrAfuyqjnjuRC5KhzubbMuJL7Da5nq7AhkUBmOgOVf9CRYT9b\ntSVLwFVYX0W3UF7rt68=\n-----END PUBLIC KEY-----\n",
  1327  				},
  1328  				Scheme: "ecdsa",
  1329  			},
  1330  			err: ErrKeyKeyTypeMismatch,
  1331  		},
  1332  		{
  1333  			name: "rsa key, but with ed25519 private key",
  1334  			key: Key{
  1335  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1336  				KeyIDHashAlgorithms: []string{"sha256"},
  1337  				KeyType:             "rsa",
  1338  				KeyVal: KeyVal{
  1339  					Private: "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEICmtWWk/6UydYjr7tmVUtPa7JIxHdhaJraSHXr2pSECu\n-----END PRIVATE KEY-----\n",
  1340  					Public:  "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCTik98953hKl6+B6n5l\n8DVIDwDnvrJfpasbJ3+Rw66YcawOZinRpMxPTqWBKs7sRop7jqsQNcslUoIZLrXP\nr3foPHF455TlrqPVfCZiFQ+O4CafxWOB4mL1NddvpFXTEjmUiwFrrL7PcvQKMbYz\neUHH4tH9MNzqKWbbJoekBsDpCDIxp1NbgivGBKwjRGa281sClKgpd0Q0ebl+RTcT\nvpfZVDbXazQ7VqZkidt7geWq2BidOXZp/cjoXyVneKx/gYiOUv8x94svQMzSEhw2\nLFMQ04A1KnGn1jxO35/fd6/OW32njyWs96RKu9UQVacYHsQfsACPWwmVqgnX/sp5\nujlvSDjyfZu7c5yUQ2asYfQPLvnjG+u7QcBukGf8hAfVgsezzX9QPiK35BKDgBU/\nVk43riJs165TJGYGVuLUhIEhHgiQtwo8pUTJS5npEe5XMDuZoighNdzoWY2nfsBf\np8348k6vJtDMB093/t6V9sTGYQcSbgKPyEQo5Pk6Wd4ZAgMBAAE=\n-----END PUBLIC KEY-----",
  1341  				},
  1342  				Scheme: "rsassa-pss-sha256",
  1343  			},
  1344  			err: ErrInvalidKey,
  1345  		},
  1346  		{
  1347  			name: "unsupported key type",
  1348  			key: Key{
  1349  				KeyID:               "",
  1350  				KeyIDHashAlgorithms: nil,
  1351  				KeyType:             "invalid",
  1352  				KeyVal:              KeyVal{},
  1353  				Scheme:              "",
  1354  			},
  1355  			err: ErrUnsupportedKeyType,
  1356  		},
  1357  		{
  1358  			name: "rsa key type, but ed25519 key",
  1359  			key: Key{
  1360  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1361  				KeyIDHashAlgorithms: []string{"sha256"},
  1362  				KeyType:             "rsa",
  1363  				KeyVal: KeyVal{
  1364  					Private: "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEICmtWWk/6UydYjr7tmVUtPa7JIxHdhaJraSHXr2pSECu\n-----END PRIVATE KEY-----\n",
  1365  					Public:  "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAOT5nGyAPlkxJCD00qGf12YnsHGnfe2Z1j+RxyFkbE5w=\n-----END PUBLIC KEY-----\n",
  1366  				},
  1367  				Scheme: "rsassa-pss-sha256",
  1368  			},
  1369  			err: ErrInvalidKey,
  1370  		},
  1371  		{
  1372  			name: "rsa key, but not ecdsa key type",
  1373  			key: Key{
  1374  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1375  				KeyIDHashAlgorithms: []string{"sha256"},
  1376  				KeyType:             "ecdsa",
  1377  				KeyVal: KeyVal{
  1378  					Private: "",
  1379  					Public:  "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyCTik98953hKl6+B6n5l\n8DVIDwDnvrJfpasbJ3+Rw66YcawOZinRpMxPTqWBKs7sRop7jqsQNcslUoIZLrXP\nr3foPHF455TlrqPVfCZiFQ+O4CafxWOB4mL1NddvpFXTEjmUiwFrrL7PcvQKMbYz\neUHH4tH9MNzqKWbbJoekBsDpCDIxp1NbgivGBKwjRGa281sClKgpd0Q0ebl+RTcT\nvpfZVDbXazQ7VqZkidt7geWq2BidOXZp/cjoXyVneKx/gYiOUv8x94svQMzSEhw2\nLFMQ04A1KnGn1jxO35/fd6/OW32njyWs96RKu9UQVacYHsQfsACPWwmVqgnX/sp5\nujlvSDjyfZu7c5yUQ2asYfQPLvnjG+u7QcBukGf8hAfVgsezzX9QPiK35BKDgBU/\nVk43riJs165TJGYGVuLUhIEhHgiQtwo8pUTJS5npEe5XMDuZoighNdzoWY2nfsBf\np8348k6vJtDMB093/t6V9sTGYQcSbgKPyEQo5Pk6Wd4ZAgMBAAE=\n-----END PUBLIC KEY-----",
  1380  				},
  1381  				Scheme: "ecdsa",
  1382  			},
  1383  			err: ErrKeyKeyTypeMismatch,
  1384  		},
  1385  		{
  1386  			name: "ecdsa key, but rsa key type",
  1387  			key: Key{
  1388  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1389  				KeyIDHashAlgorithms: []string{"sha256"},
  1390  				KeyType:             "rsa",
  1391  				KeyVal: KeyVal{
  1392  					Private: "-----BEGIN PRIVATE KEY-----\nMIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6fQnV71xKx6kFgJv\nYTMq0ytvWi2mDlYu6aNm1761c1OSInbBxBNb0ligpM65KyaeeRce6JR9eQW6TB6R\n+5pNzvOhgYkDgYYABAFy0CeDAyV/2mY1NqxLLgqEXSxaqM3fM8gYn/ZWzrLnO+1h\nK2QAanID3JuPff1NdhehhL/U1prXdyyaItA5X4ChkQHMTsiS/3HkWRuLR8L22SGs\nB+7KqOeO5ELkqHO5tsy4kvsNrmersCGRQGY6A5V/0JFhP1u1JUvAVVhfRbdQXuu3\nrw==\n-----END PRIVATE KEY-----\n",
  1393  					Public:  "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBctAngwMlf9pmNTasSy4KhF0sWqjN\n3zPIGJ/2Vs6y5zvtYStkAGpyA9ybj339TXYXoYS/1Naa13csmiLQOV+AoZEBzE7I\nkv9x5Fkbi0fC9tkhrAfuyqjnjuRC5KhzubbMuJL7Da5nq7AhkUBmOgOVf9CRYT9b\ntSVLwFVYX0W3UF7rt68=\n-----END PUBLIC KEY-----\n",
  1394  				},
  1395  				Scheme: "rsassa-pss-sha256",
  1396  			},
  1397  			err: ErrKeyKeyTypeMismatch,
  1398  		},
  1399  		{
  1400  			name: "ecdsa key, but rsa key type",
  1401  			key: Key{
  1402  				KeyID:               "b7d643dec0a051096ee5d87221b5d91a33daa658699d30903e1cefb90c418401",
  1403  				KeyIDHashAlgorithms: []string{"sha256"},
  1404  				KeyType:             "ecdsa",
  1405  				KeyVal: KeyVal{
  1406  					Private: "-----BEGIN PRIVATE KEY-----\nMIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIB6fQnV71xKx6kFgJv\nYTMq0ytvWi2mDlYu6aNm1761c1OSInbBxBNb0ligpM65KyaeeRce6JR9eQW6TB6R\n+5pNzvOhgYkDgYYABAFy0CeDAyV/2mY1NqxLLgqEXSxaqM3fM8gYn/ZWzrLnO+1h\nK2QAanID3JuPff1NdhehhL/U1prXdyyaItA5X4ChkQHMTsiS/3HkWRuLR8L22SGs\nB+7KqOeO5ELkqHO5tsy4kvsNrmersCGRQGY6A5V/0JFhP1u1JUvAVVhfRbdQXuu3\nrw==\n-----END PRIVATE KEY-----\n",
  1407  					Public:  "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBctAngwMlf9pmNTasSy4KhF0sWqjN\n3zPIGJ/2Vs6y5zvtYStkAGpyA9ybj339TXYXoYS/1Naa13csmiLQOV+AoZEBzE7I\nkv9x5Fkbi0fC9tkhrAfuyqjnjuRC5KhzubbMuJL7Da5nq7AhkUBmOgOVf9CRYT9b\ntSVLwFVYX0W3UF7rt68=\n-----END PUBLIC KEY-----\n",
  1408  				},
  1409  				Scheme: "ecdsa",
  1410  			},
  1411  			err: nil,
  1412  		},
  1413  	}
  1414  	for _, table := range tables {
  1415  		err := validateKeyVal(table.key)
  1416  		if !errors.Is(err, table.err) {
  1417  			t.Errorf("test '%s' failed, expected error: '%s', got '%s'", table.name, table.err, err)
  1418  		}
  1419  	}
  1420  }
  1421  
  1422  func TestMatchKeyTypeScheme(t *testing.T) {
  1423  	tables := []struct {
  1424  		name string
  1425  		key  Key
  1426  		err  error
  1427  	}{
  1428  		{name: "test for unsupported key type",
  1429  			key: Key{
  1430  				KeyID:               "",
  1431  				KeyIDHashAlgorithms: nil,
  1432  				KeyType:             "invalid",
  1433  				KeyVal:              KeyVal{},
  1434  				Scheme:              "",
  1435  			},
  1436  			err: ErrUnsupportedKeyType,
  1437  		},
  1438  		{
  1439  			name: "test for scheme key type mismatch",
  1440  			key: Key{
  1441  				KeyID:               "",
  1442  				KeyIDHashAlgorithms: nil,
  1443  				KeyType:             "rsa",
  1444  				KeyVal:              KeyVal{},
  1445  				Scheme:              "ed25519",
  1446  			},
  1447  			err: ErrSchemeKeyTypeMismatch,
  1448  		},
  1449  	}
  1450  	for _, table := range tables {
  1451  		err := matchKeyTypeScheme(table.key)
  1452  		if !errors.Is(err, table.err) {
  1453  			t.Errorf("%s returned wrong error. We got: %s, we should have got: %s", table.name, err, table.err)
  1454  		}
  1455  	}
  1456  }
  1457  
  1458  func TestValidatePublicKey(t *testing.T) {
  1459  	validTables := []struct {
  1460  		name string
  1461  		key  Key
  1462  	}{
  1463  		{
  1464  			name: "test with valid key",
  1465  			key: Key{
  1466  				KeyID:               "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
  1467  				KeyIDHashAlgorithms: []string{"sha512"},
  1468  				KeyType:             "ed25519",
  1469  				KeyVal: KeyVal{
  1470  					Private: "",
  1471  					Public:  "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAOT5nGyAPlkxJCD00qGf12YnsHGnfe2Z1j+RxyFkbE5w=\n-----END PUBLIC KEY-----\n",
  1472  				},
  1473  				Scheme: "ed25519",
  1474  			},
  1475  		},
  1476  	}
  1477  	for _, table := range validTables {
  1478  		err := validatePublicKey(table.key)
  1479  		if err != nil {
  1480  			t.Errorf("%s returned error %s, instead of nil", table.name, err)
  1481  		}
  1482  	}
  1483  
  1484  	invalidTables := []struct {
  1485  		name string
  1486  		key  Key
  1487  		err  error
  1488  	}{
  1489  		{
  1490  			name: "test with valid key",
  1491  			key: Key{
  1492  				KeyID:               "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
  1493  				KeyIDHashAlgorithms: []string{"sha512"},
  1494  				KeyType:             "ed25519",
  1495  				KeyVal: KeyVal{
  1496  					Private: "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEICmtWWk/6UydYjr7tmVUtPa7JIxHdhaJraSHXr2pSECu\n-----END PRIVATE KEY-----\n",
  1497  					Public:  "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAOT5nGyAPlkxJCD00qGf12YnsHGnfe2Z1j+RxyFkbE5w=\n-----END PUBLIC KEY-----\n",
  1498  				},
  1499  				Scheme: "ed25519",
  1500  			},
  1501  			err: ErrNoPublicKey,
  1502  		},
  1503  	}
  1504  	for _, table := range invalidTables {
  1505  		err := validatePublicKey(table.key)
  1506  		if err != table.err {
  1507  			t.Errorf("%s returned unexpected error %s, we should got: %s", table.name, err, table.err)
  1508  		}
  1509  	}
  1510  }
  1511  
  1512  func TestSignatureGetCertificate(t *testing.T) {
  1513  	sig := Signature{}
  1514  	_, err := sig.GetCertificate()
  1515  	assert.NotNil(t, err, "expected empty signature error")
  1516  
  1517  	certTemplate := &x509.Certificate{
  1518  		Subject: pkix.Name{
  1519  			CommonName:   "step1.example.com",
  1520  			Organization: []string{"example"},
  1521  		},
  1522  	}
  1523  
  1524  	cert, _, _, err := createTestCert(certTemplate, x509.Ed25519, time.Hour)
  1525  	assert.Nil(t, err, "unexpected error when creating test certificate")
  1526  	sig.Certificate = string(generatePEMBlock(cert.Raw, "CERTIFICATE"))
  1527  	_, err = sig.GetCertificate()
  1528  	assert.Nil(t, err, "unexpected error getting certificate from signature")
  1529  }
  1530  
  1531  func TestStepCheckCertConstraints(t *testing.T) {
  1532  	step := Step{}
  1533  	key := Key{}
  1534  	rootPool := x509.NewCertPool()
  1535  	intermediatePool := x509.NewCertPool()
  1536  	// Test failure if the step has no constraints
  1537  	err := step.CheckCertConstraints(key, []string{}, rootPool, intermediatePool)
  1538  	assert.NotNil(t, err, "expected error")
  1539  
  1540  	certTemplate := &x509.Certificate{
  1541  		Subject: pkix.Name{
  1542  			CommonName:   "step1.example.com",
  1543  			Organization: []string{"example"},
  1544  		},
  1545  	}
  1546  
  1547  	leaf, intermediate, root, err := createTestCert(certTemplate, x509.Ed25519, time.Hour)
  1548  	assert.Nil(t, err, "unexpected error creating test certificates")
  1549  	rootPool.AddCert(root)
  1550  	intermediatePool.AddCert(intermediate)
  1551  	step.CertificateConstraints = []CertificateConstraint{
  1552  		{
  1553  			CommonName:    certTemplate.Subject.CommonName,
  1554  			Organizations: certTemplate.Subject.Organization,
  1555  			Emails:        certTemplate.EmailAddresses,
  1556  			DNSNames:      certTemplate.DNSNames,
  1557  			URIs:          []string{},
  1558  			Roots:         []string{"*"},
  1559  		},
  1560  	}
  1561  
  1562  	err = key.LoadKeyReaderDefaults(bytes.NewReader(generatePEMBlock(leaf.Raw, "CERTIFICATE")))
  1563  	rootCAIDs := []string{key.KeyID}
  1564  	assert.Nil(t, err, "unexpected error when loading Key")
  1565  
  1566  	// Test to ensure we fail if the key has no certificate
  1567  	err = step.CheckCertConstraints(Key{}, rootCAIDs, rootPool, intermediatePool)
  1568  	assert.NotNil(t, err, "expected error when using key with no certificate")
  1569  
  1570  	// Test to ensure our test constraint passes
  1571  	err = step.CheckCertConstraints(key, rootCAIDs, rootPool, intermediatePool)
  1572  	assert.Nil(t, err, "unexpected error when checking constraints")
  1573  
  1574  	// Test to ensure we get an error when our certificate doesn't match a constraint
  1575  	step.CertificateConstraints[0].CommonName = "bad common name"
  1576  	err = step.CheckCertConstraints(key, rootCAIDs, rootPool, intermediatePool)
  1577  	assert.NotNil(t, err, "expected error when checking constraint without match")
  1578  }
  1579  
  1580  func TestRootCAIDs(t *testing.T) {
  1581  	layout := Layout{
  1582  		RootCas: map[string]Key{
  1583  			"123123": {},
  1584  			"456456": {},
  1585  		},
  1586  	}
  1587  
  1588  	expectedCAIDs := []string{"123123", "456456"}
  1589  	rootCAIDs := layout.RootCAIDs()
  1590  	assert.ElementsMatch(t, expectedCAIDs, rootCAIDs, "expected root ca ids don't match")
  1591  }
  1592  
  1593  func TestLoadMetadata(t *testing.T) {
  1594  	// Create a bunch of tmp json files with invalid format and test load errors:
  1595  	// - invalid json
  1596  	// - missing signatures and signed field
  1597  	// - invalid signatures field
  1598  	// - invalid signed field
  1599  	// - invalid signed type
  1600  	// - invalid signed field for type link
  1601  	// - invalid signed field for type layout
  1602  	invalidJSONBytes := [][]byte{
  1603  		[]byte("{"),
  1604  		[]byte("{}"),
  1605  		[]byte(`{"signatures": null, "signed": {}}`),
  1606  		[]byte(`{"signatures": "string", "signed": {}}`),
  1607  		[]byte(`{"signatures": [], "signed": []}`),
  1608  		[]byte(`{"signatures": [], "signed": {"_type": "something else"}}`),
  1609  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1610  			"materials": "invalid", "name": "some name", "products": "invalid",
  1611  			"byproducts": "invalid", "command": "some command",
  1612  			"environment": "some list"}}`),
  1613  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
  1614  			"steps": "invalid", "inspect": "invalid", "readme": "some readme",
  1615  			"keys": "some keys", "expires": "some date", "rootcas": [], "intermediatecas": []}}`),
  1616  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
  1617  			"inspect": "invalid", "readme": "some readme", "keys": "some keys",
  1618  			"expires": "some date", "rootcas": [], "intermediatecas": []}}`),
  1619  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
  1620  			"steps": "invalid", "readme": "some readme", "keys": "some keys",
  1621  			"expires": "some date", "rootcas": [], "intermediatecas": []}}`),
  1622  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
  1623  			"steps": "invalid", "inspect": "invalid", "readme": "some readme",
  1624  			"expires": "some date", "rootcas": [], "intermediatecas": []}}`),
  1625  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
  1626  			"steps": "invalid", "inspect": "invalid", "readme": "some readme",
  1627  			"keys": "some keys", "rootcas": [], "intermediatecas": []}}`),
  1628  		[]byte(`{"signatures": [], "signed": {"_type": "layout",
  1629  			"steps": "invalid", "inspect": "invalid",
  1630  			"keys": "some keys", "expires": "some date", "rootcas": [], "intermediatecas": []}}`),
  1631  		[]byte(`{"signatures": [], "signed": {"_type": "layout", "steps": [],
  1632  			"inspect": [], "readme": "some readme", "keys": {},
  1633  			"expires": "some date", "foo": "bar", "rootcas": [], "intermediatecas": []}}`),
  1634  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1635  			"materials": "invalid", "products": "invalid",
  1636  			"byproducts": "invalid", "command": "some command",
  1637  			"environment": "some list"}}`),
  1638  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1639  			"name": "some name", "products": "invalid",
  1640  			"byproducts": "invalid", "command": "some command",
  1641  			"environment": "some list"}}`),
  1642  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1643  			"materials": "invalid", "name": "some name",
  1644  			"byproducts": "invalid", "command": "some command",
  1645  			"environment": "some list"}}`),
  1646  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1647  			"materials": "invalid", "name": "some name", "products": "invalid",
  1648  			"command": "some command",
  1649  			"environment": "some list"}}`),
  1650  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1651  			"materials": "invalid", "name": "some name", "products": "invalid",
  1652  			"byproducts": "invalid", "environment": "some list"}}`),
  1653  		[]byte(`{"signatures": [], "signed": {"_type": "link",
  1654  			"materials": "invalid", "name": "some name", "products": "invalid",
  1655  			"byproducts": "invalid", "command": "some command"}}`),
  1656  		[]byte(`{"signatures": [], "signed": {"_type": "link", "materials": {},
  1657  			"name": "some name", "products": {}, "byproducts": {},
  1658  			"command": [], "environment": {}, "foo": "bar"}}`),
  1659  		[]byte(`{"payloadType": "invalid", "payload": "eyJfdHlwZSI6ICJsaW5rIiwgIm1hdGVyaWFscyI6ICJpbnZhbGlkIiwgIm5hbWUiOiAic29tZSBuYW1lIiwgInByb2R1Y3RzIjogImludmFsaWQiLCAiYnlwcm9kdWN0cyI6ICJpbnZhbGlkIiwgImNvbW1hbmQiOiAic29tZSBjb21tYW5kIn0=", "signatures": []}`),
  1660  		[]byte(`{"payloadType": "application/vnd.in-toto+json", "payload": "eyJfdHlwZSI6ICJsaW5rIiwgIm1hdGVyaWFscyI6ICJpbnZhbGlkIiwgIm5hbWUiOiAic29tZSBuYW1lIiwgInByb2R1Y3RzIjogImludmFsaWQiLCAiYnlwcm9kdWN0cyI6ICJpbnZhbGlkIiwgImNvbW1hbmQiOiAic29tZSBjb21tYW5kIn0="}`),
  1661  		[]byte(`{"payloadType": "application/vnd.in-toto+json", "signatures": []}`),
  1662  	}
  1663  
  1664  	expectedErrors := []string{
  1665  		"unexpected end",
  1666  		"requires 'signed' and 'signatures' parts",
  1667  		"requires 'signed' and 'signatures' parts",
  1668  		"cannot unmarshal string into Go value of type []in_toto.Signature",
  1669  		"cannot unmarshal array into Go value of type map[string]interface {}",
  1670  		ErrUnknownMetadataType.Error(),
  1671  		"cannot unmarshal string into Go struct field Link.materials",
  1672  		"cannot unmarshal string into Go struct field Layout.steps",
  1673  		"required field steps missing",
  1674  		"required field inspect missing",
  1675  		"required field keys missing",
  1676  		"required field expires missing",
  1677  		"required field readme missing",
  1678  		"json: unknown field \"foo\"",
  1679  		"required field name missing",
  1680  		"required field materials missing",
  1681  		"required field products missing",
  1682  		"required field byproducts missing",
  1683  		"required field command missing",
  1684  		"required field environment missing",
  1685  		"json: unknown field \"foo\"",
  1686  		ErrInvalidPayloadType.Error(),
  1687  		"in-toto metadata envelope requires 'payload' and 'signatures' parts",
  1688  		"in-toto metadata envelope requires 'payload' and 'signatures' parts",
  1689  	}
  1690  
  1691  	for i := 0; i < len(invalidJSONBytes); i++ {
  1692  		fn := fmt.Sprintf("invalid-metadata-%v.tmp", i)
  1693  		if err := os.WriteFile(fn, invalidJSONBytes[i], 0644); err != nil {
  1694  			fmt.Printf("Could not write file: %s", err)
  1695  		}
  1696  		_, err := LoadMetadata(fn)
  1697  		if err == nil || !strings.Contains(err.Error(), expectedErrors[i]) {
  1698  			t.Log(err)
  1699  			t.Errorf("LoadMetadata returned '%s', expected '%s' error", err,
  1700  				expectedErrors[i])
  1701  		}
  1702  		if err := os.Remove(fn); err != nil {
  1703  			t.Errorf("unable to remove directory %s: %s", fn, err)
  1704  		}
  1705  	}
  1706  }