storj/satellite/orders/signer.go
Egon Elbre 267506bb20 satellite/metabase: move package one level higher
metabase has become a central concept and it's more suitable for it to
be directly nested under satellite rather than being part of metainfo.

metainfo is going to be the "endpoint" logic for handling requests.

Change-Id: I53770d6761ac1e9a1283b5aa68f471b21e784198
2021-04-21 15:54:22 +03:00

197 lines
6.8 KiB
Go

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package orders
import (
"context"
"crypto/rand"
"encoding/binary"
"time"
"github.com/zeebo/errs"
"storj.io/common/pb"
"storj.io/common/signing"
"storj.io/common/storj"
"storj.io/storj/satellite/internalpb"
"storj.io/storj/satellite/metabase"
)
// ErrSigner is default error class for Signer.
var ErrSigner = errs.Class("signer")
// Signer implements signing of order limits.
type Signer struct {
// TODO: should this be a ref to the necessary pieces instead of the service?
Service *Service
Bucket metabase.BucketLocation
// TODO: use a Template pb.OrderLimit here?
RootPieceID storj.PieceID
PieceExpiration time.Time
OrderCreation time.Time
OrderExpiration time.Time
PublicKey storj.PiecePublicKey
PrivateKey storj.PiecePrivateKey
Serial storj.SerialNumber
Action pb.PieceAction
Limit int64
EncryptedMetadataKeyID []byte
EncryptedMetadata []byte
AddressedLimits []*pb.AddressedOrderLimit
}
// createSerial creates a timestamped serial number.
func createSerial(orderExpiration time.Time) (_ storj.SerialNumber, err error) {
var serial storj.SerialNumber
binary.BigEndian.PutUint64(serial[0:8], uint64(orderExpiration.Unix()))
_, err = rand.Read(serial[8:])
if err != nil {
return storj.SerialNumber{}, ErrSigner.Wrap(err)
}
return serial, nil
}
// NewSigner creates an order limit signer.
func NewSigner(service *Service, rootPieceID storj.PieceID, pieceExpiration time.Time, orderCreation time.Time, limit int64, action pb.PieceAction, bucket metabase.BucketLocation) (*Signer, error) {
signer := &Signer{}
signer.Service = service
signer.Bucket = bucket
signer.RootPieceID = rootPieceID
signer.PieceExpiration = pieceExpiration
signer.OrderCreation = orderCreation
signer.OrderExpiration = orderCreation.Add(service.orderExpiration)
var err error
signer.PublicKey, signer.PrivateKey, err = storj.NewPieceKey()
if err != nil {
return nil, ErrSigner.Wrap(err)
}
signer.Serial, err = createSerial(signer.OrderExpiration)
if err != nil {
return nil, ErrSigner.Wrap(err)
}
signer.Action = action
signer.Limit = limit
return signer, nil
}
// NewSignerGet creates a new signer for get orders.
func NewSignerGet(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, limit int64, bucket metabase.BucketLocation) (*Signer, error) {
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, limit, pb.PieceAction_GET, bucket)
}
// NewSignerPut creates a new signer for put orders.
func NewSignerPut(service *Service, pieceExpiration time.Time, orderCreation time.Time, limit int64, bucket metabase.BucketLocation) (*Signer, error) {
rootPieceID := storj.NewPieceID()
return NewSigner(service, rootPieceID, pieceExpiration, orderCreation, limit, pb.PieceAction_PUT, bucket)
}
// NewSignerDelete creates a new signer for delete orders.
func NewSignerDelete(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, bucket metabase.BucketLocation) (*Signer, error) {
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, 0, pb.PieceAction_DELETE, bucket)
}
// NewSignerRepairGet creates a new signer for get repair orders.
func NewSignerRepairGet(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, pieceSize int64, bucket metabase.BucketLocation) (*Signer, error) {
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, pieceSize, pb.PieceAction_GET_REPAIR, bucket)
}
// NewSignerRepairPut creates a new signer for put repair orders.
func NewSignerRepairPut(service *Service, rootPieceID storj.PieceID, pieceExpiration time.Time, orderCreation time.Time, pieceSize int64, bucket metabase.BucketLocation) (*Signer, error) {
return NewSigner(service, rootPieceID, pieceExpiration, orderCreation, pieceSize, pb.PieceAction_PUT_REPAIR, bucket)
}
// NewSignerAudit creates a new signer for audit orders.
func NewSignerAudit(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, pieceSize int64, bucket metabase.BucketLocation) (*Signer, error) {
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, pieceSize, pb.PieceAction_GET_AUDIT, bucket)
}
// NewSignerGracefulExit creates a new signer for graceful exit orders.
func NewSignerGracefulExit(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, shareSize int32, bucket metabase.BucketLocation) (*Signer, error) {
// TODO: we're using zero time.Time for piece expiration for some reason.
// TODO: we're using `PUT_REPAIR` here even though we should be using `PUT`, such
// that the storage node cannot distinguish between requests. We can't use `PUT`
// because we don't want to charge bucket owners for graceful exit bandwidth, and
// we can't use `PUT_GRACEFUL_EXIT` because storagenode will only accept upload
// orders with `PUT` or `PUT_REPAIR` as the action. we also don't have a bunch of
// supporting code/tables to aggregate `PUT_GRACEFUL_EXIT` bandwidth into our rollups
// and stuff. so, for now, we just use `PUT_REPAIR` because it's the least bad of
// our options. this should be fixed.
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, int64(shareSize), pb.PieceAction_PUT_REPAIR, bucket)
}
// Sign signs an order limit for the specified node.
func (signer *Signer) Sign(ctx context.Context, node storj.NodeURL, pieceNum int32) (_ *pb.AddressedOrderLimit, err error) {
defer mon.Task()(&ctx)(&err)
if len(signer.EncryptedMetadata) == 0 {
encryptionKey := signer.Service.encryptionKeys.Default
if encryptionKey.IsZero() {
return nil, ErrSigner.New("default encryption key is missing")
}
encrypted, err := encryptionKey.EncryptMetadata(
signer.Serial,
&internalpb.OrderLimitMetadata{
CompactProjectBucketPrefix: signer.Bucket.CompactPrefix(),
},
)
if err != nil {
return nil, ErrSigner.Wrap(err)
}
signer.EncryptedMetadataKeyID = encryptionKey.ID[:]
signer.EncryptedMetadata = encrypted
}
limit := &pb.OrderLimit{
SerialNumber: signer.Serial,
SatelliteId: signer.Service.satellite.ID(),
UplinkPublicKey: signer.PublicKey,
StorageNodeId: node.ID,
PieceId: signer.RootPieceID.Derive(node.ID, pieceNum),
Limit: signer.Limit,
Action: signer.Action,
PieceExpiration: signer.PieceExpiration,
OrderCreation: signer.OrderCreation,
OrderExpiration: signer.OrderExpiration,
EncryptedMetadataKeyId: signer.EncryptedMetadataKeyID,
EncryptedMetadata: signer.EncryptedMetadata,
}
signedLimit, err := signing.SignOrderLimit(ctx, signer.Service.satellite, limit)
if err != nil {
return nil, ErrSigner.Wrap(err)
}
addressedLimit := &pb.AddressedOrderLimit{
Limit: signedLimit,
StorageNodeAddress: &pb.NodeAddress{
Address: node.Address,
},
}
signer.AddressedLimits = append(signer.AddressedLimits, addressedLimit)
return addressedLimit, nil
}