...

Source file src/github.com/in-toto/in-toto-golang/in_toto/model.go

Documentation: github.com/in-toto/in-toto-golang/in_toto

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

View as plain text