...

Source file src/github.com/google/go-containerregistry/pkg/v1/hash.go

Documentation: github.com/google/go-containerregistry/pkg/v1

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package v1
    16  
    17  import (
    18  	"crypto"
    19  	"encoding/hex"
    20  	"encoding/json"
    21  	"fmt"
    22  	"hash"
    23  	"io"
    24  	"strconv"
    25  	"strings"
    26  )
    27  
    28  // Hash is an unqualified digest of some content, e.g. sha256:deadbeef
    29  type Hash struct {
    30  	// Algorithm holds the algorithm used to compute the hash.
    31  	Algorithm string
    32  
    33  	// Hex holds the hex portion of the content hash.
    34  	Hex string
    35  }
    36  
    37  // String reverses NewHash returning the string-form of the hash.
    38  func (h Hash) String() string {
    39  	return fmt.Sprintf("%s:%s", h.Algorithm, h.Hex)
    40  }
    41  
    42  // NewHash validates the input string is a hash and returns a strongly type Hash object.
    43  func NewHash(s string) (Hash, error) {
    44  	h := Hash{}
    45  	if err := h.parse(s); err != nil {
    46  		return Hash{}, err
    47  	}
    48  	return h, nil
    49  }
    50  
    51  // MarshalJSON implements json.Marshaler
    52  func (h Hash) MarshalJSON() ([]byte, error) {
    53  	return json.Marshal(h.String())
    54  }
    55  
    56  // UnmarshalJSON implements json.Unmarshaler
    57  func (h *Hash) UnmarshalJSON(data []byte) error {
    58  	s, err := strconv.Unquote(string(data))
    59  	if err != nil {
    60  		return err
    61  	}
    62  	return h.parse(s)
    63  }
    64  
    65  // MarshalText implements encoding.TextMarshaler. This is required to use
    66  // v1.Hash as a key in a map when marshalling JSON.
    67  func (h Hash) MarshalText() (text []byte, err error) {
    68  	return []byte(h.String()), nil
    69  }
    70  
    71  // UnmarshalText implements encoding.TextUnmarshaler. This is required to use
    72  // v1.Hash as a key in a map when unmarshalling JSON.
    73  func (h *Hash) UnmarshalText(text []byte) error {
    74  	return h.parse(string(text))
    75  }
    76  
    77  // Hasher returns a hash.Hash for the named algorithm (e.g. "sha256")
    78  func Hasher(name string) (hash.Hash, error) {
    79  	switch name {
    80  	case "sha256":
    81  		return crypto.SHA256.New(), nil
    82  	default:
    83  		return nil, fmt.Errorf("unsupported hash: %q", name)
    84  	}
    85  }
    86  
    87  func (h *Hash) parse(unquoted string) error {
    88  	parts := strings.Split(unquoted, ":")
    89  	if len(parts) != 2 {
    90  		return fmt.Errorf("cannot parse hash: %q", unquoted)
    91  	}
    92  
    93  	rest := strings.TrimLeft(parts[1], "0123456789abcdef")
    94  	if len(rest) != 0 {
    95  		return fmt.Errorf("found non-hex character in hash: %c", rest[0])
    96  	}
    97  
    98  	hasher, err := Hasher(parts[0])
    99  	if err != nil {
   100  		return err
   101  	}
   102  	// Compare the hex to the expected size (2 hex characters per byte)
   103  	if len(parts[1]) != hasher.Size()*2 {
   104  		return fmt.Errorf("wrong number of hex digits for %s: %s", parts[0], parts[1])
   105  	}
   106  
   107  	h.Algorithm = parts[0]
   108  	h.Hex = parts[1]
   109  	return nil
   110  }
   111  
   112  // SHA256 computes the Hash of the provided io.Reader's content.
   113  func SHA256(r io.Reader) (Hash, int64, error) {
   114  	hasher := crypto.SHA256.New()
   115  	n, err := io.Copy(hasher, r)
   116  	if err != nil {
   117  		return Hash{}, 0, err
   118  	}
   119  	return Hash{
   120  		Algorithm: "sha256",
   121  		Hex:       hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))),
   122  	}, n, nil
   123  }
   124  

View as plain text