...

Source file src/github.com/google/go-containerregistry/internal/verify/verify.go

Documentation: github.com/google/go-containerregistry/internal/verify

     1  // Copyright 2020 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 verify provides a ReadCloser that verifies content matches the
    16  // expected hash values.
    17  package verify
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/hex"
    22  	"errors"
    23  	"fmt"
    24  	"hash"
    25  	"io"
    26  
    27  	"github.com/google/go-containerregistry/internal/and"
    28  	v1 "github.com/google/go-containerregistry/pkg/v1"
    29  )
    30  
    31  // SizeUnknown is a sentinel value to indicate that the expected size is not known.
    32  const SizeUnknown = -1
    33  
    34  type verifyReader struct {
    35  	inner             io.Reader
    36  	hasher            hash.Hash
    37  	expected          v1.Hash
    38  	gotSize, wantSize int64
    39  }
    40  
    41  // Error provides information about the failed hash verification.
    42  type Error struct {
    43  	got     string
    44  	want    v1.Hash
    45  	gotSize int64
    46  }
    47  
    48  func (v Error) Error() string {
    49  	return fmt.Sprintf("error verifying %s checksum after reading %d bytes; got %q, want %q",
    50  		v.want.Algorithm, v.gotSize, v.got, v.want)
    51  }
    52  
    53  // Read implements io.Reader
    54  func (vc *verifyReader) Read(b []byte) (int, error) {
    55  	n, err := vc.inner.Read(b)
    56  	vc.gotSize += int64(n)
    57  	if err == io.EOF {
    58  		if vc.wantSize != SizeUnknown && vc.gotSize != vc.wantSize {
    59  			return n, fmt.Errorf("error verifying size; got %d, want %d", vc.gotSize, vc.wantSize)
    60  		}
    61  		got := hex.EncodeToString(vc.hasher.Sum(nil))
    62  		if want := vc.expected.Hex; got != want {
    63  			return n, Error{
    64  				got:     vc.expected.Algorithm + ":" + got,
    65  				want:    vc.expected,
    66  				gotSize: vc.gotSize,
    67  			}
    68  		}
    69  	}
    70  	return n, err
    71  }
    72  
    73  // ReadCloser wraps the given io.ReadCloser to verify that its contents match
    74  // the provided v1.Hash before io.EOF is returned.
    75  //
    76  // The reader will only be read up to size bytes, to prevent resource
    77  // exhaustion. If EOF is returned before size bytes are read, an error is
    78  // returned.
    79  //
    80  // A size of SizeUnknown (-1) indicates disables size verification when the size
    81  // is unknown ahead of time.
    82  func ReadCloser(r io.ReadCloser, size int64, h v1.Hash) (io.ReadCloser, error) {
    83  	w, err := v1.Hasher(h.Algorithm)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	r2 := io.TeeReader(r, w) // pass all writes to the hasher.
    88  	if size != SizeUnknown {
    89  		r2 = io.LimitReader(r2, size) // if we know the size, limit to that size.
    90  	}
    91  	return &and.ReadCloser{
    92  		Reader: &verifyReader{
    93  			inner:    r2,
    94  			hasher:   w,
    95  			expected: h,
    96  			wantSize: size,
    97  		},
    98  		CloseFunc: r.Close,
    99  	}, nil
   100  }
   101  
   102  // Descriptor verifies that the embedded Data field matches the Size and Digest
   103  // fields of the given v1.Descriptor, returning an error if the Data field is
   104  // missing or if it contains incorrect data.
   105  func Descriptor(d v1.Descriptor) error {
   106  	if d.Data == nil {
   107  		return errors.New("error verifying descriptor; Data == nil")
   108  	}
   109  
   110  	h, sz, err := v1.SHA256(bytes.NewReader(d.Data))
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if h != d.Digest {
   115  		return fmt.Errorf("error verifying Digest; got %q, want %q", h, d.Digest)
   116  	}
   117  	if sz != d.Size {
   118  		return fmt.Errorf("error verifying Size; got %d, want %d", sz, d.Size)
   119  	}
   120  
   121  	return nil
   122  }
   123  

View as plain text