storj.io/uplink@v1.13.0/private/storage/streams/splitter/splitter.go (about)

     1  // Copyright (C) 2023 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package splitter
     5  
     6  import (
     7  	"context"
     8  	"crypto/rand"
     9  	"io"
    10  
    11  	"github.com/zeebo/errs"
    12  
    13  	"storj.io/common/encryption"
    14  	"storj.io/common/storj"
    15  	"storj.io/uplink/private/metaclient"
    16  	"storj.io/uplink/private/storage/streams/buffer"
    17  )
    18  
    19  // Segment is an interface describing what operations a segment must provide
    20  // to be uploaded to the network.
    21  type Segment interface {
    22  	// Begin returns a metaclient.BatchItem to begin the segment, either inline
    23  	// or remote.
    24  	Begin() metaclient.BatchItem
    25  
    26  	// Position returns the segment position.
    27  	Position() metaclient.SegmentPosition
    28  
    29  	// Inline returns true if the segment is small enough to be inline.
    30  	Inline() bool
    31  
    32  	// Reader returns a fresh io.Reader that reads the data of the segment.
    33  	Reader() io.Reader
    34  
    35  	// EncryptETag encrypts the provided etag with the correct encryption
    36  	// keys that the segment is using.
    37  	EncryptETag(eTag []byte) ([]byte, error)
    38  
    39  	// Finalize returns a SegmentInfo if the segment is done being read
    40  	// from.
    41  	Finalize() *SegmentInfo
    42  
    43  	// DoneReading reports to the segment that we are no longer reading
    44  	// with the provided error to report to writes.
    45  	DoneReading(err error)
    46  }
    47  
    48  // SegmentInfo is information related to what is necessary to commit
    49  // the segment.
    50  type SegmentInfo struct {
    51  	// Encryption contains the encryption parameters that will be stored
    52  	// on the satellite.
    53  	Encryption metaclient.SegmentEncryption
    54  
    55  	// PlainSize is the plaintext number of bytes in the segment.
    56  	PlainSize int64
    57  
    58  	// EncryptedSize is the encrypted number of bytes in the segment.
    59  	EncryptedSize int64
    60  }
    61  
    62  // Options controls parameters of how an incoming stream of bytes is
    63  // split into segments, remote and inline.
    64  type Options struct {
    65  	// Split is the plaintext number of bytes to start new segments.
    66  	Split int64
    67  
    68  	// Minimum is the plaintext number of bytes necessary to create
    69  	// a remote segment.
    70  	Minimum int64
    71  
    72  	// Params controls the encryption used on the plaintext bytes.
    73  	Params storj.EncryptionParameters
    74  
    75  	// Key is used to encrypt the encryption keys used to encrypt
    76  	// the data.
    77  	Key *storj.Key
    78  
    79  	// PartNumber is the segment's part number if doing multipart
    80  	// uploads, and 0 otherwise.
    81  	PartNumber int32
    82  }
    83  
    84  // Splitter takes an incoming stream of bytes and splits it into
    85  // encrypted segments.
    86  type Splitter struct {
    87  	// NewBackend lets one swap out the backend used to store segments
    88  	// while they are being uploaded.
    89  	NewBackend func() (buffer.Backend, error)
    90  
    91  	split          *baseSplitter
    92  	opts           Options
    93  	maxSegmentSize int64
    94  	index          int32
    95  }
    96  
    97  // New constructs a Splitter with the provided Options.
    98  func New(opts Options) (*Splitter, error) {
    99  	maxSegmentSize, err := encryption.CalcEncryptedSize(opts.Split, opts.Params)
   100  	if err != nil {
   101  		return nil, errs.Wrap(err)
   102  	}
   103  
   104  	return &Splitter{
   105  		NewBackend: func() (buffer.Backend, error) {
   106  			return buffer.NewChunkBackend(maxSegmentSize), nil
   107  		},
   108  
   109  		split:          newBaseSplitter(opts.Split, opts.Minimum),
   110  		opts:           opts,
   111  		maxSegmentSize: maxSegmentSize,
   112  	}, nil
   113  }
   114  
   115  // Finish informs the Splitter that no more writes are coming, along with any error
   116  // that may have caused the writes to stop.
   117  func (s *Splitter) Finish(err error) { s.split.Finish(err) }
   118  
   119  // Write appends data into the stream.
   120  func (s *Splitter) Write(p []byte) (int, error) { return s.split.Write(p) }
   121  
   122  // Next returns the next Segment split from the stream. If the stream is finished then
   123  // it will return nil, nil.
   124  func (s *Splitter) Next(ctx context.Context) (Segment, error) {
   125  	position := metaclient.SegmentPosition{
   126  		PartNumber: s.opts.PartNumber,
   127  		Index:      s.index,
   128  	}
   129  	var contentKey storj.Key
   130  	var keyNonce storj.Nonce
   131  
   132  	// do all of the fallible actions before checking with the splitter
   133  	nonce, err := nonceForPosition(position)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	if _, err := rand.Read(contentKey[:]); err != nil {
   138  		return nil, errs.Wrap(err)
   139  	}
   140  	if _, err := rand.Read(keyNonce[:]); err != nil {
   141  		return nil, errs.Wrap(err)
   142  	}
   143  
   144  	// note that we are *not* using the cipher suite from the encryption store, which
   145  	// might be encnull. we must make sure this actually encrypts here, otherwise the
   146  	// satellite will receive the decryption keys for all uploaded data.
   147  	// also, maybe the storage nodes may receive unencrypted data?
   148  	if s.opts.Params.CipherSuite == storj.EncNull ||
   149  		s.opts.Params.CipherSuite == storj.EncNullBase64URL {
   150  		return nil, errs.New("programmer error")
   151  	}
   152  	enc, err := encryption.NewEncrypter(s.opts.Params.CipherSuite, &contentKey, &nonce, int(s.opts.Params.BlockSize))
   153  	if err != nil {
   154  		return nil, errs.Wrap(err)
   155  	}
   156  	encKey, err := encryption.EncryptKey(&contentKey, s.opts.Params.CipherSuite, s.opts.Key, &keyNonce)
   157  	if err != nil {
   158  		return nil, errs.Wrap(err)
   159  	}
   160  	backend, err := s.NewBackend()
   161  	if err != nil {
   162  		return nil, errs.Wrap(err)
   163  	}
   164  
   165  	buf := buffer.New(backend, s.opts.Minimum)
   166  	wrc := encryption.TransformWriterPadded(buf, enc)
   167  	encBuf := newEncryptedBuffer(buf, wrc)
   168  	segEncryption := metaclient.SegmentEncryption{
   169  		EncryptedKeyNonce: keyNonce,
   170  		EncryptedKey:      encKey,
   171  	}
   172  
   173  	// check for the next segment/inline boundary. if an error, don't update any
   174  	// local state.
   175  	inline, eof, err := s.split.Next(ctx, encBuf)
   176  	switch {
   177  	case err != nil:
   178  		return nil, errs.Combine(errs.Wrap(err), errs.Wrap(backend.Close()))
   179  
   180  	case eof:
   181  		return nil, errs.Wrap(backend.Close())
   182  
   183  	case inline != nil:
   184  		// encrypt the inline data, and update the internal state if it succeeds.
   185  		encData, err := encryption.Encrypt(inline, s.opts.Params.CipherSuite, &contentKey, &nonce)
   186  		if err != nil {
   187  			return nil, errs.Combine(errs.Wrap(err), errs.Wrap(backend.Close()))
   188  		}
   189  
   190  		// everything fallible is done. update the internal state.
   191  		s.index++
   192  
   193  		return &splitterInline{
   194  			position:   position,
   195  			encryption: segEncryption,
   196  			encParams:  s.opts.Params,
   197  			contentKey: &contentKey,
   198  
   199  			encData:   encData,
   200  			plainSize: int64(len(inline)),
   201  		}, errs.Wrap(backend.Close())
   202  
   203  	default:
   204  		// everything fallible is done. update the internal state.
   205  		s.index++
   206  
   207  		return &splitterSegment{
   208  			position:   position,
   209  			encryption: segEncryption,
   210  			encParams:  s.opts.Params,
   211  			contentKey: &contentKey,
   212  
   213  			maxSegmentSize: s.maxSegmentSize,
   214  			encTransformer: enc,
   215  			encBuf:         encBuf,
   216  		}, nil
   217  	}
   218  }