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 }