-
Notifications
You must be signed in to change notification settings - Fork 387
/
signer.go
196 lines (153 loc) · 6.84 KB
/
signer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// 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
rootPieceIDDeriver storj.PieceIDDeriver
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.rootPieceIDDeriver = rootPieceID.Deriver()
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 *pb.Node, 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.rootPieceIDDeriver.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: node.Address,
}
signer.AddressedLimits = append(signer.AddressedLimits, addressedLimit)
return addressedLimit, nil
}