github.com/boxboat/in-toto-golang@v0.0.3-0.20210303203820-2fa16ecbe6f6/in_toto/model.go (about)

     1  package in_toto
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/rsa"
     6  	"crypto/x509"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"reflect"
    13  	"regexp"
    14  	"strconv"
    15  	"strings"
    16  	"time"
    17  )
    18  
    19  /*
    20  KeyVal contains the actual values of a key, as opposed to key metadata such as
    21  a key identifier or key type.  For RSA keys, the key value is a pair of public
    22  and private keys in PEM format stored as strings.  For public keys the Private
    23  field may be an empty string.
    24  */
    25  type KeyVal struct {
    26  	Private     string `json:"private"`
    27  	Public      string `json:"public"`
    28  	Certificate string `json:"-"` // We just use this for signing, no need to store it
    29  }
    30  
    31  /*
    32  Key represents a generic in-toto key that contains key metadata, such as an
    33  identifier, supported hash algorithms to create the identifier, the key type
    34  and the supported signature scheme, and the actual key value.
    35  */
    36  type Key struct {
    37  	KeyID               string   `json:"keyid"`
    38  	KeyIDHashAlgorithms []string `json:"keyid_hash_algorithms"`
    39  	KeyType             string   `json:"keytype"`
    40  	KeyVal              KeyVal   `json:"keyval"`
    41  	Scheme              string   `json:"scheme"`
    42  }
    43  
    44  // ErrEmptyKeyField will be thrown if a field in our Key struct is empty.
    45  var ErrEmptyKeyField = errors.New("empty field in key")
    46  
    47  // ErrInvalidHexString will be thrown, if a string doesn't match a hex string.
    48  var ErrInvalidHexString = errors.New("invalid hex string")
    49  
    50  // ErrSchemeKeyTypeMismatch will be thrown, if the given scheme and key type are not supported together.
    51  var ErrSchemeKeyTypeMismatch = errors.New("the scheme and key type are not supported together")
    52  
    53  // ErrUnsupportedKeyIDHashAlgorithms will be thrown, if the specified KeyIDHashAlgorithms is not supported.
    54  var ErrUnsupportedKeyIDHashAlgorithms = errors.New("the given keyID hash algorithm is not supported")
    55  
    56  // ErrKeyKeyTypeMismatch will be thrown, if the specified keyType does not match the key
    57  var ErrKeyKeyTypeMismatch = errors.New("the given key does not match its key type")
    58  
    59  // ErrNoPublicKey gets returned when the private key value is not empty.
    60  var ErrNoPublicKey = errors.New("the given key is not a public key")
    61  
    62  // ErrCurveSizeSchemeMismatch gets returned, when the scheme and curve size are incompatible
    63  // for example: curve size = "521" and scheme = "ecdsa-sha2-nistp224"
    64  var ErrCurveSizeSchemeMismatch = errors.New("the scheme does not match the curve size")
    65  
    66  /*
    67  matchEcdsaScheme checks if the scheme suffix, matches the ecdsa key
    68  curve size. We do not need a full regex match here, because
    69  our validateKey functions are already checking for a valid scheme string.
    70  */
    71  func matchEcdsaScheme(curveSize int, scheme string) error {
    72  	if !strings.HasSuffix(scheme, strconv.Itoa(curveSize)) {
    73  		return ErrCurveSizeSchemeMismatch
    74  	}
    75  	return nil
    76  }
    77  
    78  /*
    79  validateHexString is used to validate that a string passed to it contains
    80  only valid hexadecimal characters.
    81  */
    82  func validateHexString(str string) error {
    83  	formatCheck, _ := regexp.MatchString("^[a-fA-F0-9]+$", str)
    84  	if !formatCheck {
    85  		return fmt.Errorf("%w: %s", ErrInvalidHexString, str)
    86  	}
    87  	return nil
    88  }
    89  
    90  /*
    91  validateKeyVal validates the KeyVal struct. In case of an ed25519 key,
    92  it will check for a hex string for private and public key. In any other
    93  case, validateKeyVal will try to decode the PEM block. If this succeeds,
    94  we have a valid PEM block in our KeyVal struct. On success it will return nil
    95  on failure it will return the corresponding error. This can be either
    96  an ErrInvalidHexString, an ErrNoPEMBlock or an ErrUnsupportedKeyType
    97  if the KeyType is unknown.
    98  */
    99  func validateKeyVal(key Key) error {
   100  	switch key.KeyType {
   101  	case ed25519KeyType:
   102  		// We cannot use matchPublicKeyKeyType or matchPrivateKeyKeyType here,
   103  		// because we retrieve the key not from PEM. Hence we are dealing with
   104  		// plain ed25519 key bytes. These bytes can't be typechecked like in the
   105  		// matchKeyKeytype functions.
   106  		err := validateHexString(key.KeyVal.Public)
   107  		if err != nil {
   108  			return err
   109  		}
   110  		if key.KeyVal.Private != "" {
   111  			err := validateHexString(key.KeyVal.Private)
   112  			if err != nil {
   113  				return err
   114  			}
   115  		}
   116  	case rsaKeyType, ecdsaKeyType:
   117  		// We do not need the pemData here, so we can throw it away via '_'
   118  		_, parsedKey, err := decodeAndParse([]byte(key.KeyVal.Public))
   119  		if err != nil {
   120  			return err
   121  		}
   122  		err = matchPublicKeyKeyType(parsedKey, key.KeyType)
   123  		if err != nil {
   124  			return err
   125  		}
   126  		if key.KeyVal.Private != "" {
   127  			// We do not need the pemData here, so we can throw it away via '_'
   128  			_, parsedKey, err := decodeAndParse([]byte(key.KeyVal.Private))
   129  			if err != nil {
   130  				return err
   131  			}
   132  			err = matchPrivateKeyKeyType(parsedKey, key.KeyType)
   133  			if err != nil {
   134  				return err
   135  			}
   136  		}
   137  	default:
   138  		return ErrUnsupportedKeyType
   139  	}
   140  	return nil
   141  }
   142  
   143  /*
   144  matchPublicKeyKeyType validates an interface if it can be asserted to a
   145  the RSA or ECDSA public key type. We can only check RSA and ECDSA this way,
   146  because we are storing them in PEM format. Ed25519 keys are stored as plain
   147  ed25519 keys encoded as hex strings, thus we have no metadata for them.
   148  This function will return nil on success. If the key type does not match
   149  it will return an ErrKeyKeyTypeMismatch.
   150  */
   151  func matchPublicKeyKeyType(key interface{}, keyType string) error {
   152  	switch key.(type) {
   153  	case *rsa.PublicKey:
   154  		if keyType != rsaKeyType {
   155  			return ErrKeyKeyTypeMismatch
   156  		}
   157  	case *ecdsa.PublicKey:
   158  		if keyType != ecdsaKeyType {
   159  			return ErrKeyKeyTypeMismatch
   160  		}
   161  	default:
   162  		return ErrInvalidKey
   163  	}
   164  	return nil
   165  }
   166  
   167  /*
   168  matchPrivateKeyKeyType validates an interface if it can be asserted to a
   169  the RSA or ECDSA private key type. We can only check RSA and ECDSA this way,
   170  because we are storing them in PEM format. Ed25519 keys are stored as plain
   171  ed25519 keys encoded as hex strings, thus we have no metadata for them.
   172  This function will return nil on success. If the key type does not match
   173  it will return an ErrKeyKeyTypeMismatch.
   174  */
   175  func matchPrivateKeyKeyType(key interface{}, keyType string) error {
   176  	// we can only check RSA and ECDSA this way, because we are storing them in PEM
   177  	// format. ed25519 keys are stored as plain ed25519 keys encoded as hex strings
   178  	// so we have no metadata for them.
   179  	switch key.(type) {
   180  	case *rsa.PrivateKey:
   181  		if keyType != rsaKeyType {
   182  			return ErrKeyKeyTypeMismatch
   183  		}
   184  	case *ecdsa.PrivateKey:
   185  		if keyType != ecdsaKeyType {
   186  			return ErrKeyKeyTypeMismatch
   187  		}
   188  	default:
   189  		return ErrInvalidKey
   190  	}
   191  	return nil
   192  }
   193  
   194  /*
   195  matchKeyTypeScheme checks if the specified scheme matches our specified
   196  keyType. If the keyType is not supported it will return an
   197  ErrUnsupportedKeyType. If the keyType and scheme do not match it will return
   198  an ErrSchemeKeyTypeMismatch. If the specified keyType and scheme are
   199  compatible matchKeyTypeScheme will return nil.
   200  */
   201  func matchKeyTypeScheme(key Key) error {
   202  	switch key.KeyType {
   203  	case rsaKeyType:
   204  		for _, scheme := range getSupportedRSASchemes() {
   205  			if key.Scheme == scheme {
   206  				return nil
   207  			}
   208  		}
   209  	case ed25519KeyType:
   210  		for _, scheme := range getSupportedEd25519Schemes() {
   211  			if key.Scheme == scheme {
   212  				return nil
   213  			}
   214  		}
   215  	case ecdsaKeyType:
   216  		for _, scheme := range getSupportedEcdsaSchemes() {
   217  			if key.Scheme == scheme {
   218  				return nil
   219  			}
   220  		}
   221  	default:
   222  		return fmt.Errorf("%w: %s", ErrUnsupportedKeyType, key.KeyType)
   223  	}
   224  	return ErrSchemeKeyTypeMismatch
   225  }
   226  
   227  /*
   228  validateKey checks the outer key object (everything, except the KeyVal struct).
   229  It verifies the keyID for being a hex string and checks for empty fields.
   230  On success it will return nil, on error it will return the corresponding error.
   231  Either: ErrEmptyKeyField or ErrInvalidHexString.
   232  */
   233  func validateKey(key Key) error {
   234  	err := validateHexString(key.KeyID)
   235  	if err != nil {
   236  		return err
   237  	}
   238  	// This probably can be done more elegant with reflection
   239  	// but we care about performance, do we?!
   240  	if key.KeyType == "" {
   241  		return fmt.Errorf("%w: keytype", ErrEmptyKeyField)
   242  	}
   243  	if key.KeyVal.Public == "" {
   244  		return fmt.Errorf("%w: keyval.public", ErrEmptyKeyField)
   245  	}
   246  	if key.Scheme == "" {
   247  		return fmt.Errorf("%w: scheme", ErrEmptyKeyField)
   248  	}
   249  	err = matchKeyTypeScheme(key)
   250  	if err != nil {
   251  		return err
   252  	}
   253  	// only check for supported KeyIDHashAlgorithms, if the variable has been set
   254  	if key.KeyIDHashAlgorithms != nil {
   255  		supportedKeyIDHashAlgorithms := getSupportedKeyIDHashAlgorithms()
   256  		if !supportedKeyIDHashAlgorithms.IsSubSet(NewSet(key.KeyIDHashAlgorithms...)) {
   257  			return fmt.Errorf("%w: %#v, supported are: %#v", ErrUnsupportedKeyIDHashAlgorithms, key.KeyIDHashAlgorithms, getSupportedKeyIDHashAlgorithms())
   258  		}
   259  	}
   260  	return nil
   261  }
   262  
   263  /*
   264  validatePublicKey is a wrapper around validateKey. It test if the private key
   265  value in the key is empty and then validates the key via calling validateKey.
   266  On success it will return nil, on error it will return an ErrNoPublicKey error.
   267  */
   268  func validatePublicKey(key Key) error {
   269  	if key.KeyVal.Private != "" {
   270  		return ErrNoPublicKey
   271  	}
   272  	err := validateKey(key)
   273  	if err != nil {
   274  		return err
   275  	}
   276  	return nil
   277  }
   278  
   279  // TODO: implement this
   280  func validateCertificates(cert []string) error {
   281  	return nil
   282  }
   283  
   284  /*
   285  Signature represents a generic in-toto signature that contains the identifier
   286  of the Key, which was used to create the signature and the signature data.  The
   287  used signature scheme is found in the corresponding Key.
   288  */
   289  type Signature struct {
   290  	KeyID       string `json:"keyid"`
   291  	Sig         string `json:"sig"`
   292  	Certificate string `json:"cert"`
   293  }
   294  
   295  // GetCertificate returns the parsed x509 certificate attached to the signature,
   296  // if it exists.
   297  func (sig Signature) GetCertificate() (Key, error) {
   298  	key := Key{}
   299  	if len(sig.Certificate) == 0 {
   300  		return key, errors.New("Signature has empty Certificate")
   301  	}
   302  
   303  	err := key.LoadKeyReaderDefaults(strings.NewReader(sig.Certificate))
   304  	return key, err
   305  }
   306  
   307  /*
   308  validateSignature is a function used to check if a passed signature is valid,
   309  by inspecting the key ID and the signature itself.
   310  */
   311  func validateSignature(signature Signature) error {
   312  	if err := validateHexString(signature.KeyID); err != nil {
   313  		return err
   314  	}
   315  	if err := validateHexString(signature.Sig); err != nil {
   316  		return err
   317  	}
   318  	return nil
   319  }
   320  
   321  /*
   322  validateSliceOfSignatures is a helper function used to validate multiple
   323  signatures stored in a slice.
   324  */
   325  func validateSliceOfSignatures(slice []Signature) error {
   326  	for _, signature := range slice {
   327  		if err := validateSignature(signature); err != nil {
   328  			return err
   329  		}
   330  	}
   331  	return nil
   332  }
   333  
   334  /*
   335  Link represents the evidence of a supply chain step performed by a functionary.
   336  It should be contained in a generic Metablock object, which provides
   337  functionality for signing and signature verification, and reading from and
   338  writing to disk.
   339  */
   340  type Link struct {
   341  	Type        string                 `json:"_type"`
   342  	Name        string                 `json:"name"`
   343  	Materials   map[string]interface{} `json:"materials"`
   344  	Products    map[string]interface{} `json:"products"`
   345  	ByProducts  map[string]interface{} `json:"byproducts"`
   346  	Command     []string               `json:"command"`
   347  	Environment map[string]interface{} `json:"environment"`
   348  }
   349  
   350  /*
   351  validateArtifacts is a general function used to validate products and materials.
   352  */
   353  func validateArtifacts(artifacts map[string]interface{}) error {
   354  	for artifactName, artifact := range artifacts {
   355  		artifactValue := reflect.ValueOf(artifact).MapRange()
   356  		for artifactValue.Next() {
   357  			value := artifactValue.Value().Interface().(string)
   358  			hashType := artifactValue.Key().Interface().(string)
   359  			if err := validateHexString(value); err != nil {
   360  				return fmt.Errorf("in artifact '%s', %s hash value: %s",
   361  					artifactName, hashType, err.Error())
   362  			}
   363  		}
   364  	}
   365  	return nil
   366  }
   367  
   368  /*
   369  validateLink is a function used to ensure that a passed item of type Link
   370  matches the necessary format.
   371  */
   372  func validateLink(link Link) error {
   373  	if link.Type != "link" {
   374  		return fmt.Errorf("invalid type for link '%s': should be 'link'",
   375  			link.Name)
   376  	}
   377  
   378  	if err := validateArtifacts(link.Materials); err != nil {
   379  		return fmt.Errorf("in materials of link '%s': %s", link.Name,
   380  			err.Error())
   381  	}
   382  
   383  	if err := validateArtifacts(link.Products); err != nil {
   384  		return fmt.Errorf("in products of link '%s': %s", link.Name,
   385  			err.Error())
   386  	}
   387  
   388  	return nil
   389  }
   390  
   391  /*
   392  LinkNameFormat represents a format string used to create the filename for a
   393  signed Link (wrapped in a Metablock). It consists of the name of the link and
   394  the first 8 characters of the signing key id. E.g.:
   395  	fmt.Sprintf(LinkNameFormat, "package",
   396  	"2f89b9272acfc8f4a0a0f094d789fdb0ba798b0fe41f2f5f417c12f0085ff498")
   397  	// returns "package.2f89b9272.link"
   398  */
   399  const LinkNameFormat = "%s.%.8s.link"
   400  const PreliminaryLinkNameFormat = ".%s.%.8s.link-unfinished"
   401  
   402  /*
   403  LinkNameFormatShort is for links that are not signed, e.g.:
   404  	fmt.Sprintf(LinkNameFormatShort, "unsigned")
   405  	// returns "unsigned.link"
   406  */
   407  const LinkNameFormatShort = "%s.link"
   408  const LinkGlobFormat = "%s.????????.link"
   409  
   410  /*
   411  SublayoutLinkDirFormat represents the format of the name of the directory for
   412  sublayout links during the verification workflow.
   413  */
   414  const SublayoutLinkDirFormat = "%s.%.8s"
   415  
   416  /*
   417  SupplyChainItem summarizes common fields of the two available supply chain
   418  item types, Inspection and Step.
   419  */
   420  type SupplyChainItem struct {
   421  	Name              string     `json:"name"`
   422  	ExpectedMaterials [][]string `json:"expected_materials"`
   423  	ExpectedProducts  [][]string `json:"expected_products"`
   424  }
   425  
   426  /*
   427  validateArtifactRule calls UnpackRule to validate that the passed rule conforms
   428  with any of the available rule formats.
   429  */
   430  func validateArtifactRule(rule []string) error {
   431  	if _, err := UnpackRule(rule); err != nil {
   432  		return err
   433  	}
   434  	return nil
   435  }
   436  
   437  /*
   438  validateSliceOfArtifactRules iterates over passed rules to validate them.
   439  */
   440  func validateSliceOfArtifactRules(rules [][]string) error {
   441  	for _, rule := range rules {
   442  		if err := validateArtifactRule(rule); err != nil {
   443  			return err
   444  		}
   445  	}
   446  	return nil
   447  }
   448  
   449  /*
   450  validateSupplyChainItem is used to validate the common elements found in both
   451  steps and inspections. Here, the function primarily ensures that the name of
   452  a supply chain item isn't empty.
   453  */
   454  func validateSupplyChainItem(item SupplyChainItem) error {
   455  	if item.Name == "" {
   456  		return fmt.Errorf("name cannot be empty")
   457  	}
   458  
   459  	if err := validateSliceOfArtifactRules(item.ExpectedMaterials); err != nil {
   460  		return fmt.Errorf("invalid material rule: %s", err)
   461  	}
   462  	if err := validateSliceOfArtifactRules(item.ExpectedProducts); err != nil {
   463  		return fmt.Errorf("invalid product rule: %s", err)
   464  	}
   465  	return nil
   466  }
   467  
   468  /*
   469  Inspection represents an in-toto supply chain inspection, whose command in the
   470  Run field is executed during final product verification, generating unsigned
   471  link metadata.  Materials and products used/produced by the inspection are
   472  constrained by the artifact rules in the inspection's ExpectedMaterials and
   473  ExpectedProducts fields.
   474  */
   475  type Inspection struct {
   476  	Type string   `json:"_type"`
   477  	Run  []string `json:"run"`
   478  	SupplyChainItem
   479  }
   480  
   481  /*
   482  validateInspection ensures that a passed inspection is valid and matches the
   483  necessary format of an inspection.
   484  */
   485  func validateInspection(inspection Inspection) error {
   486  	if err := validateSupplyChainItem(inspection.SupplyChainItem); err != nil {
   487  		return fmt.Errorf("inspection %s", err.Error())
   488  	}
   489  	if inspection.Type != "inspection" {
   490  		return fmt.Errorf("invalid Type value for inspection '%s': should be "+
   491  			"'inspection'", inspection.SupplyChainItem.Name)
   492  	}
   493  	return nil
   494  }
   495  
   496  /*
   497  Step represents an in-toto step of the supply chain performed by a functionary.
   498  During final product verification in-toto looks for corresponding Link
   499  metadata, which is used as signed evidence that the step was performed
   500  according to the supply chain definition.  Materials and products used/produced
   501  by the step are constrained by the artifact rules in the step's
   502  ExpectedMaterials and ExpectedProducts fields.
   503  */
   504  type Step struct {
   505  	Type                   string                  `json:"_type"`
   506  	PubKeys                []string                `json:"pubkeys"`
   507  	CertificateConstraints []CertificateConstraint `json:"cert_constraints"`
   508  	ExpectedCommand        []string                `json:"expected_command"`
   509  	Threshold              int                     `json:"threshold"`
   510  	SupplyChainItem
   511  }
   512  
   513  // CheckCertConstraints returns true if the provided certificate matches at least one
   514  // of the constraints for this step.
   515  func (s Step) CheckCertConstraints(key Key) bool {
   516  	_, possibleCert, err := decodeAndParse([]byte(key.KeyVal.Certificate))
   517  	if err != nil {
   518  		return false
   519  	}
   520  
   521  	cert, ok := possibleCert.(*x509.Certificate)
   522  	if !ok {
   523  		return false
   524  	}
   525  
   526  	for _, constraint := range s.CertificateConstraints {
   527  		if constraint.Check(cert) {
   528  			return true
   529  		}
   530  	}
   531  
   532  	return false
   533  }
   534  
   535  /*
   536  validateStep ensures that a passed step is valid and matches the
   537  necessary format of an step.
   538  */
   539  func validateStep(step Step) error {
   540  	if err := validateSupplyChainItem(step.SupplyChainItem); err != nil {
   541  		return fmt.Errorf("step %s", err.Error())
   542  	}
   543  	if step.Type != "step" {
   544  		return fmt.Errorf("invalid Type value for step '%s': should be 'step'",
   545  			step.SupplyChainItem.Name)
   546  	}
   547  	for _, keyID := range step.PubKeys {
   548  		if err := validateHexString(keyID); err != nil {
   549  			return err
   550  		}
   551  	}
   552  	return nil
   553  }
   554  
   555  /*
   556  ISO8601DateSchema defines the format string of a timestamp following the
   557  ISO 8601 standard.
   558  */
   559  const ISO8601DateSchema = "2006-01-02T15:04:05Z"
   560  
   561  /*
   562  Layout represents the definition of a software supply chain.  It lists the
   563  sequence of steps required in the software supply chain and the functionaries
   564  authorized to perform these steps.  Functionaries are identified by their
   565  public keys.  In addition, the layout may list a sequence of inspections that
   566  are executed during in-toto supply chain verification.  A layout should be
   567  contained in a generic Metablock object, which provides functionality for
   568  signing and signature verification, and reading from and writing to disk.
   569  */
   570  type Layout struct {
   571  	Type            string         `json:"_type"`
   572  	Steps           []Step         `json:"steps"`
   573  	Inspect         []Inspection   `json:"inspect"`
   574  	Keys            map[string]Key `json:"keys"`
   575  	RootCas         []string       `json:"rootcas"`
   576  	IntermediateCas []string       `json:"intermediatecas"`
   577  	Expires         string         `json:"expires"`
   578  	Readme          string         `json:"readme"`
   579  }
   580  
   581  // Go does not allow to pass `[]T` (slice with certain type) to a function
   582  // that accepts `[]interface{}` (slice with generic type)
   583  // We have to manually create the interface slice first, see
   584  // https://golang.org/doc/faq#convert_slice_of_interface
   585  // TODO: Is there a better way to do polymorphism for steps and inspections?
   586  func (l *Layout) stepsAsInterfaceSlice() []interface{} {
   587  	stepsI := make([]interface{}, len(l.Steps))
   588  	for i, v := range l.Steps {
   589  		stepsI[i] = v
   590  	}
   591  	return stepsI
   592  }
   593  func (l *Layout) inspectAsInterfaceSlice() []interface{} {
   594  	inspectionsI := make([]interface{}, len(l.Inspect))
   595  	for i, v := range l.Inspect {
   596  		inspectionsI[i] = v
   597  	}
   598  	return inspectionsI
   599  }
   600  
   601  /*
   602  validateLayout is a function used to ensure that a passed item of type Layout
   603  matches the necessary format.
   604  */
   605  func validateLayout(layout Layout) error {
   606  	if layout.Type != "layout" {
   607  		return fmt.Errorf("invalid Type value for layout: should be 'layout'")
   608  	}
   609  
   610  	if _, err := time.Parse(ISO8601DateSchema, layout.Expires); err != nil {
   611  		return fmt.Errorf("expiry time parsed incorrectly - date either" +
   612  			" invalid or of incorrect format")
   613  	}
   614  
   615  	for keyID, key := range layout.Keys {
   616  		if key.KeyID != keyID {
   617  			return fmt.Errorf("invalid key found")
   618  		}
   619  		err := validatePublicKey(key)
   620  		if err != nil {
   621  			return err
   622  		}
   623  	}
   624  
   625  	if err := validateCertificates(layout.RootCas); err != nil {
   626  		return err
   627  	}
   628  
   629  	if err := validateCertificates(layout.IntermediateCas); err != nil {
   630  		return err
   631  	}
   632  
   633  	var namesSeen = make(map[string]bool)
   634  	for _, step := range layout.Steps {
   635  		if namesSeen[step.Name] {
   636  			return fmt.Errorf("non unique step or inspection name found")
   637  		}
   638  
   639  		namesSeen[step.Name] = true
   640  
   641  		if err := validateStep(step); err != nil {
   642  			return err
   643  		}
   644  	}
   645  	for _, inspection := range layout.Inspect {
   646  		if namesSeen[inspection.Name] {
   647  			return fmt.Errorf("non unique step or inspection name found")
   648  		}
   649  
   650  		namesSeen[inspection.Name] = true
   651  	}
   652  	return nil
   653  }
   654  
   655  /*
   656  Metablock is a generic container for signable in-toto objects such as Layout
   657  or Link.  It has two fields, one that contains the signable object and one that
   658  contains corresponding signatures.  Metablock also provides functionality for
   659  signing and signature verification, and reading from and writing to disk.
   660  */
   661  type Metablock struct {
   662  	// NOTE: Whenever we want to access an attribute of `Signed` we have to
   663  	// perform type assertion, e.g. `metablock.Signed.(Layout).Keys`
   664  	// Maybe there is a better way to store either Layouts or Links in `Signed`?
   665  	// The notary folks seem to have separate container structs:
   666  	// https://github.com/theupdateframework/notary/blob/master/tuf/data/root.go#L10-L14
   667  	// https://github.com/theupdateframework/notary/blob/master/tuf/data/targets.go#L13-L17
   668  	// I implemented it this way, because there will be several functions that
   669  	// receive or return a Metablock, where the type of Signed has to be inferred
   670  	// on runtime, e.g. when iterating over links for a layout, and a link can
   671  	// turn out to be a layout (sublayout)
   672  	Signed     interface{} `json:"signed"`
   673  	Signatures []Signature `json:"signatures"`
   674  }
   675  
   676  /*
   677  checkRequiredJSONFields checks that the passed map (obj) has keys for each of
   678  the json tags in the passed struct type (typ), and returns an error otherwise.
   679  */
   680  func checkRequiredJSONFields(obj map[string]interface{},
   681  	typ reflect.Type) error {
   682  
   683  	// Create list of json tags, e.g. `json:"_type"`
   684  	attributeCount := typ.NumField()
   685  	allFields := make([]string, 0)
   686  	for i := 0; i < attributeCount; i++ {
   687  		allFields = append(allFields, typ.Field(i).Tag.Get("json"))
   688  	}
   689  
   690  	// Assert that there's a key in the passed map for each tag
   691  	for _, field := range allFields {
   692  		if _, ok := obj[field]; !ok {
   693  			return fmt.Errorf("required field %s missing", field)
   694  		}
   695  	}
   696  	return nil
   697  }
   698  
   699  /*
   700  Load parses JSON formatted metadata at the passed path into the Metablock
   701  object on which it was called.  It returns an error if it cannot parse
   702  a valid JSON formatted Metablock that contains a Link or Layout.
   703  */
   704  func (mb *Metablock) Load(path string) (err error) {
   705  	// Open file and close before returning
   706  	jsonFile, err := os.Open(path)
   707  	if err != nil {
   708  		return err
   709  	}
   710  	defer func() {
   711  		if closeErr := jsonFile.Close(); closeErr != nil {
   712  			err = closeErr
   713  		}
   714  	}()
   715  
   716  	// Read entire file
   717  	jsonBytes, err := ioutil.ReadAll(jsonFile)
   718  	if err != nil {
   719  		return err
   720  	}
   721  
   722  	// Unmarshal JSON into a map of raw messages (signed and signatures)
   723  	// We can't fully unmarshal immediately, because we need to inspect the
   724  	// type (link or layout) to decide which data structure to use
   725  	var rawMb map[string]*json.RawMessage
   726  	if err := json.Unmarshal(jsonBytes, &rawMb); err != nil {
   727  		return err
   728  	}
   729  
   730  	// Error out on missing `signed` or `signatures` field or if
   731  	// one of them has a `null` value, which would lead to a nil pointer
   732  	// dereference in Unmarshal below.
   733  	if rawMb["signed"] == nil || rawMb["signatures"] == nil {
   734  		return fmt.Errorf("In-toto metadata requires 'signed' and" +
   735  			" 'signatures' parts")
   736  	}
   737  
   738  	// Fully unmarshal signatures part
   739  	if err := json.Unmarshal(*rawMb["signatures"], &mb.Signatures); err != nil {
   740  		return err
   741  	}
   742  
   743  	// Temporarily copy signed to opaque map to inspect the `_type` of signed
   744  	// and create link or layout accordingly
   745  	var signed map[string]interface{}
   746  	if err := json.Unmarshal(*rawMb["signed"], &signed); err != nil {
   747  		return err
   748  	}
   749  
   750  	if signed["_type"] == "link" {
   751  		var link Link
   752  		if err := checkRequiredJSONFields(signed, reflect.TypeOf(link)); err != nil {
   753  			return err
   754  		}
   755  
   756  		data, err := rawMb["signed"].MarshalJSON()
   757  		if err != nil {
   758  			return err
   759  		}
   760  		decoder := json.NewDecoder(strings.NewReader(string(data)))
   761  		decoder.DisallowUnknownFields()
   762  		if err := decoder.Decode(&link); err != nil {
   763  			return err
   764  		}
   765  		mb.Signed = link
   766  
   767  	} else if signed["_type"] == "layout" {
   768  		var layout Layout
   769  		if err := checkRequiredJSONFields(signed, reflect.TypeOf(layout)); err != nil {
   770  			return err
   771  		}
   772  
   773  		data, err := rawMb["signed"].MarshalJSON()
   774  		if err != nil {
   775  			return err
   776  		}
   777  		decoder := json.NewDecoder(strings.NewReader(string(data)))
   778  		decoder.DisallowUnknownFields()
   779  		if err := decoder.Decode(&layout); err != nil {
   780  			return err
   781  		}
   782  
   783  		mb.Signed = layout
   784  
   785  	} else {
   786  		return fmt.Errorf("The '_type' field of the 'signed' part of in-toto" +
   787  			" metadata must be one of 'link' or 'layout'")
   788  	}
   789  
   790  	return nil
   791  }
   792  
   793  /*
   794  Dump JSON serializes and writes the Metablock on which it was called to the
   795  passed path.  It returns an error if JSON serialization or writing fails.
   796  */
   797  func (mb *Metablock) Dump(path string) error {
   798  	// JSON encode Metablock formatted with newlines and indentation
   799  	// TODO: parametrize format
   800  	jsonBytes, err := json.MarshalIndent(mb, "", "  ")
   801  	if err != nil {
   802  		return err
   803  	}
   804  
   805  	// Write JSON bytes to the passed path with permissions (-rw-r--r--)
   806  	err = ioutil.WriteFile(path, jsonBytes, 0644)
   807  	if err != nil {
   808  		return err
   809  	}
   810  
   811  	return nil
   812  }
   813  
   814  /*
   815  GetSignableRepresentation returns the canonical JSON representation of the
   816  Signed field of the Metablock on which it was called.  If canonicalization
   817  fails the first return value is nil and the second return value is the error.
   818  */
   819  func (mb *Metablock) GetSignableRepresentation() ([]byte, error) {
   820  	return EncodeCanonical(mb.Signed)
   821  }
   822  
   823  /*
   824  VerifySignature verifies the first signature, corresponding to the passed Key,
   825  that it finds in the Signatures field of the Metablock on which it was called.
   826  It returns an error if Signatures does not contain a Signature corresponding to
   827  the passed Key, the object in Signed cannot be canonicalized, or the Signature
   828  is invalid.
   829  */
   830  func (mb *Metablock) VerifySignature(key Key) error {
   831  	sig, err := mb.GetSignatureForKeyID(key.KeyID)
   832  	if err != nil {
   833  		return err
   834  	}
   835  
   836  	dataCanonical, err := mb.GetSignableRepresentation()
   837  	if err != nil {
   838  		return err
   839  	}
   840  
   841  	if err := VerifySignature(key, sig, dataCanonical); err != nil {
   842  		return err
   843  	}
   844  	return nil
   845  }
   846  
   847  // GetSignatureForKeyID returns the signature that was created by the provided keyID, if it exists.
   848  func (mb *Metablock) GetSignatureForKeyID(keyID string) (Signature, error) {
   849  	for _, s := range mb.Signatures {
   850  		if s.KeyID == keyID {
   851  			return s, nil
   852  		}
   853  	}
   854  
   855  	return Signature{}, fmt.Errorf("No signature found for key '%s'", keyID)
   856  }
   857  
   858  /*
   859  validateMetablock ensures that a passed Metablock object is valid. It indirectly
   860  validates the Link or Layout that the Metablock object contains.
   861  */
   862  func validateMetablock(mb Metablock) error {
   863  	switch mbSignedType := mb.Signed.(type) {
   864  	case Layout:
   865  		if err := validateLayout(mb.Signed.(Layout)); err != nil {
   866  			return err
   867  		}
   868  	case Link:
   869  		if err := validateLink(mb.Signed.(Link)); err != nil {
   870  			return err
   871  		}
   872  	default:
   873  		return fmt.Errorf("unknown type '%s', should be 'layout' or 'link'",
   874  			mbSignedType)
   875  	}
   876  
   877  	if err := validateSliceOfSignatures(mb.Signatures); err != nil {
   878  		return err
   879  	}
   880  
   881  	return nil
   882  }
   883  
   884  /*
   885  Sign creates a signature over the signed portion of the metablock using the Key
   886  object provided. It then appends the resulting signature to the signatures
   887  field as provided. It returns an error if the Signed object cannot be
   888  canonicalized, or if the key is invalid or not supported.
   889  */
   890  func (mb *Metablock) Sign(key Key) error {
   891  
   892  	dataCanonical, err := mb.GetSignableRepresentation()
   893  	if err != nil {
   894  		return err
   895  	}
   896  
   897  	newSignature, err := GenerateSignature(dataCanonical, key)
   898  	if err != nil {
   899  		return err
   900  	}
   901  
   902  	mb.Signatures = append(mb.Signatures, newSignature)
   903  	return nil
   904  }