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

     1  // Copyright (C) 2019 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package metaclient
     5  
     6  import (
     7  	"context"
     8  	"crypto/rand"
     9  	"errors"
    10  	"strings"
    11  	"time"
    12  
    13  	"storj.io/common/base58"
    14  	"storj.io/common/encryption"
    15  	"storj.io/common/paths"
    16  	"storj.io/common/pb"
    17  	"storj.io/common/storj"
    18  )
    19  
    20  var contentTypeKey = "content-type"
    21  
    22  // Meta info about a segment.
    23  type Meta struct {
    24  	Modified   time.Time
    25  	Expiration time.Time
    26  	Size       int64
    27  	Data       []byte
    28  }
    29  
    30  // GetObjectIPs returns the IP addresses of the nodes which hold the object.
    31  func (db *DB) GetObjectIPs(ctx context.Context, bucket Bucket, key string) (_ *GetObjectIPsResponse, err error) {
    32  	defer mon.Task()(&ctx)(&err)
    33  
    34  	if bucket.Name == "" {
    35  		return nil, ErrNoBucket.New("")
    36  	}
    37  
    38  	if key == "" {
    39  		return nil, ErrNoPath.New("")
    40  	}
    41  
    42  	encPath, err := encryption.EncryptPathWithStoreCipher(bucket.Name, paths.NewUnencrypted(key), db.encStore)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	return db.metainfo.GetObjectIPs(ctx, GetObjectIPsParams{
    48  		Bucket:             []byte(bucket.Name),
    49  		EncryptedObjectKey: []byte(encPath.Raw()),
    50  	})
    51  }
    52  
    53  // CreateObject creates an uploading object and returns an interface for uploading Object information.
    54  func (db *DB) CreateObject(ctx context.Context, bucket, key string, createInfo *CreateObject) (object *MutableObject, err error) {
    55  	defer mon.Task()(&ctx)(&err)
    56  
    57  	if bucket == "" {
    58  		return nil, ErrNoBucket.New("")
    59  	}
    60  
    61  	if key == "" {
    62  		return nil, ErrNoPath.New("")
    63  	}
    64  
    65  	info := Object{
    66  		Bucket: Bucket{Name: bucket},
    67  		Path:   key,
    68  	}
    69  
    70  	if createInfo != nil {
    71  		info.Metadata = createInfo.Metadata
    72  		info.ContentType = createInfo.ContentType
    73  		info.Expires = createInfo.Expires
    74  		info.RedundancyScheme = createInfo.RedundancyScheme
    75  		info.EncryptionParameters = createInfo.EncryptionParameters
    76  	}
    77  
    78  	// TODO: autodetect content type from the path extension
    79  	// if info.ContentType == "" {}
    80  
    81  	return &MutableObject{
    82  		info: info,
    83  	}, nil
    84  }
    85  
    86  // ModifyObject modifies a committed object.
    87  func (db *DB) ModifyObject(ctx context.Context, bucket, key string) (object *MutableObject, err error) {
    88  	defer mon.Task()(&ctx)(&err)
    89  	return nil, errors.New("not implemented")
    90  }
    91  
    92  // UpdateObjectMetadata replaces the custom metadata for the object at the specific key with newMetadata.
    93  // Any existing custom metadata will be deleted.
    94  func (db *DB) UpdateObjectMetadata(ctx context.Context, bucket, key string, newMetadata map[string]string) (err error) {
    95  	defer mon.Task()(&ctx)(&err)
    96  
    97  	if bucket == "" {
    98  		return ErrNoBucket.New("")
    99  	}
   100  
   101  	if key == "" {
   102  		return ErrNoPath.New("")
   103  	}
   104  
   105  	encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	// TODO: check if we could avoid this round-trip to satellite
   111  	// At the moment, we need to get the object for two reason:
   112  	//   1. Retrieve the backward-compatibility metadata
   113  	//      (max segment size and last segment size)
   114  	//      and copy it to the new metadata.
   115  	//   2. Retrieve the object's encryption parameters
   116  	//      and use them for encrypting the new metadata.
   117  	objectInfo, err := db.metainfo.GetObject(ctx, GetObjectParams{
   118  		Bucket:                     []byte(bucket),
   119  		EncryptedObjectKey:         []byte(encPath.Raw()),
   120  		RedundancySchemePerSegment: true,
   121  	})
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	object, err := db.ObjectFromRawObjectItem(ctx, bucket, key, objectInfo)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	metadataBytes, err := pb.Marshal(&pb.SerializableMeta{
   132  		UserDefined: newMetadata,
   133  	})
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	streamInfo, err := pb.Marshal(&pb.StreamInfo{
   139  		SegmentsSize:    object.FixedSegmentSize,
   140  		LastSegmentSize: object.LastSegment.Size,
   141  		Metadata:        metadataBytes,
   142  	})
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	derivedKey, err := encryption.DeriveContentKey(bucket, paths.NewUnencrypted(key), db.encStore)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	var metadataKey storj.Key
   153  	// generate random key for encrypting the segment's content
   154  	_, err = rand.Read(metadataKey[:])
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	var encryptedKeyNonce storj.Nonce
   160  	// generate random nonce for encrypting the metadata key
   161  	_, err = rand.Read(encryptedKeyNonce[:])
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	encryptionParameters := objectInfo.EncryptionParameters
   167  	encryptedKey, err := encryption.EncryptKey(&metadataKey, encryptionParameters.CipherSuite, derivedKey, &encryptedKeyNonce)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	// encrypt metadata with the content encryption key and zero nonce.
   173  	encryptedStreamInfo, err := encryption.Encrypt(streamInfo, encryptionParameters.CipherSuite, &metadataKey, &storj.Nonce{})
   174  	if err != nil {
   175  		return err
   176  	}
   177  
   178  	// TODO should we commit StreamMeta or commit only encrypted StreamInfo
   179  	streamMetaBytes, err := pb.Marshal(&pb.StreamMeta{
   180  		EncryptedStreamInfo: encryptedStreamInfo,
   181  	})
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	return db.metainfo.UpdateObjectMetadata(ctx, UpdateObjectMetadataParams{
   187  		Bucket:                        []byte(bucket),
   188  		EncryptedObjectKey:            []byte(encPath.Raw()),
   189  		StreamID:                      object.Stream.ID,
   190  		EncryptedMetadata:             streamMetaBytes,
   191  		EncryptedMetadataEncryptedKey: encryptedKey,
   192  		EncryptedMetadataNonce:        encryptedKeyNonce,
   193  	})
   194  }
   195  
   196  // DeleteObject deletes an object from database.
   197  func (db *DB) DeleteObject(ctx context.Context, bucket, key string, version []byte) (_ Object, err error) {
   198  	defer mon.Task()(&ctx)(&err)
   199  
   200  	if bucket == "" {
   201  		return Object{}, ErrNoBucket.New("")
   202  	}
   203  
   204  	if len(key) == 0 {
   205  		return Object{}, ErrNoPath.New("")
   206  	}
   207  
   208  	encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore)
   209  	if err != nil {
   210  		return Object{}, err
   211  	}
   212  
   213  	object, err := db.metainfo.BeginDeleteObject(ctx, BeginDeleteObjectParams{
   214  		Bucket:             []byte(bucket),
   215  		EncryptedObjectKey: []byte(encPath.Raw()),
   216  		Version:            version,
   217  	})
   218  	if err != nil {
   219  		return Object{}, err
   220  	}
   221  
   222  	return db.ObjectFromRawObjectItem(ctx, bucket, key, object)
   223  }
   224  
   225  // ModifyPendingObject creates an interface for updating a partially uploaded object.
   226  func (db *DB) ModifyPendingObject(ctx context.Context, bucket, key string) (object *MutableObject, err error) {
   227  	defer mon.Task()(&ctx)(&err)
   228  	return nil, errors.New("not implemented")
   229  }
   230  
   231  // ListPendingObjects lists pending objects in bucket based on the ListOptions.
   232  func (db *DB) ListPendingObjects(ctx context.Context, bucket string, options ListOptions) (list ObjectList, err error) {
   233  	defer mon.Task()(&ctx)(&err)
   234  	return ObjectList{}, errors.New("not implemented")
   235  }
   236  
   237  // ListPendingObjectStreams lists streams for a specific pending object key.
   238  func (db *DB) ListPendingObjectStreams(ctx context.Context, bucket string, options ListOptions) (list ObjectList, err error) {
   239  	defer mon.Task()(&ctx)(&err)
   240  
   241  	if bucket == "" {
   242  		return ObjectList{}, ErrNoBucket.New("")
   243  	}
   244  
   245  	var startAfter string
   246  	switch options.Direction {
   247  	// TODO for now we are supporting only After
   248  	// case Forward:
   249  	// 	// forward lists forwards from cursor, including cursor
   250  	// 	startAfter = keyBefore(options.Cursor)
   251  	case After:
   252  		// after lists forwards from cursor, without cursor
   253  		startAfter = options.Cursor
   254  	default:
   255  		return ObjectList{}, errClass.New("invalid direction %d", options.Direction)
   256  	}
   257  
   258  	pi, err := encryption.GetPrefixInfo(bucket, paths.NewUnencrypted(options.Prefix), db.encStore)
   259  	if err != nil {
   260  		return ObjectList{}, errClass.Wrap(err)
   261  	}
   262  
   263  	resp, err := db.metainfo.ListPendingObjectStreams(ctx, ListPendingObjectStreamsParams{
   264  		Bucket:             []byte(bucket),
   265  		EncryptedObjectKey: []byte(pi.PathEnc.Raw()),
   266  		EncryptedCursor:    []byte(startAfter),
   267  		Limit:              int32(options.Limit),
   268  	})
   269  	if err != nil {
   270  		return ObjectList{}, errClass.Wrap(err)
   271  	}
   272  
   273  	objectsList, err := db.pendingObjectsFromRawObjectList(ctx, resp.Items, pi, startAfter)
   274  	if err != nil {
   275  		return ObjectList{}, errClass.Wrap(err)
   276  	}
   277  
   278  	return ObjectList{
   279  		Bucket: bucket,
   280  		Prefix: options.Prefix,
   281  		More:   resp.More,
   282  		Items:  objectsList,
   283  	}, nil
   284  }
   285  
   286  func (db *DB) pendingObjectsFromRawObjectList(ctx context.Context, items []RawObjectListItem, pi *encryption.PrefixInfo, startAfter string) (objectList []Object, err error) {
   287  	objectList = make([]Object, 0, len(items))
   288  
   289  	for _, item := range items {
   290  		stream, streamMeta, err := db.typedDecryptStreamInfo(ctx, pi.Bucket, pi.PathUnenc,
   291  			item.EncryptedMetadata,
   292  			item.EncryptedMetadataEncryptedKey,
   293  			item.EncryptedMetadataNonce,
   294  		)
   295  		if err != nil {
   296  			// skip items that cannot be decrypted
   297  			if encryption.ErrDecryptFailed.Has(err) {
   298  				continue
   299  			}
   300  			return nil, errClass.Wrap(err)
   301  		}
   302  
   303  		object, err := db.objectFromRawObjectListItem(pi.Bucket, pi.PathUnenc.Raw(), item, stream, streamMeta)
   304  		if err != nil {
   305  			return nil, errClass.Wrap(err)
   306  		}
   307  
   308  		objectList = append(objectList, object)
   309  	}
   310  
   311  	return objectList, nil
   312  }
   313  
   314  // ListObjects lists objects in bucket based on the ListOptions.
   315  func (db *DB) ListObjects(ctx context.Context, bucket string, options ListOptions) (list ObjectList, err error) {
   316  	defer mon.Task()(&ctx)(&err)
   317  
   318  	if bucket == "" {
   319  		return ObjectList{}, ErrNoBucket.New("")
   320  	}
   321  
   322  	if options.Prefix != "" && !strings.HasSuffix(options.Prefix, "/") {
   323  		return ObjectList{}, errClass.New("prefix should end with slash")
   324  	}
   325  
   326  	var startAfter string
   327  	switch options.Direction {
   328  	// TODO for now we are supporting only After
   329  	// case Forward:
   330  	// 	// forward lists forwards from cursor, including cursor
   331  	// 	startAfter = keyBefore(options.Cursor)
   332  	case After:
   333  		// after lists forwards from cursor, without cursor
   334  		startAfter = options.Cursor
   335  	default:
   336  		return ObjectList{}, errClass.New("invalid direction %d", options.Direction)
   337  	}
   338  
   339  	// TODO: we should let libuplink users be able to determine what metadata fields they request as well
   340  	// metaFlags := meta.All
   341  	// if db.pathCipher(bucket) == EncNull || db.pathCipher(bucket) == EncNullBase64URL {
   342  	// 	metaFlags = meta.None
   343  	// }
   344  
   345  	// TODO use flags with listing
   346  	// if metaFlags&meta.Size != 0 {
   347  	// Calculating the stream's size require also the user-defined metadata,
   348  	// where stream store keeps info about the number of segments and their size.
   349  	// metaFlags |= meta.UserDefined
   350  	// }
   351  
   352  	pi, err := encryption.GetPrefixInfo(bucket, paths.NewUnencrypted(options.Prefix), db.encStore)
   353  	if err != nil {
   354  		return ObjectList{}, errClass.Wrap(err)
   355  	}
   356  
   357  	startAfter, err = encryption.EncryptPathRaw(startAfter, pi.Cipher, &pi.ParentKey)
   358  	if err != nil {
   359  		return ObjectList{}, errClass.Wrap(err)
   360  	}
   361  
   362  	startAfterEnc := []byte(startAfter)
   363  	if len(options.CursorEnc) > 0 {
   364  		startAfterEnc = options.CursorEnc
   365  	}
   366  	versionCursor := options.VersionCursor
   367  
   368  	var m bool
   369  	var objectsList []Object
   370  	// Keep looking until we find an object we can decrypt or we run out of objects
   371  	for {
   372  		items, more, err := db.metainfo.ListObjects(ctx, ListObjectsParams{
   373  			Bucket:                []byte(bucket),
   374  			EncryptedPrefix:       []byte(pi.ParentEnc.Raw()),
   375  			EncryptedCursor:       startAfterEnc,
   376  			Limit:                 int32(options.Limit),
   377  			IncludeCustomMetadata: options.IncludeCustomMetadata,
   378  			IncludeSystemMetadata: options.IncludeSystemMetadata,
   379  			Recursive:             options.Recursive,
   380  			Status:                options.Status,
   381  			IncludeAllVersions:    options.IncludeAllVersions,
   382  			VersionCursor:         versionCursor,
   383  		})
   384  		if err != nil {
   385  			return ObjectList{}, errClass.Wrap(err)
   386  		}
   387  		m = more
   388  
   389  		objectsList, err = db.objectsFromRawObjectList(ctx, items, pi)
   390  		if err != nil {
   391  			return ObjectList{}, errClass.Wrap(err)
   392  		}
   393  
   394  		if len(items) > 0 {
   395  			startAfterEnc = items[len(items)-1].EncryptedObjectKey
   396  			versionCursor = items[len(items)-1].Version
   397  		}
   398  
   399  		if len(objectsList) != 0 || !more {
   400  			break
   401  		}
   402  	}
   403  
   404  	return ObjectList{
   405  		Bucket:        bucket,
   406  		Prefix:        options.Prefix,
   407  		More:          m,
   408  		Items:         objectsList,
   409  		Cursor:        startAfterEnc,
   410  		VersionCursor: versionCursor,
   411  	}, nil
   412  }
   413  
   414  func (db *DB) objectsFromRawObjectList(ctx context.Context, items []RawObjectListItem, pi *encryption.PrefixInfo) (objectList []Object, err error) {
   415  	objectList = make([]Object, 0, len(items))
   416  
   417  	for _, item := range items {
   418  		unencItem, err := encryption.DecryptPathRaw(string(item.EncryptedObjectKey), pi.Cipher, &pi.ParentKey)
   419  		if err != nil {
   420  			// skip items that cannot be decrypted
   421  			if encryption.ErrDecryptFailed.Has(err) {
   422  				continue
   423  			}
   424  			return nil, errClass.Wrap(err)
   425  		}
   426  
   427  		var unencKey paths.Unencrypted
   428  		if pi.ParentEnc.Valid() {
   429  			unencKey = paths.NewUnencrypted(pi.ParentUnenc.Raw() + "/" + unencItem)
   430  		} else {
   431  			unencKey = paths.NewUnencrypted(unencItem)
   432  		}
   433  
   434  		stream, streamMeta, err := db.typedDecryptStreamInfo(ctx, pi.Bucket, unencKey,
   435  			item.EncryptedMetadata,
   436  			item.EncryptedMetadataEncryptedKey,
   437  			item.EncryptedMetadataNonce,
   438  		)
   439  		if err != nil {
   440  			// skip items that cannot be decrypted
   441  			if encryption.ErrDecryptFailed.Has(err) {
   442  				continue
   443  			}
   444  			return nil, errClass.Wrap(err)
   445  		}
   446  
   447  		object, err := db.objectFromRawObjectListItem(pi.Bucket, unencItem, item, stream, streamMeta)
   448  		if err != nil {
   449  			return nil, errClass.Wrap(err)
   450  		}
   451  
   452  		objectList = append(objectList, object)
   453  	}
   454  
   455  	return objectList, nil
   456  }
   457  
   458  // DownloadOptions contains additional options for downloading.
   459  type DownloadOptions struct {
   460  	Range StreamRange
   461  }
   462  
   463  // DownloadInfo contains response for DownloadObject.
   464  type DownloadInfo struct {
   465  	Object             Object
   466  	EncPath            paths.Encrypted
   467  	DownloadedSegments []DownloadSegmentWithRSResponse
   468  	ListSegments       ListSegmentsResponse
   469  	Range              StreamRange
   470  }
   471  
   472  // DownloadObject gets object information, lists segments and downloads the first segment.
   473  func (db *DB) DownloadObject(ctx context.Context, bucket, key string, version []byte, options DownloadOptions) (info DownloadInfo, err error) {
   474  	defer mon.Task()(&ctx)(&err)
   475  
   476  	if bucket == "" {
   477  		return DownloadInfo{}, ErrNoBucket.New("")
   478  	}
   479  	if key == "" {
   480  		return DownloadInfo{}, ErrNoPath.New("")
   481  	}
   482  
   483  	encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore)
   484  	if err != nil {
   485  		return DownloadInfo{}, err
   486  	}
   487  
   488  	resp, err := db.metainfo.DownloadObject(ctx, DownloadObjectParams{
   489  		Bucket:             []byte(bucket),
   490  		EncryptedObjectKey: []byte(encPath.Raw()),
   491  		Version:            version,
   492  		Range:              options.Range,
   493  	})
   494  	if err != nil {
   495  		return DownloadInfo{}, err
   496  	}
   497  
   498  	info, err = db.newDownloadInfo(ctx, bucket, key, encPath, resp, options.Range)
   499  	if err != nil {
   500  		return DownloadInfo{}, err
   501  	}
   502  
   503  	for info.ListSegments.More {
   504  		var cursor SegmentPosition
   505  		if len(info.ListSegments.Items) > 0 {
   506  			last := info.ListSegments.Items[len(info.ListSegments.Items)-1]
   507  			cursor = last.Position
   508  		}
   509  
   510  		result, err := db.ListSegments(ctx, ListSegmentsParams{
   511  			StreamID: info.Object.Stream.ID,
   512  			Cursor:   cursor,
   513  			Range:    info.Range,
   514  		})
   515  		if err != nil {
   516  			return DownloadInfo{}, err
   517  		}
   518  
   519  		info.ListSegments.Items = append(info.ListSegments.Items, result.Items...)
   520  		info.ListSegments.More = result.More
   521  	}
   522  
   523  	return info, nil
   524  }
   525  
   526  func (db *DB) newDownloadInfo(ctx context.Context, bucket, key string, encPath paths.Encrypted, response DownloadObjectResponse, streamRange StreamRange) (DownloadInfo, error) {
   527  	object, err := db.ObjectFromRawObjectItem(ctx, bucket, key, response.Object)
   528  	if err != nil {
   529  		return DownloadInfo{}, err
   530  	}
   531  
   532  	return DownloadInfo{
   533  		Object:             object,
   534  		EncPath:            encPath,
   535  		DownloadedSegments: response.DownloadedSegments,
   536  		ListSegments:       response.ListSegments,
   537  		Range:              streamRange.Normalize(object.Size),
   538  	}, nil
   539  }
   540  
   541  // ListSegments returns paged segments list.
   542  func (db *DB) ListSegments(ctx context.Context, params ListSegmentsParams) (response ListSegmentsResponse, err error) {
   543  	result, err := db.metainfo.ListSegments(ctx, params)
   544  	if err != nil {
   545  		return ListSegmentsResponse{}, err
   546  	}
   547  	return result, nil
   548  }
   549  
   550  // GetObject returns information about an object.
   551  func (db *DB) GetObject(ctx context.Context, bucket, key string, version []byte) (info Object, err error) {
   552  	defer mon.Task()(&ctx)(&err)
   553  
   554  	if bucket == "" {
   555  		return Object{}, ErrNoBucket.New("")
   556  	}
   557  
   558  	if key == "" {
   559  		return Object{}, ErrNoPath.New("")
   560  	}
   561  
   562  	encPath, err := encryption.EncryptPathWithStoreCipher(bucket, paths.NewUnencrypted(key), db.encStore)
   563  	if err != nil {
   564  		return Object{}, err
   565  	}
   566  
   567  	objectInfo, err := db.metainfo.GetObject(ctx, GetObjectParams{
   568  		Bucket:                     []byte(bucket),
   569  		EncryptedObjectKey:         []byte(encPath.Raw()),
   570  		Version:                    version,
   571  		RedundancySchemePerSegment: true,
   572  	})
   573  	if err != nil {
   574  		return Object{}, err
   575  	}
   576  
   577  	return db.ObjectFromRawObjectItem(ctx, bucket, key, objectInfo)
   578  }
   579  
   580  // CommitObject commits an object.
   581  func (db *DB) CommitObject(ctx context.Context, bucket, key, uploadID string, customMetadata map[string]string, encryptionParameters storj.EncryptionParameters) (info Object, err error) {
   582  	defer mon.Task()(&ctx)(&err)
   583  
   584  	switch {
   585  	case bucket == "":
   586  		return Object{}, ErrNoBucket.New("")
   587  	case key == "":
   588  		return Object{}, ErrNoPath.New("")
   589  	case uploadID == "":
   590  		return Object{}, ErrUploadIDInvalid.New("")
   591  	}
   592  
   593  	decodedStreamID, version, err := base58.CheckDecode(uploadID)
   594  	if err != nil || version != 1 {
   595  		return Object{}, ErrUploadIDInvalid.New("")
   596  	}
   597  
   598  	id, err := storj.StreamIDFromBytes(decodedStreamID)
   599  	if err != nil {
   600  		return Object{}, err
   601  	}
   602  
   603  	commitObjParams, err := db.fillMetadata(bucket, key, id, customMetadata, encryptionParameters)
   604  	if err != nil {
   605  		return Object{}, err
   606  	}
   607  
   608  	response, err := db.metainfo.CommitObjectWithResponse(ctx, commitObjParams)
   609  	if err != nil {
   610  		return Object{}, err
   611  	}
   612  
   613  	return db.ObjectFromRawObjectItem(ctx, bucket, key, response.Object)
   614  }
   615  
   616  func (db *DB) fillMetadata(bucket, key string, id storj.StreamID, metadata map[string]string, encryptionParameters storj.EncryptionParameters) (CommitObjectParams, error) {
   617  	commitObjParams := CommitObjectParams{StreamID: id}
   618  	if len(metadata) == 0 {
   619  		return commitObjParams, nil
   620  	}
   621  
   622  	clone := make(map[string]string, len(metadata))
   623  	for k, v := range metadata {
   624  		clone[k] = v
   625  	}
   626  
   627  	metadataBytes, err := pb.Marshal(&pb.SerializableMeta{
   628  		UserDefined: clone,
   629  	})
   630  	if err != nil {
   631  		return CommitObjectParams{}, err
   632  	}
   633  
   634  	streamInfo, err := pb.Marshal(&pb.StreamInfo{
   635  		Metadata: metadataBytes,
   636  	})
   637  	if err != nil {
   638  		return CommitObjectParams{}, err
   639  	}
   640  
   641  	derivedKey, err := encryption.DeriveContentKey(bucket, paths.NewUnencrypted(key), db.encStore)
   642  	if err != nil {
   643  		return CommitObjectParams{}, err
   644  	}
   645  
   646  	var metadataKey storj.Key
   647  	// generate random key for encrypting the segment's content
   648  	_, err = rand.Read(metadataKey[:])
   649  	if err != nil {
   650  		return CommitObjectParams{}, err
   651  	}
   652  
   653  	var encryptedKeyNonce storj.Nonce
   654  	// generate random nonce for encrypting the metadata key
   655  	_, err = rand.Read(encryptedKeyNonce[:])
   656  	if err != nil {
   657  		return CommitObjectParams{}, err
   658  	}
   659  
   660  	encryptedKey, err := encryption.EncryptKey(&metadataKey, encryptionParameters.CipherSuite, derivedKey, &encryptedKeyNonce)
   661  	if err != nil {
   662  		return CommitObjectParams{}, err
   663  	}
   664  
   665  	// encrypt metadata with the content encryption key and zero nonce.
   666  	encryptedStreamInfo, err := encryption.Encrypt(streamInfo, encryptionParameters.CipherSuite, &metadataKey, &storj.Nonce{})
   667  	if err != nil {
   668  		return CommitObjectParams{}, err
   669  	}
   670  
   671  	// TODO should we commit StreamMeta or commit only encrypted StreamInfo
   672  	streamMetaBytes, err := pb.Marshal(&pb.StreamMeta{
   673  		EncryptedStreamInfo: encryptedStreamInfo,
   674  	})
   675  	if err != nil {
   676  		return CommitObjectParams{}, err
   677  	}
   678  
   679  	commitObjParams.EncryptedMetadataEncryptedKey = encryptedKey
   680  	commitObjParams.EncryptedMetadataNonce = encryptedKeyNonce
   681  	commitObjParams.EncryptedMetadata = streamMetaBytes
   682  
   683  	return commitObjParams, nil
   684  }
   685  
   686  // ObjectFromRawObjectItem converts RawObjectItem into storj.Object struct.
   687  func (db *DB) ObjectFromRawObjectItem(ctx context.Context, bucket, key string, objectInfo RawObjectItem) (Object, error) {
   688  	if objectInfo.Bucket == "" { // zero objectInfo
   689  		return Object{}, nil
   690  	}
   691  
   692  	object := Object{
   693  		Version:        version(objectInfo.Status, objectInfo.Version),
   694  		Bucket:         Bucket{Name: bucket},
   695  		Path:           key,
   696  		IsPrefix:       objectInfo.IsPrefix(),
   697  		IsDeleteMarker: objectInfo.IsDeleteMarker(),
   698  
   699  		Created:  objectInfo.Created, // TODO: use correct field
   700  		Modified: objectInfo.Created, // TODO: use correct field
   701  		Expires:  objectInfo.Expires, // TODO: use correct field
   702  
   703  		Retention: objectInfo.Retention,
   704  
   705  		Stream: Stream{
   706  			ID: objectInfo.StreamID,
   707  
   708  			Size: objectInfo.PlainSize,
   709  
   710  			RedundancyScheme:     objectInfo.RedundancyScheme,
   711  			EncryptionParameters: objectInfo.EncryptionParameters,
   712  		},
   713  	}
   714  
   715  	streamInfo, streamMeta, err := db.typedDecryptStreamInfo(ctx, bucket, paths.NewUnencrypted(key),
   716  		objectInfo.EncryptedMetadata,
   717  		objectInfo.EncryptedMetadataEncryptedKey,
   718  		objectInfo.EncryptedMetadataNonce,
   719  	)
   720  	if err != nil {
   721  		return Object{}, err
   722  	}
   723  
   724  	if object.Stream.EncryptionParameters.CipherSuite == storj.EncUnspecified {
   725  		object.Stream.EncryptionParameters = storj.EncryptionParameters{
   726  			CipherSuite: storj.CipherSuite(streamMeta.EncryptionType),
   727  			BlockSize:   streamMeta.EncryptionBlockSize,
   728  		}
   729  	}
   730  	if streamMeta.LastSegmentMeta != nil {
   731  		var nonce storj.Nonce
   732  		copy(nonce[:], streamMeta.LastSegmentMeta.KeyNonce)
   733  
   734  		object.Stream.LastSegment = LastSegment{
   735  			EncryptedKeyNonce: nonce,
   736  			EncryptedKey:      streamMeta.LastSegmentMeta.EncryptedKey,
   737  		}
   738  	}
   739  
   740  	err = updateObjectWithStream(&object, streamInfo, streamMeta)
   741  	if err != nil {
   742  		return Object{}, err
   743  	}
   744  
   745  	return object, nil
   746  }
   747  
   748  func (db *DB) objectFromRawObjectListItem(bucket string, path storj.Path, listItem RawObjectListItem, stream *pb.StreamInfo, streamMeta pb.StreamMeta) (Object, error) {
   749  	object := Object{
   750  		Version:        listItem.Version,
   751  		Bucket:         Bucket{Name: bucket},
   752  		Path:           path,
   753  		IsPrefix:       listItem.IsPrefix,
   754  		IsDeleteMarker: listItem.IsDeleteMarker(),
   755  
   756  		Created:  listItem.CreatedAt, // TODO: use correct field
   757  		Modified: listItem.CreatedAt, // TODO: use correct field
   758  		Expires:  listItem.ExpiresAt,
   759  
   760  		Stream: Stream{
   761  			Size: listItem.PlainSize,
   762  		},
   763  	}
   764  
   765  	object.Stream.ID = listItem.StreamID
   766  
   767  	err := updateObjectWithStream(&object, stream, streamMeta)
   768  	if err != nil {
   769  		return Object{}, err
   770  	}
   771  
   772  	return object, nil
   773  }
   774  
   775  func updateObjectWithStream(object *Object, stream *pb.StreamInfo, streamMeta pb.StreamMeta) error {
   776  	if stream == nil {
   777  		return nil
   778  	}
   779  
   780  	serializableMeta := pb.SerializableMeta{}
   781  	err := pb.Unmarshal(stream.Metadata, &serializableMeta)
   782  	if err != nil {
   783  		return err
   784  	}
   785  
   786  	// ensure that the map is not nil
   787  	if serializableMeta.UserDefined == nil {
   788  		serializableMeta.UserDefined = map[string]string{}
   789  	}
   790  
   791  	_, found := serializableMeta.UserDefined[contentTypeKey]
   792  	if !found && serializableMeta.ContentType != "" {
   793  		serializableMeta.UserDefined[contentTypeKey] = serializableMeta.ContentType
   794  	}
   795  
   796  	segmentCount := streamMeta.NumberOfSegments
   797  	object.Metadata = serializableMeta.UserDefined
   798  
   799  	if object.Stream.Size == 0 {
   800  		object.Stream.Size = ((segmentCount - 1) * stream.SegmentsSize) + stream.LastSegmentSize
   801  	}
   802  	object.Stream.SegmentCount = segmentCount
   803  	object.Stream.FixedSegmentSize = stream.SegmentsSize
   804  	object.Stream.LastSegment.Size = stream.LastSegmentSize
   805  
   806  	return nil
   807  }
   808  
   809  // MutableObject is for creating an object stream.
   810  type MutableObject struct {
   811  	info Object
   812  }
   813  
   814  // Info gets the current information about the object.
   815  func (object *MutableObject) Info() Object { return object.info }
   816  
   817  // CreateStream creates a new stream for the object.
   818  func (object *MutableObject) CreateStream(ctx context.Context) (_ *MutableStream, err error) {
   819  	defer mon.Task()(&ctx)(&err)
   820  	return &MutableStream{
   821  		info: object.info,
   822  	}, nil
   823  }
   824  
   825  // CreateDynamicStream creates a new dynamic stream for the object.
   826  func (object *MutableObject) CreateDynamicStream(ctx context.Context, metadata SerializableMeta, expires time.Time) (_ *MutableStream, err error) {
   827  	defer mon.Task()(&ctx)(&err)
   828  	return &MutableStream{
   829  		info: object.info,
   830  
   831  		dynamic:         true,
   832  		dynamicMetadata: metadata,
   833  		dynamicExpires:  expires,
   834  	}, nil
   835  }
   836  
   837  // typedDecryptStreamInfo decrypts stream info.
   838  func (db *DB) typedDecryptStreamInfo(ctx context.Context, bucket string, unencryptedKey paths.Unencrypted,
   839  	streamMetaBytes, metadataKey []byte, metadataNonce storj.Nonce) (_ *pb.StreamInfo, _ pb.StreamMeta, err error) {
   840  	defer mon.Task()(&ctx)(&err)
   841  
   842  	streamMeta := pb.StreamMeta{}
   843  	err = pb.Unmarshal(streamMetaBytes, &streamMeta)
   844  	if err != nil {
   845  		return nil, pb.StreamMeta{}, err
   846  	}
   847  
   848  	if db.encStore.EncryptionBypass {
   849  		return nil, streamMeta, nil
   850  	}
   851  
   852  	derivedKey, err := encryption.DeriveContentKey(bucket, unencryptedKey, db.encStore)
   853  	if err != nil {
   854  		return nil, pb.StreamMeta{}, err
   855  	}
   856  
   857  	cipher := storj.CipherSuite(streamMeta.EncryptionType)
   858  	encryptedKey, keyNonce := getEncryptedKeyAndNonce(metadataKey, metadataNonce, streamMeta.LastSegmentMeta)
   859  	contentKey, err := encryption.DecryptKey(encryptedKey, cipher, derivedKey, keyNonce)
   860  	if err != nil {
   861  		return nil, pb.StreamMeta{}, err
   862  	}
   863  
   864  	// decrypt metadata with the content encryption key and zero nonce
   865  	streamInfo, err := encryption.Decrypt(streamMeta.EncryptedStreamInfo, cipher, contentKey, &storj.Nonce{})
   866  	if err != nil {
   867  		return nil, pb.StreamMeta{}, err
   868  	}
   869  
   870  	var stream pb.StreamInfo
   871  	if err := pb.Unmarshal(streamInfo, &stream); err != nil {
   872  		return nil, pb.StreamMeta{}, err
   873  	}
   874  
   875  	return &stream, streamMeta, nil
   876  }
   877  
   878  // getEncryptedKeyAndNonce returns key and nonce directly if exists, otherwise try to get them from SegmentMeta.
   879  func getEncryptedKeyAndNonce(metadataKey []byte, metadataNonce storj.Nonce, m *pb.SegmentMeta) (storj.EncryptedPrivateKey, *storj.Nonce) {
   880  	if len(metadataKey) > 0 {
   881  		return storj.EncryptedPrivateKey(metadataKey), &metadataNonce
   882  	}
   883  
   884  	if m == nil {
   885  		return nil, nil
   886  	}
   887  
   888  	var nonce storj.Nonce
   889  	copy(nonce[:], m.KeyNonce)
   890  
   891  	return m.EncryptedKey, &nonce
   892  }
   893  
   894  func version(status int32, version []byte) []byte {
   895  	if status == int32(pb.Object_COMMITTED_VERSIONED) || status == int32(pb.Object_DELETE_MARKER_VERSIONED) {
   896  		return version
   897  	}
   898  	return nil
   899  }