github.com/anchore/syft@v1.38.2/internal/file/digest.go (about) 1 package file 2 3 import ( 4 "context" 5 "crypto" 6 "fmt" 7 "hash" 8 "io" 9 "strings" 10 11 "github.com/anchore/go-sync" 12 "github.com/anchore/syft/syft/cataloging" 13 "github.com/anchore/syft/syft/file" 14 ) 15 16 func supportedHashAlgorithms() []crypto.Hash { 17 return []crypto.Hash{ 18 crypto.MD5, 19 crypto.SHA1, 20 crypto.SHA224, 21 crypto.SHA256, 22 crypto.SHA384, 23 crypto.SHA512, 24 } 25 } 26 27 func NewDigestsFromFile(ctx context.Context, closer io.ReadCloser, hashes []crypto.Hash) ([]file.Digest, error) { 28 hashes = NormalizeHashes(hashes) 29 // create a set of hasher objects tied together with a single writer to feed content into 30 hashers := make([]hash.Hash, len(hashes)) 31 writers := make([]io.Writer, len(hashes)) 32 for idx, hashObj := range hashes { 33 hashers[idx] = hashObj.New() 34 writers[idx] = hashers[idx] 35 } 36 37 size, err := io.Copy(sync.ParallelWriter(ctx, cataloging.ExecutorCPU, writers...), closer) 38 if err != nil { 39 return nil, err 40 } 41 42 if size == 0 { 43 return make([]file.Digest, 0), nil 44 } 45 46 result := make([]file.Digest, len(hashes)) 47 // only capture digests when there is content. It is important to do this based on SIZE and not 48 // FILE TYPE. The reasoning is that it is possible for a tar to be crafted with a header-only 49 // file type but a body is still allowed. 50 for idx, hasher := range hashers { 51 result[idx] = file.Digest{ 52 Algorithm: CleanDigestAlgorithmName(hashes[idx].String()), 53 Value: fmt.Sprintf("%+x", hasher.Sum(nil)), 54 } 55 } 56 57 return result, nil 58 } 59 60 func Hashers(names ...string) ([]crypto.Hash, error) { 61 hashByName := make(map[string]crypto.Hash) 62 for _, h := range supportedHashAlgorithms() { 63 hashByName[CleanDigestAlgorithmName(h.String())] = h 64 } 65 66 var hashers []crypto.Hash 67 for _, hashStr := range names { 68 hashObj, ok := hashByName[CleanDigestAlgorithmName(hashStr)] 69 if !ok { 70 return nil, fmt.Errorf("unsupported hash algorithm: %s", hashStr) 71 } 72 hashers = append(hashers, hashObj) 73 } 74 return NormalizeHashes(hashers), nil 75 } 76 77 func CleanDigestAlgorithmName(name string) string { 78 lower := strings.ToLower(name) 79 return strings.ReplaceAll(lower, "-", "") 80 }