github.com/uber/kraken@v0.1.4/core/digest.go (about)

     1  // Copyright (c) 2016-2019 Uber Technologies, Inc.
     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  package core
    15  
    16  import (
    17  	_ "crypto/sha256" // For computing digest.
    18  	"database/sql/driver"
    19  	"encoding/hex"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"strings"
    24  )
    25  
    26  const (
    27  	// DigestEmptyTar is the sha256 digest of an empty tar file.
    28  	DigestEmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    29  )
    30  
    31  // DigestList is a list of digests.
    32  type DigestList []Digest
    33  
    34  // Value marshals a list of digests and returns []byte as driver.Value.
    35  func (l DigestList) Value() (driver.Value, error) {
    36  	b, err := json.Marshal(l)
    37  	if err != nil {
    38  		return driver.Value([]byte{}), err
    39  	}
    40  	return driver.Value(b), nil
    41  }
    42  
    43  // Scan unmarshals []byte to a list of Digest.
    44  func (l *DigestList) Scan(src interface{}) error {
    45  	return json.Unmarshal(src.([]byte), l)
    46  }
    47  
    48  // Digest can be represented in a string like "<algorithm>:<hex_digest_string>"
    49  // Example:
    50  // 	 sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    51  type Digest struct {
    52  	algo string
    53  	hex  string
    54  	raw  string
    55  }
    56  
    57  // NewSHA256DigestFromHex constructs a Digest from a sha256 in hexadecimal
    58  // format. Returns error if hex is not a valid sha256.
    59  func NewSHA256DigestFromHex(hex string) (Digest, error) {
    60  	if err := ValidateSHA256(hex); err != nil {
    61  		return Digest{}, fmt.Errorf("invalid sha256: %s", err)
    62  	}
    63  	return Digest{
    64  		algo: SHA256,
    65  		hex:  hex,
    66  		raw:  fmt.Sprintf("%s:%s", SHA256, hex),
    67  	}, nil
    68  }
    69  
    70  // ParseSHA256Digest parses a raw "<algo>:<hex>" sha256 digest. Returns error if the
    71  // algo is not sha256 or the hex is not a valid sha256.
    72  func ParseSHA256Digest(raw string) (Digest, error) {
    73  	if raw == "" {
    74  		return Digest{}, errors.New("invalid digest: empty")
    75  	}
    76  	parts := strings.Split(raw, ":")
    77  	if len(parts) != 2 {
    78  		return Digest{}, errors.New("invalid digest: expected '<algo>:<hex>'")
    79  	}
    80  	algo := parts[0]
    81  	hex := parts[1]
    82  	if algo != SHA256 {
    83  		return Digest{}, errors.New("invalid digest algo: expected sha256")
    84  	}
    85  	if err := ValidateSHA256(hex); err != nil {
    86  		return Digest{}, fmt.Errorf("invalid sha256: %s", err)
    87  	}
    88  	return Digest{
    89  		algo: algo,
    90  		hex:  hex,
    91  		raw:  raw,
    92  	}, nil
    93  }
    94  
    95  // Value marshals a digest and returns []byte as driver.Value.
    96  func (d Digest) Value() (driver.Value, error) {
    97  	b, err := json.Marshal(d)
    98  	if err != nil {
    99  		return driver.Value([]byte{}), err
   100  	}
   101  	return driver.Value(b), nil
   102  }
   103  
   104  // Scan unmarshals []byte to a Digest.
   105  func (d *Digest) Scan(src interface{}) error {
   106  	return json.Unmarshal(src.([]byte), d)
   107  }
   108  
   109  // UnmarshalJSON unmarshals "<algorithm>:<hex_digest_string>" to Digest.
   110  func (d *Digest) UnmarshalJSON(str []byte) error {
   111  	var raw string
   112  	if err := json.Unmarshal(str, &raw); err != nil {
   113  		return err
   114  	}
   115  	digest, err := ParseSHA256Digest(raw)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	*d = digest
   120  	return nil
   121  }
   122  
   123  // MarshalJSON unmarshals hexBytes to Digest.
   124  func (d Digest) MarshalJSON() ([]byte, error) {
   125  	return json.Marshal(d.raw)
   126  }
   127  
   128  // String returns digest in string format like "<algorithm>:<hex_digest_string>".
   129  func (d Digest) String() string {
   130  	return d.raw
   131  }
   132  
   133  // Algo returns the algo part of the digest.
   134  // Example:
   135  //   sha256
   136  func (d Digest) Algo() string {
   137  	return d.algo
   138  }
   139  
   140  // Hex returns the hex part of the digest.
   141  // Example:
   142  //   e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
   143  func (d Digest) Hex() string {
   144  	return d.hex
   145  }
   146  
   147  // ShardID returns the shard id of the digest.
   148  func (d Digest) ShardID() string {
   149  	return d.hex[:4]
   150  }
   151  
   152  // ValidateSHA256 returns error if s is not a valid SHA256 hex digest.
   153  func ValidateSHA256(s string) error {
   154  	if len(s) != 64 {
   155  		return fmt.Errorf("expected 64 characters, got %d from %q", len(s), s)
   156  	}
   157  	if _, err := hex.DecodeString(s); err != nil {
   158  		return fmt.Errorf("hex: %s", err)
   159  	}
   160  	return nil
   161  }