Skip to content

Commit a42e3d4

Browse files
authored
AcceptView may expose a signing oracle #1233 (#1254)
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent 366c8e2 commit a42e3d4

File tree

3 files changed

+71
-116
lines changed

3 files changed

+71
-116
lines changed

token/services/ttx/accept.go

Lines changed: 47 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -7,172 +7,107 @@ SPDX-License-Identifier: Apache-2.0
77
package ttx
88

99
import (
10-
"encoding/base64"
11-
"time"
12-
1310
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
1411
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/id"
1512
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/sig"
16-
"github.com/hyperledger-labs/fabric-smart-client/platform/view/services/storage/kvs"
1713
"github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
18-
"github.com/hyperledger-labs/fabric-token-sdk/token"
1914
"github.com/hyperledger-labs/fabric-token-sdk/token/services/tokens"
2015
"github.com/hyperledger-labs/fabric-token-sdk/token/services/utils"
21-
session2 "github.com/hyperledger-labs/fabric-token-sdk/token/services/utils/json/session"
2216
)
2317

18+
// AcceptView is used to accept tokens without the need to generate any signature.
19+
// This is a view executed by a responder.
20+
// This view is to be used in conjunction with CollectEndorsementsView.
21+
// Usually, AcceptView is preceded by an invocation of `tx.ReceiveTransaction(context)`
22+
// necessary if the initiator has invoked CollectEndorsementsView.
2423
type AcceptView struct {
25-
tx *Transaction
26-
options *EndorsementsOpts
24+
tx *Transaction
25+
opts []EndorsementsOpt
2726
}
2827

28+
// NewAcceptView returns a new instance of AcceptView given in input a transaction.
2929
func NewAcceptView(tx *Transaction, opts ...EndorsementsOpt) *AcceptView {
30-
options, err := CompileCollectEndorsementsOpts(opts...)
31-
if err != nil {
32-
panic(err)
33-
}
34-
return &AcceptView{tx: tx, options: options}
30+
return &AcceptView{tx: tx, opts: opts}
3531
}
3632

33+
// Call accepts the tokens created by the transaction this view has been created with.
3734
func (s *AcceptView) Call(context view.Context) (interface{}, error) {
38-
if err := s.respondToSignatureRequests(context); err != nil {
39-
return nil, err
35+
// validate inputs
36+
if s.tx == nil {
37+
return nil, errors.WithMessagef(ErrInvalidInput, "transaction is nil")
4038
}
4139

42-
// Store transaction in the token transaction database
40+
// store transaction in the token transaction database
4341
if err := StoreTransactionRecords(context, s.tx); err != nil {
4442
return nil, errors.Wrapf(err, "failed storing transaction records %s", s.tx.ID())
4543
}
4644

45+
// ack
46+
if err := s.ack(context); err != nil {
47+
return nil, errors.Wrapf(err, "failed acknowledging transaction %s", s.tx.ID())
48+
}
49+
50+
// metrics
51+
labels := []string{
52+
"network", s.tx.Network(),
53+
"channel", s.tx.Channel(),
54+
"namespace", s.tx.Namespace(),
55+
}
56+
GetMetrics(context).AcceptedTransactions.With(labels...).Add(1)
57+
58+
// cache request
59+
if err := s.cacheRequest(context); err != nil {
60+
return nil, errors.Wrapf(err, "failed caching request for [%s]", s.tx.ID())
61+
}
62+
63+
return s.tx, nil
64+
}
65+
66+
// ack sends back an acknowledgement by signing the received transaction
67+
// with the identity of the FSC node running this stack.
68+
func (s *AcceptView) ack(context view.Context) error {
4769
txRaw := s.tx.FromRaw
48-
// Send back an acknowledgement
4970
idProvider, err := id.GetProvider(context)
5071
if err != nil {
51-
return nil, errors.Wrapf(err, "failed to get identity provider")
72+
return errors.Wrapf(err, "failed to get identity provider")
5273
}
5374
defaultIdentity := idProvider.DefaultIdentity()
5475

5576
logger.DebugfContext(context.Context(), "signing ack response [%s] with identity [%s]", utils.Hashable(txRaw), defaultIdentity)
5677
sigService, err := sig.GetService(context)
5778
if err != nil {
58-
return nil, errors.Wrapf(err, "failed to get sig service")
79+
return errors.Wrapf(err, "failed to get sig service")
5980
}
6081
signer, err := sigService.GetSigner(defaultIdentity)
6182
if err != nil {
62-
return nil, errors.WithMessagef(err, "failed to get signer for default identity")
83+
return errors.WithMessagef(err, "failed to get signer for default identity")
6384
}
6485
logger.DebugfContext(context.Context(), "Sign ack for distribution")
6586
sigma, err := signer.Sign(txRaw)
6687
if err != nil {
67-
return nil, errors.WithMessagef(err, "failed to sign ack response")
88+
return errors.WithMessagef(err, "failed to sign ack response")
6889
}
6990

7091
// Ack for distribution
7192
// Send the signature back
7293
session := context.Session()
7394
logger.DebugfContext(context.Context(), "ack response: [%s] from [%s]", utils.Hashable(sigma), defaultIdentity)
7495
if err := session.SendWithContext(context.Context(), sigma); err != nil {
75-
return nil, errors.WithMessagef(err, "failed sending ack")
96+
return errors.WithMessagef(err, "failed sending ack")
7697
}
98+
return nil
99+
}
77100

101+
func (s *AcceptView) cacheRequest(context view.Context) error {
78102
// cache the token request into the tokens db
79103
t, err := tokens.GetService(context, s.tx.TMSID())
80104
if err != nil {
81-
return nil, errors.Wrapf(err, "failed to get tokens db for [%s]", s.tx.TMSID())
105+
return errors.Wrapf(err, "failed to get tokens db for [%s]", s.tx.TMSID())
82106
}
83107

84108
if err := t.CacheRequest(context.Context(), s.tx.TMSID(), s.tx.TokenRequest); err != nil {
85109
logger.WarnfContext(context.Context(), "failed to cache token request [%s], this might cause delay, investigate when possible: [%s]", s.tx.TokenRequest.Anchor, err)
86110
}
87111

88-
labels := []string{
89-
"network", s.tx.Network(),
90-
"channel", s.tx.Channel(),
91-
"namespace", s.tx.Namespace(),
92-
}
93-
GetMetrics(context).AcceptedTransactions.With(labels...).Add(1)
94-
95-
return s.tx, nil
96-
}
97-
98-
func (s *AcceptView) respondToSignatureRequests(context view.Context) error {
99-
requestsToBeSigned, err := requestsToBeSigned(context.Context(), s.tx.TokenRequest)
100-
if err != nil {
101-
return errors.Wrapf(err, "failed collecting requests of signature")
102-
}
103-
logger.DebugfContext(context.Context(), "respond to signature requests [%s][%d]", s.tx.ID(), len(requestsToBeSigned))
104-
105-
session := context.Session()
106-
for i := range requestsToBeSigned {
107-
logger.DebugfContext(context.Context(), "Sign request no %d", i)
108-
signatureRequest := &SignatureRequest{}
109-
110-
if i == 0 {
111-
logger.DebugfContext(context.Context(), "First request is fetched from KVS")
112-
k, err := kvs.CreateCompositeKey("signatureRequest", []string{s.tx.ID()})
113-
if err != nil {
114-
return errors.Wrap(err, "failed to generate key to store signature request")
115-
}
116-
var srStr string
117-
if kvss, err := context.GetService(&kvs.KVS{}); err != nil {
118-
return errors.Wrap(err, "failed to get KVS from context")
119-
} else if err := kvss.(*kvs.KVS).Get(context.Context(), k, &srStr); err != nil {
120-
return errors.Wrap(err, "failed to store signature request")
121-
}
122-
srRaw, err := base64.StdEncoding.DecodeString(srStr)
123-
if err != nil {
124-
return errors.Wrap(err, "failed to decode signature request")
125-
}
126-
if err := Unmarshal(srRaw, signatureRequest); err != nil {
127-
return errors.Wrap(err, "failed unmarshalling signature request")
128-
}
129-
} else {
130-
logger.DebugfContext(context.Context(), "Receiving signature request...")
131-
jsonSession := session2.JSON(context)
132-
err := jsonSession.ReceiveWithTimeout(signatureRequest, time.Minute)
133-
if err != nil {
134-
return errors.Wrap(err, "failed reading signature request")
135-
}
136-
}
137-
logger.DebugfContext(context.Context(), "Fetched request from session")
138-
tms, err := token.GetManagementService(context, token.WithTMS(s.tx.Network(), s.tx.Channel(), s.tx.Namespace()))
139-
if err != nil {
140-
return errors.Wrapf(err, "failed getting TMS for [%s:%s:%s]", s.tx.Network(), s.tx.Channel(), s.tx.Namespace())
141-
}
142-
143-
if !tms.SigService().IsMe(context.Context(), signatureRequest.Signer) {
144-
return errors.Errorf("identity [%s] is not me", signatureRequest.Signer.UniqueID())
145-
}
146-
signer, err := s.tx.TokenService().SigService().GetSigner(context.Context(), signatureRequest.Signer)
147-
if err != nil {
148-
return errors.Wrapf(err, "cannot find signer for [%s]", signatureRequest.Signer.UniqueID())
149-
}
150-
logger.DebugfContext(context.Context(), "Sign message")
151-
sigma, err := signer.Sign(signatureRequest.MessageToSign())
152-
if err != nil {
153-
return errors.Wrapf(err, "failed signing request")
154-
}
155-
logger.DebugfContext(context.Context(), "Send back signature...")
156-
157-
err = session.SendWithContext(context.Context(), sigma)
158-
if err != nil {
159-
return errors.Wrapf(err, "failed sending signature back")
160-
}
161-
}
162-
163-
if len(requestsToBeSigned) > 0 {
164-
logger.DebugfContext(context.Context(), "wait the transaction to be sent back [%s]", s.tx.ID())
165-
// expect again to receive a transaction
166-
tx, err := ReceiveTransaction(context)
167-
if err != nil {
168-
return errors.Wrapf(err, "expected to receive a transaction")
169-
}
170-
// TODO: check that the token requests match
171-
s.tx = tx
172-
logger.DebugfContext(context.Context(), "wait the transaction to be sent back [%s], received", s.tx.ID())
173-
} else {
174-
logger.DebugfContext(context.Context(), "no need to wait the transaction to be sent back [%s]", s.tx.ID())
175-
}
176-
177112
return nil
178113
}

token/services/ttx/errors.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package ttx
8+
9+
import (
10+
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
11+
)
12+
13+
var (
14+
// ErrFailedCompilingOptions signals a failure when compiling the options
15+
ErrFailedCompilingOptions = errors.New("failed to compiling options")
16+
// ErrInvalidInput signals that the input is invalid
17+
ErrInvalidInput = errors.New("invalid input")
18+
)

token/services/ttx/transaction.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ type Payload struct {
3535
type Transaction struct {
3636
*Payload
3737

38-
TMS *token.ManagementService
39-
NetworkProvider GetNetworkFunc
40-
Opts *TxOptions
41-
Context context.Context
38+
TMS *token.ManagementService
39+
NetworkProvider GetNetworkFunc
40+
Opts *TxOptions
41+
Context context.Context
42+
// FromRaw contains the raw material used to unmarshall this transaction.
43+
// It is nil if the transaction was created from scratch.
4244
FromRaw []byte
4345
EndpointResolver *endpoint.Service
4446
}

0 commit comments

Comments
 (0)