github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/artifact/dependency.go (about)

     1  /*
     2   * Copyright (c) 2021 CodeNotary, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package artifact
    10  
    11  import (
    12  	"errors"
    13  	"fmt"
    14  
    15  	"github.com/vchain-us/vcn/pkg/api"
    16  	"github.com/vchain-us/vcn/pkg/meta"
    17  )
    18  
    19  type HashType uint
    20  
    21  const (
    22  	HashInvalid HashType = iota
    23  	HashSHA1
    24  	HashSHA224
    25  	HashSHA256
    26  	HashSHA384
    27  	HashSHA512
    28  	HashMD2
    29  	HashMD4
    30  	HashMD5
    31  	HashMD6
    32  	minHash = HashSHA1
    33  	maxHash = HashMD6
    34  )
    35  
    36  var hashText = [maxHash + 1]string{"Invalid", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "MD2", "MD4", "MD5", "MD6"}
    37  
    38  type TrustLevel uint
    39  
    40  const (
    41  	Invalid TrustLevel = iota
    42  	Untrusted
    43  	Unsupported
    44  	Unknown
    45  	Trusted
    46  	MinTrustLevel = Untrusted
    47  	MaxTrustLevel = Trusted
    48  )
    49  
    50  var levelText = [MaxTrustLevel + 1]string{"", "Untrusted", "Unsupported", "Unknown", "Trusted"}
    51  
    52  const MaxGoroutines = 8 // used by other packages that query components from external sources
    53  
    54  // Dependency is a single building block, used for building the Artifact
    55  type Dependency struct {
    56  	Name       string
    57  	Version    string
    58  	Hash       string
    59  	Kind       string
    60  	HashType   HashType
    61  	TrustLevel TrustLevel // set by Notorize/Authenticate
    62  	SignerID   string     // set by Notorize/Authenticate
    63  	License    string
    64  }
    65  
    66  func HashTypeName(hashType HashType) string {
    67  	if hashType > maxHash {
    68  		return hashText[HashInvalid]
    69  	}
    70  	return hashText[hashType]
    71  }
    72  
    73  func HashTypeByName(text string) HashType {
    74  	for i := range hashText {
    75  		if hashText[i] == text {
    76  			return HashType(i)
    77  		}
    78  	}
    79  	return HashInvalid
    80  }
    81  
    82  func TrustLevelName(level TrustLevel) string {
    83  	if level > MaxTrustLevel {
    84  		return "" // return empty string to avoid printing SPDX comment for invalid level
    85  	}
    86  	return levelText[level]
    87  }
    88  
    89  // AuthenticateDependencies ...
    90  func AuthenticateDependencies(
    91  	lcUser *api.LcUser,
    92  	signerID string,
    93  	deps []Dependency,
    94  	batchSize int,
    95  	progressCallback func([]Dependency),
    96  ) ([]error, error) {
    97  	if len(deps) == 0 {
    98  		return nil, nil
    99  	}
   100  
   101  	hashes := make([]string, 0, len(deps))
   102  	for _, dep := range deps {
   103  		hashes = append(hashes, dep.Hash)
   104  	}
   105  
   106  	if batchSize <= 0 {
   107  		batchSize = len(hashes)
   108  	}
   109  	nbBatches := len(hashes) / int(batchSize)
   110  	if nbBatches*batchSize < len(hashes) {
   111  		nbBatches++
   112  	}
   113  
   114  	var artifacts []*api.LcArtifact
   115  	var verified []bool
   116  	var errs []error
   117  
   118  	for i := 0; i < nbBatches; i++ {
   119  		startAt := i * batchSize
   120  		endBefore := startAt + batchSize
   121  		if endBefore > len(hashes) {
   122  			endBefore = len(hashes)
   123  		}
   124  
   125  		currArtifacts, currVerified, currErrs, err := lcUser.LoadArtifacts(signerID, hashes[startAt:endBefore], nil)
   126  		if progressCallback != nil {
   127  			progressCallback(deps[startAt:endBefore])
   128  		}
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  
   133  		artifacts = append(artifacts, currArtifacts...)
   134  		verified = append(verified, currVerified...)
   135  		errs = append(errs, currErrs...)
   136  	}
   137  
   138  	retErrs := make([]error, len(deps))
   139  	for i := 0; i < len(deps); i++ {
   140  		level := Unknown
   141  		err := errs[i]
   142  		if err == nil {
   143  			switch {
   144  			case !verified[i]:
   145  				return nil, errors.New("the ledger is compromised")
   146  			case artifacts[i].Status == meta.StatusUntrusted || (artifacts[i].Revoked != nil && !artifacts[i].Revoked.IsZero()):
   147  				level = Untrusted
   148  			case artifacts[i].Status == meta.StatusUnsupported:
   149  				level = Unsupported
   150  			default:
   151  				level = Trusted
   152  			}
   153  		} else if err != api.ErrNotFound {
   154  			retErrs[i] = err
   155  		}
   156  
   157  		deps[i].TrustLevel = level
   158  		if level != Unknown {
   159  			deps[i].SignerID = signerID
   160  		}
   161  	}
   162  
   163  	return retErrs, nil
   164  }
   165  
   166  // NotarizeDependencies ...
   167  func NotarizeDependencies(
   168  	lcUser *api.LcUser,
   169  	kinds []string,
   170  	deps []*Dependency,
   171  	batchSize int,
   172  	progressCallback func([]*Dependency),
   173  ) ([]error, error) {
   174  	if len(deps) == 0 {
   175  		return nil, nil
   176  	}
   177  
   178  	if len(kinds) != len(deps) {
   179  		return nil, fmt.Errorf("number of kinds (%d) and dependencies (%d) must match", len(kinds), len(deps))
   180  	}
   181  
   182  	if batchSize <= 0 {
   183  		batchSize = len(deps)
   184  	}
   185  	nbBatches := len(deps) / int(batchSize)
   186  	if nbBatches*batchSize < len(deps) {
   187  		nbBatches++
   188  	}
   189  
   190  	var errs []error
   191  
   192  	for i := 0; i < nbBatches; i++ {
   193  		startAt := i * batchSize
   194  		endBefore := startAt + batchSize
   195  		if endBefore > len(deps) {
   196  			endBefore = len(deps)
   197  		}
   198  		currDeps := deps[startAt:endBefore]
   199  
   200  		artifacts := make([]*api.Artifact, len(currDeps))
   201  		options := make([][]api.LcSignOption, len(currDeps))
   202  		for i, dep := range currDeps {
   203  			artifacts[i] = ToApiArtifact(kinds[i], dep.Name, dep.Version, dep.Hash, dep.HashType)
   204  			options[i] = []api.LcSignOption{api.LcSignWithStatus(meta.StatusTrusted)}
   205  		}
   206  
   207  		_, _, currErrs, err := lcUser.SignMulti(artifacts, options)
   208  		if progressCallback != nil {
   209  			progressCallback(currDeps)
   210  		}
   211  		if err != nil {
   212  			return nil, fmt.Errorf("notarization of %d dependencies failed: %w", len(deps), err)
   213  		}
   214  		errs = append(errs, currErrs...)
   215  	}
   216  
   217  	signerID := api.GetSignerIDByApiKey(lcUser.Client.ApiKey)
   218  	for i := range deps {
   219  		if errs[i] == nil {
   220  			deps[i].TrustLevel = Trusted
   221  		}
   222  		deps[i].SignerID = signerID
   223  	}
   224  
   225  	return errs, nil
   226  }
   227  
   228  // ToApiArtifact ...
   229  func ToApiArtifact(kind, name, version, hash string, hashType HashType) *api.Artifact {
   230  	return &api.Artifact{
   231  		Kind: kind,
   232  		Name: name,
   233  		Hash: hash,
   234  		Size: 0,
   235  		Metadata: map[string]interface{}{
   236  			"version":  version,
   237  			"hashtype": HashTypeName(hashType)}}
   238  }