storj.io/uplink@v1.13.0/private/metaclient/movecopy.go (about)

     1  // Copyright (C) 2022 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package metaclient
     5  
     6  import (
     7  	"context"
     8  	"crypto/rand"
     9  	"strings"
    10  
    11  	"github.com/zeebo/errs"
    12  
    13  	"storj.io/common/encryption"
    14  	"storj.io/common/paths"
    15  	"storj.io/common/pb"
    16  	"storj.io/common/storj"
    17  )
    18  
    19  // EncryptedKeyAndNonce holds single segment encrypted key.
    20  type EncryptedKeyAndNonce struct {
    21  	Position          SegmentPosition
    22  	EncryptedKeyNonce storj.Nonce
    23  	EncryptedKey      []byte
    24  }
    25  
    26  // CopyObject atomically copies object to a different bucket or/and key. Source object version can be specified.
    27  func (db *DB) CopyObject(ctx context.Context, sourceBucket, sourceKey string, sourceVersion []byte, targetBucket, targetKey string) (_ *Object, err error) {
    28  	defer mon.Task()(&ctx)(&err)
    29  
    30  	err = validateMoveCopyInput(sourceBucket, sourceKey, targetBucket, targetKey)
    31  	if err != nil {
    32  		return nil, errs.Wrap(err)
    33  	}
    34  
    35  	sourceEncKey, err := encryption.EncryptPathWithStoreCipher(sourceBucket, paths.NewUnencrypted(sourceKey), db.encStore)
    36  	if err != nil {
    37  		return nil, errs.Wrap(err)
    38  	}
    39  
    40  	targetEncKey, err := encryption.EncryptPathWithStoreCipher(targetBucket, paths.NewUnencrypted(targetKey), db.encStore)
    41  	if err != nil {
    42  		return nil, errs.Wrap(err)
    43  	}
    44  
    45  	response, err := db.metainfo.BeginCopyObject(ctx, BeginCopyObjectParams{
    46  		Bucket:                []byte(sourceBucket),
    47  		EncryptedObjectKey:    []byte(sourceEncKey.Raw()),
    48  		Version:               sourceVersion,
    49  		NewBucket:             []byte(targetBucket),
    50  		NewEncryptedObjectKey: []byte(targetEncKey.Raw()),
    51  	})
    52  	if err != nil {
    53  		return nil, errs.Wrap(err)
    54  	}
    55  
    56  	oldDerivedKey, err := encryption.DeriveContentKey(sourceBucket, paths.NewUnencrypted(sourceKey), db.encStore)
    57  	if err != nil {
    58  		return nil, errs.Wrap(err)
    59  	}
    60  
    61  	newDerivedKey, err := encryption.DeriveContentKey(targetBucket, paths.NewUnencrypted(targetKey), db.encStore)
    62  	if err != nil {
    63  		return nil, errs.Wrap(err)
    64  	}
    65  
    66  	newMetadataEncryptedKey, newMetadataKeyNonce, err := db.reencryptMetadataKey(response.EncryptedMetadataKey, response.EncryptedMetadataKeyNonce, oldDerivedKey, newDerivedKey)
    67  	if err != nil {
    68  		return nil, errs.Wrap(err)
    69  	}
    70  
    71  	newKeys, err := db.reencryptKeys(response.SegmentKeys, oldDerivedKey, newDerivedKey)
    72  	if err != nil {
    73  		return nil, errs.Wrap(err)
    74  	}
    75  
    76  	obj, err := db.metainfo.FinishCopyObject(ctx, FinishCopyObjectParams{
    77  		StreamID:                     response.StreamID,
    78  		NewBucket:                    []byte(targetBucket),
    79  		NewEncryptedObjectKey:        []byte(targetEncKey.Raw()),
    80  		NewEncryptedMetadataKeyNonce: newMetadataKeyNonce,
    81  		NewEncryptedMetadataKey:      newMetadataEncryptedKey,
    82  		NewSegmentKeys:               newKeys,
    83  	})
    84  	if err != nil {
    85  		return nil, errs.Wrap(err)
    86  	}
    87  
    88  	info, err := db.ObjectFromRawObjectItem(ctx, targetBucket, targetKey, obj.Info)
    89  	if err != nil {
    90  		return nil, errs.Wrap(err)
    91  	}
    92  	return &info, nil
    93  }
    94  
    95  func (db *DB) reencryptMetadataKey(encryptedMetadataKey []byte, encryptedMetadataKeyNonce storj.Nonce, oldDerivedKey, newDerivedKey *storj.Key) ([]byte, storj.Nonce, error) {
    96  	if len(encryptedMetadataKey) == 0 {
    97  		return nil, storj.Nonce{}, nil
    98  	}
    99  
   100  	cipherSuite := db.encryptionParameters.CipherSuite
   101  
   102  	// decrypt old metadata key
   103  	metadataContentKey, err := encryption.DecryptKey(encryptedMetadataKey, cipherSuite, oldDerivedKey, &encryptedMetadataKeyNonce)
   104  	if err != nil {
   105  		return nil, storj.Nonce{}, errs.Wrap(err)
   106  	}
   107  
   108  	// encrypt metadata content key with new derived key and old nonce
   109  	newMetadataKeyNonce := encryptedMetadataKeyNonce
   110  	newMetadataEncryptedKey, err := encryption.EncryptKey(metadataContentKey, cipherSuite, newDerivedKey, &newMetadataKeyNonce)
   111  	if err != nil {
   112  		return nil, storj.Nonce{}, errs.Wrap(err)
   113  	}
   114  
   115  	return newMetadataEncryptedKey, newMetadataKeyNonce, nil
   116  }
   117  
   118  func (db *DB) reencryptKeys(keys []EncryptedKeyAndNonce, oldDerivedKey, newDerivedKey *storj.Key) ([]EncryptedKeyAndNonce, error) {
   119  	cipherSuite := db.encryptionParameters.CipherSuite
   120  
   121  	newKeys := make([]EncryptedKeyAndNonce, len(keys))
   122  	for i, oldKey := range keys {
   123  		// decrypt old key
   124  		contentKey, err := encryption.DecryptKey(oldKey.EncryptedKey, cipherSuite, oldDerivedKey, &oldKey.EncryptedKeyNonce)
   125  		if err != nil {
   126  			return nil, errs.Wrap(err)
   127  		}
   128  
   129  		// create new random nonce and encrypt
   130  		var newEncryptedKeyNonce storj.Nonce
   131  		// generate random nonce for encrypting the content key
   132  		_, err = rand.Read(newEncryptedKeyNonce[:])
   133  		if err != nil {
   134  			return nil, errs.Wrap(err)
   135  		}
   136  
   137  		newEncryptedKey, err := encryption.EncryptKey(contentKey, cipherSuite, newDerivedKey, &newEncryptedKeyNonce)
   138  		if err != nil {
   139  			return nil, errs.Wrap(err)
   140  		}
   141  
   142  		newKeys[i] = EncryptedKeyAndNonce{
   143  			Position:          oldKey.Position,
   144  			EncryptedKeyNonce: newEncryptedKeyNonce,
   145  			EncryptedKey:      newEncryptedKey,
   146  		}
   147  	}
   148  
   149  	return newKeys, nil
   150  }
   151  
   152  func convertKeys(input []*pb.EncryptedKeyAndNonce) []EncryptedKeyAndNonce {
   153  	keys := make([]EncryptedKeyAndNonce, len(input))
   154  	for i, key := range input {
   155  		keys[i] = EncryptedKeyAndNonce{
   156  			EncryptedKeyNonce: key.EncryptedKeyNonce,
   157  			EncryptedKey:      key.EncryptedKey,
   158  		}
   159  		if key.Position != nil {
   160  			keys[i].Position = SegmentPosition{
   161  				PartNumber: key.Position.PartNumber,
   162  				Index:      key.Position.Index,
   163  			}
   164  		}
   165  	}
   166  
   167  	return keys
   168  }
   169  
   170  func validateMoveCopyInput(oldbucket, oldkey, newbucket, newkey string) error {
   171  	switch {
   172  	case oldbucket == "":
   173  		return ErrNoBucket.New(oldbucket)
   174  	case oldkey == "":
   175  		return ErrNoPath.New(oldkey)
   176  	case strings.HasSuffix(oldkey, "/"):
   177  		return errs.New("oldkey cannot be a prefix")
   178  	case newbucket == "": // TODO should we make this error different
   179  		return ErrNoBucket.New(newbucket)
   180  	case newkey == "": // TODO should we make this error different
   181  		return ErrNoPath.New(newkey)
   182  	case strings.HasSuffix(newkey, "/"):
   183  		return errs.New("newkey cannot be a prefix")
   184  	}
   185  
   186  	return nil
   187  }