oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/content/reader.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     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  
    16  package content
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/opencontainers/go-digest"
    24  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    25  )
    26  
    27  var (
    28  	// ErrInvalidDescriptorSize is returned by ReadAll() when
    29  	// the descriptor has an invalid size.
    30  	ErrInvalidDescriptorSize = errors.New("invalid descriptor size")
    31  
    32  	// ErrMismatchedDigest is returned by ReadAll() when
    33  	// the descriptor has an invalid digest.
    34  	ErrMismatchedDigest = errors.New("mismatched digest")
    35  
    36  	// ErrTrailingData is returned by ReadAll() when
    37  	// there exists trailing data unread when the read terminates.
    38  	ErrTrailingData = errors.New("trailing data")
    39  )
    40  
    41  var (
    42  	// errEarlyVerify is returned by VerifyReader.Verify() when
    43  	// Verify() is called before completing reading the entire content blob.
    44  	errEarlyVerify = errors.New("early verify")
    45  )
    46  
    47  // VerifyReader reads the content described by its descriptor and verifies
    48  // against its size and digest.
    49  type VerifyReader struct {
    50  	base     *io.LimitedReader
    51  	verifier digest.Verifier
    52  	verified bool
    53  	err      error
    54  }
    55  
    56  // Read reads up to len(p) bytes into p. It returns the number of bytes
    57  // read (0 <= n <= len(p)) and any error encountered.
    58  func (vr *VerifyReader) Read(p []byte) (n int, err error) {
    59  	if vr.err != nil {
    60  		return 0, vr.err
    61  	}
    62  
    63  	n, err = vr.base.Read(p)
    64  	if err != nil {
    65  		if err == io.EOF && vr.base.N > 0 {
    66  			err = io.ErrUnexpectedEOF
    67  		}
    68  		vr.err = err
    69  	}
    70  	return
    71  }
    72  
    73  // Verify checks for remaining unread content and verifies the read content against the digest
    74  func (vr *VerifyReader) Verify() error {
    75  	if vr.verified {
    76  		return nil
    77  	}
    78  	if vr.err == nil {
    79  		if vr.base.N > 0 {
    80  			return errEarlyVerify
    81  		}
    82  	} else if vr.err != io.EOF {
    83  		return vr.err
    84  	}
    85  
    86  	if err := ensureEOF(vr.base.R); err != nil {
    87  		vr.err = err
    88  		return vr.err
    89  	}
    90  	if !vr.verifier.Verified() {
    91  		vr.err = ErrMismatchedDigest
    92  		return vr.err
    93  	}
    94  
    95  	vr.verified = true
    96  	vr.err = io.EOF
    97  	return nil
    98  }
    99  
   100  // NewVerifyReader wraps r for reading content with verification against desc.
   101  func NewVerifyReader(r io.Reader, desc ocispec.Descriptor) *VerifyReader {
   102  	verifier := desc.Digest.Verifier()
   103  	lr := &io.LimitedReader{
   104  		R: io.TeeReader(r, verifier),
   105  		N: desc.Size,
   106  	}
   107  	return &VerifyReader{
   108  		base:     lr,
   109  		verifier: verifier,
   110  	}
   111  }
   112  
   113  // ReadAll safely reads the content described by the descriptor.
   114  // The read content is verified against the size and the digest
   115  // using a VerifyReader.
   116  func ReadAll(r io.Reader, desc ocispec.Descriptor) ([]byte, error) {
   117  	if desc.Size < 0 {
   118  		return nil, ErrInvalidDescriptorSize
   119  	}
   120  	buf := make([]byte, desc.Size)
   121  
   122  	vr := NewVerifyReader(r, desc)
   123  	if n, err := io.ReadFull(vr, buf); err != nil {
   124  		if errors.Is(err, io.ErrUnexpectedEOF) {
   125  			return nil, fmt.Errorf("read failed: expected content size of %d, got %d, for digest %s: %w", desc.Size, n, desc.Digest.String(), err)
   126  		}
   127  		return nil, fmt.Errorf("read failed: %w", err)
   128  	}
   129  	if err := vr.Verify(); err != nil {
   130  		return nil, err
   131  	}
   132  	return buf, nil
   133  }
   134  
   135  // ensureEOF ensures the read operation ends with an EOF and no
   136  // trailing data is present.
   137  func ensureEOF(r io.Reader) error {
   138  	var peek [1]byte
   139  	_, err := io.ReadFull(r, peek[:])
   140  	if err != io.EOF {
   141  		return ErrTrailingData
   142  	}
   143  	return nil
   144  }