-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmiddleware.go
121 lines (108 loc) · 3.62 KB
/
middleware.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
package envelope
import (
"crypto/sha256"
"encoding/base64"
"time"
"github.com/btcsuite/btcutil/base58"
"go.uber.org/zap"
"github.com/hyperledger-labs/cckit/router"
)
const (
// argument indexes
methodNamePos = iota
payloadPos
envelopePos
nonceObjectType = "nonce"
invokeType = "invoke"
initType = "init"
TimeLayout = "2006-01-02T15:04:05.000Z"
PubKey string = "envelopePubkey" // router context key
)
// Verify is a middleware for checking signature in envelop
func Verify(verifier Verifier) router.MiddlewareFunc {
return func(next router.HandlerFunc, pos ...int) router.HandlerFunc {
return func(ctx router.Context) (interface{}, error) {
if ctx.Handler().Type == invokeType {
iArgs := ctx.GetArgs()
if string(iArgs[methodNamePos]) != initType {
if len(iArgs) == 2 {
ctx.Logger().Sugar().Error(ErrSignatureNotFound)
return nil, ErrSignatureNotFound
} else {
var (
e *Envelope
err error
)
if e, err = verifyEnvelope(ctx, verifier, iArgs[methodNamePos], iArgs[payloadPos], iArgs[envelopePos]); err != nil {
return nil, err
}
// store correct pubkey in context
ctx.SetParam(PubKey, e.PublicKey)
}
}
}
return next(ctx)
}
}
}
func verifyEnvelope(ctx router.Context, verifier Verifier, method, payload, envlp []byte) (*Envelope, error) {
// parse json envelope format (json is original format for envelope from frontend)
data, err := ctx.Serializer().FromBytesTo(envlp, &Envelope{})
if err != nil {
ctx.Logger().Error(`convert from bytes failed:`, zap.Error(err))
return nil, err
}
envelope := data.(*Envelope)
if envelope.Deadline.AsTime().Unix() != 0 {
if envelope.Deadline.AsTime().Unix() < time.Now().Unix() {
ctx.Logger().Sugar().Error(ErrDeadlineExpired)
return nil, ErrDeadlineExpired
}
}
// check method and channel names because envelope can only be used once for channel+chaincode+method combination
if string(method) != envelope.Method {
ctx.Logger().Sugar().Error(ErrInvalidMethod)
return nil, ErrInvalidMethod
}
if ctx.Stub().GetChannelID() != envelope.Channel {
ctx.Logger().Sugar().Error(ErrInvalidChannel)
return nil, ErrInvalidChannel
}
// replay attack check
txHash := txNonceKey(payload, envelope.Nonce, envelope.Channel, envelope.Chaincode, envelope.Method, envelope.PublicKey)
key, err := ctx.Stub().CreateCompositeKey(nonceObjectType, []string{txHash})
if err != nil {
return nil, err
}
bb, err := ctx.Stub().GetState(key)
if bb == nil && err == nil {
if err := ctx.Stub().PutState(key, []byte{'0'}); err != nil {
return nil, err
}
// convert public key and sig from base58
pubkey := base58.Decode(envelope.PublicKey)
sig := base58.Decode(envelope.Signature)
// convert deadline to frontend format
var deadline string
if envelope.Deadline != nil {
deadline = envelope.Deadline.AsTime().Format(TimeLayout)
}
if err := verifier.Verify(payload, envelope.Nonce, envelope.Channel, envelope.Chaincode, envelope.Method, deadline, pubkey, sig); err != nil {
ctx.Logger().Error(ErrCheckSignatureFailed.Error(), zap.String("payload", string(payload)), zap.Any("envelope", envelope))
return nil, ErrCheckSignatureFailed
}
} else {
ctx.Logger().Sugar().Error(ErrTxAlreadyExecuted)
return nil, ErrTxAlreadyExecuted
}
return envelope, nil
}
func txNonceKey(payload []byte, nonce, channel, chaincode, method, pubKey string) string {
bb := append(payload, pubKey...)
bb = append(bb, nonce...)
bb = append(bb, channel...)
bb = append(bb, chaincode...)
bb = append(bb, method...)
hashed := sha256.Sum256(bb)
return base64.StdEncoding.EncodeToString(hashed[:])
}