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 }