github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/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 verifies the read content against the size and 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 _, err := io.ReadFull(vr, buf); err != nil { 124 return nil, fmt.Errorf("read failed: %w", err) 125 } 126 if err := vr.Verify(); err != nil { 127 return nil, err 128 } 129 return buf, nil 130 } 131 132 // ensureEOF ensures the read operation ends with an EOF and no 133 // trailing data is present. 134 func ensureEOF(r io.Reader) error { 135 var peek [1]byte 136 _, err := io.ReadFull(r, peek[:]) 137 if err != io.EOF { 138 return ErrTrailingData 139 } 140 return nil 141 }