storj/uplink/piecestore/verification.go
Ivan Fraixedes 9caa3181d3
uplink/piecestore: Check SN piece hash timestamp (#3246)
Uplink must verify that every piece upload to a storage node return a
hash whose timestamp isn't older than the maximum elapsed time allowed
by the Satellite.

We cannot leave this check only to the Satellite site, because if there
is no error reported by this matter, the uplink cuts down the long tail.
When uplink submits the result uploads including these invalid ones, the
Satellite filters out the invalid ones and that can provoke that it gets
less than the optimal threshold amount of valid upload results, so it
rejects the request.

Detecting the error at this stage will allow the uplink to detect these
uploads as invalid and avoid to cut down the long tail prematurely.
2019-10-15 16:07:18 +02:00

56 lines
1.9 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package piecestore
import (
"bytes"
"context"
"time"
"github.com/zeebo/errs"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/signing"
)
const pieceHashExpiration = 2 * time.Hour
var (
// ErrInternal is an error class for internal errors.
ErrInternal = errs.Class("internal")
// ErrProtocol is an error class for unexpected protocol sequence.
ErrProtocol = errs.Class("protocol")
// ErrVerifyUntrusted is an error in case there is a trust issue.
ErrVerifyUntrusted = errs.Class("untrusted")
// ErrStorageNodeInvalidResponse is an error when a storage node returns a response with invalid data
ErrStorageNodeInvalidResponse = errs.Class("storage node has returned an invalid response")
)
// VerifyPieceHash verifies piece hash which is sent by peer.
func (client *Client) VerifyPieceHash(ctx context.Context, peer *identity.PeerIdentity, limit *pb.OrderLimit, hash *pb.PieceHash, expectedHash []byte) (err error) {
defer mon.Task()(&ctx)(&err)
if peer == nil || limit == nil || hash == nil || len(expectedHash) == 0 {
return ErrProtocol.New("invalid arguments")
}
if limit.PieceId != hash.PieceId {
return ErrProtocol.New("piece id changed") // TODO: report rpc status bad message
}
if !bytes.Equal(hash.Hash, expectedHash) {
return ErrVerifyUntrusted.New("hashes don't match") // TODO: report rpc status bad message
}
if err := signing.VerifyPieceHashSignature(ctx, signing.SigneeFromPeerIdentity(peer), hash); err != nil {
return ErrVerifyUntrusted.New("invalid hash signature: %v", err) // TODO: report rpc status bad message
}
if hash.Timestamp.Before(time.Now().Add(-pieceHashExpiration)) {
return ErrStorageNodeInvalidResponse.New("piece has timestamp is too old (%v). Required to be not older than %s",
hash.Timestamp, pieceHashExpiration,
)
}
return nil
}