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