github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/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 // create a set of hasher objects tied together with a single writer to feed content into 26 hashers := make([]hash.Hash, len(hashes)) 27 writers := make([]io.Writer, len(hashes)) 28 for idx, hashObj := range hashes { 29 hashers[idx] = hashObj.New() 30 writers[idx] = hashers[idx] 31 } 32 33 size, err := io.Copy(io.MultiWriter(writers...), closer) 34 if err != nil { 35 return nil, err 36 } 37 38 if size == 0 { 39 return make([]file.Digest, 0), nil 40 } 41 42 result := make([]file.Digest, len(hashes)) 43 // only capture digests when there is content. It is important to do this based on SIZE and not 44 // FILE TYPE. The reasoning is that it is possible for a tar to be crafted with a header-only 45 // file type but a body is still allowed. 46 for idx, hasher := range hashers { 47 result[idx] = file.Digest{ 48 Algorithm: CleanDigestAlgorithmName(hashes[idx].String()), 49 Value: fmt.Sprintf("%+x", hasher.Sum(nil)), 50 } 51 } 52 53 return result, nil 54 } 55 56 func Hashers(names ...string) ([]crypto.Hash, error) { 57 hashByName := make(map[string]crypto.Hash) 58 for _, h := range supportedHashAlgorithms() { 59 hashByName[CleanDigestAlgorithmName(h.String())] = h 60 } 61 62 var hashers []crypto.Hash 63 for _, hashStr := range names { 64 hashObj, ok := hashByName[CleanDigestAlgorithmName(hashStr)] 65 if !ok { 66 return nil, fmt.Errorf("unsupported hash algorithm: %s", hashStr) 67 } 68 hashers = append(hashers, hashObj) 69 } 70 return hashers, nil 71 } 72 73 func CleanDigestAlgorithmName(name string) string { 74 lower := strings.ToLower(name) 75 return strings.ReplaceAll(lower, "-", "") 76 }