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

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