Skip to content

Commit 57c94a7

Browse files
authored
Avoid fingerprint verification when checking IsUserKYC (#2548)
* chore: no fingerprint verification when checking uphold.IsUserKYC() is used only for online checks but not for payments. So skip certificate fingerprint checks to prevent outages when Uphold changes the certificate. Remove unused uphold.Wallet.Logger. Wallet methods always extract the logger from the passed Context. Plus remove from tests log level initialization. It was no-op as LogLevelCTXKey must be zerolog.Level, not a string. CLOSE: #2394 * fix: review comments
1 parent a917f9e commit 57c94a7

File tree

6 files changed

+88
-33
lines changed

6 files changed

+88
-33
lines changed

libs/clients/coingecko/client_test.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,8 @@ func (suite *CoingeckoTestSuite) SetupTest() {
3939
// setup the context
4040
suite.ctx = context.Background()
4141

42-
// setup debug for client
42+
// debug logging generates too much noise, so do not activate it.
4343
suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, false)
44-
// setup debug log level
45-
suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "info")
4644

4745
// setup a logger and put on context
4846
suite.ctx, _ = logutils.SetupLogger(suite.ctx)

libs/clients/stripe/client_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ func (suite *StripeTestSuite) SetupTest() {
3636
// setup the context
3737
suite.ctx = context.Background()
3838
suite.ctx = context.WithValue(suite.ctx, appctx.DebugLoggingCTXKey, false)
39-
suite.ctx = context.WithValue(suite.ctx, appctx.LogLevelCTXKey, "info")
4039
suite.ctx, _ = logutils.SetupLogger(suite.ctx)
4140

4241
stripeKey := os.Getenv("STRIPE_ONRAMP_SECRET_KEY")

libs/wallet/provider/uphold/uphold.go

+78-21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"crypto"
7+
"crypto/tls"
78
"encoding/base64"
89
"encoding/hex"
910
"encoding/json"
@@ -45,7 +46,6 @@ import (
4546
// A wallet corresponds to a single Uphold "card"
4647
type Wallet struct {
4748
walletutils.Info
48-
Logger *zerolog.Logger
4949
PrivKey crypto.Signer
5050
PubKey httpsignature.Verifier
5151
}
@@ -54,6 +54,7 @@ const (
5454
dateFormat = "2006-01-02T15:04:05.000Z"
5555
batchSize = 50
5656
listTransactionsRetries = 5
57+
httpTimeout = time.Second * 60
5758
)
5859

5960
const (
@@ -90,7 +91,13 @@ var (
9091
"sandbox": sandboxFingerprint,
9192
"prod": prodFingerprint,
9293
}[environment]
93-
client *http.Client
94+
95+
// The client to connect to Uphold servers while performing fingerprint
96+
// checks on the server certificates.
97+
defaultHTTPClient *http.Client
98+
99+
// The client without fingerprint checks.
100+
httpClientNoFP *http.Client
94101
)
95102

96103
func init() {
@@ -115,12 +122,33 @@ func init() {
115122
} else {
116123
proxy = nil
117124
}
118-
client = &http.Client{
119-
Timeout: time.Second * 60,
125+
126+
fingerprintDialer := pindialer.MakeContextDialer(upholdCertFingerprint)
127+
128+
// Uphold reports HTTP 401 error when connecting with HTTP2, so disable
129+
// HTTP/2 via setting TLSNextProto to an empty map. We do not need to set
130+
// this field on defaultHTTPClient as we set DialTLSContext without setting
131+
// ForceAttemptHTTP2 and that disables HTTP/2 also. But for clarity we
132+
// always set TLSNextProto.
133+
disableHTTP2 := make(
134+
map[string]func(authority string, c *tls.Conn) http.RoundTripper,
135+
0,
136+
)
137+
defaultHTTPClient = &http.Client{
138+
Timeout: httpTimeout,
120139
Transport: middleware.InstrumentRoundTripper(
121140
&http.Transport{
141+
DialTLSContext: fingerprintDialer,
122142
Proxy: proxy,
123-
DialTLSContext: pindialer.MakeContextDialer(upholdCertFingerprint),
143+
TLSNextProto: disableHTTP2,
144+
}, "uphold"),
145+
}
146+
httpClientNoFP = &http.Client{
147+
Timeout: httpTimeout,
148+
Transport: middleware.InstrumentRoundTripper(
149+
&http.Transport{
150+
Proxy: proxy,
151+
TLSNextProto: disableHTTP2,
124152
}, "uphold"),
125153
}
126154
}
@@ -141,7 +169,11 @@ func New(ctx context.Context, info walletutils.Info, privKey crypto.Signer, pubK
141169
if !info.AltCurrency.IsValid() {
142170
return nil, errors.New("a wallet must have a valid altcurrency")
143171
}
144-
return &Wallet{Info: info, PrivKey: privKey, PubKey: pubKey}, nil
172+
return &Wallet{
173+
Info: info,
174+
PrivKey: privKey,
175+
PubKey: pubKey,
176+
}, nil
145177
}
146178

147179
// FromWalletInfo returns an uphold wallet matching the provided wallet info
@@ -169,7 +201,11 @@ func newRequest(method, path string, body io.Reader) (*http.Request, error) {
169201
return req, err
170202
}
171203

172-
func submit(logger *zerolog.Logger, req *http.Request) ([]byte, *http.Response, error) {
204+
func submit(
205+
logger *zerolog.Logger,
206+
client *http.Client,
207+
req *http.Request,
208+
) ([]byte, *http.Response, error) {
173209
req.Header.Add("content-type", "application/json")
174210

175211
dump, err := httputil.DumpRequestOut(req, true)
@@ -262,8 +298,18 @@ func (w *Wallet) IsUserKYC(ctx context.Context, destination string) (string, boo
262298
return "", false, "", fmt.Errorf("failed to prepare transaction: %w", err)
263299
}
264300

265-
// submit the transaction the payload
266-
uhResp, err := grantWallet.SubmitTransaction(ctx, transactionB64, false)
301+
// Submit the transaction payload.
302+
//
303+
// As we use the wallet only for validation but not for a payment, skip
304+
// fingerprint checks to avoid outages when Uphold change the certificate.
305+
// For payments it is not a problem as they are done in batch and can be
306+
// repeated.
307+
uhResp, err := grantWallet.submitTransaction(
308+
ctx,
309+
httpClientNoFP,
310+
transactionB64,
311+
false, /*confirm*/
312+
)
267313
if err != nil {
268314
logger.Error().Err(err).Msg("failed to submit transaction")
269315
return "", false, "", fmt.Errorf("failed to submit transaction: %w", err)
@@ -361,7 +407,7 @@ func (w *Wallet) Register(ctx context.Context, label string) error {
361407
return err
362408
}
363409

364-
body, _, err := submit(logger, req)
410+
body, _, err := submit(logger, defaultHTTPClient, req)
365411
if err != nil {
366412
return err
367413
}
@@ -400,7 +446,7 @@ func (w *Wallet) SubmitRegistration(ctx context.Context, registrationB64 string)
400446
return err
401447
}
402448

403-
body, _, err := submit(logger, req)
449+
body, _, err := submit(logger, defaultHTTPClient, req)
404450
if err != nil {
405451
return err
406452
}
@@ -456,7 +502,7 @@ func (w *Wallet) GetCardDetails(ctx context.Context) (*CardDetails, error) {
456502
if err != nil {
457503
return nil, err
458504
}
459-
body, _, err := submit(logger, req)
505+
body, _, err := submit(logger, defaultHTTPClient, req)
460506
if err != nil {
461507
return nil, err
462508
}
@@ -588,7 +634,7 @@ func (w *Wallet) Transfer(ctx context.Context, altcurrency altcurrency.AltCurren
588634
return nil, fmt.Errorf("failed to sign the transfer: %w", err)
589635
}
590636

591-
respBody, _, err := submit(logger, req)
637+
respBody, _, err := submit(logger, defaultHTTPClient, req)
592638
if err != nil {
593639
// we need this to be draincoded wrapped error so we get the reason for failure in drains
594640
if codedErr, ok := err.(Coded); ok {
@@ -843,10 +889,21 @@ func (resp upholdTransactionResponse) ToTransactionInfo() *walletutils.Transacti
843889
return &txInfo
844890
}
845891

846-
// SubmitTransaction submits the base64 encoded transaction for verification but does not move funds
847-
//
848-
// unless confirm is set to true.
892+
// SubmitTransaction submits the base64 encoded transaction for verification but
893+
// does not move funds unless confirm is set to true.
849894
func (w *Wallet) SubmitTransaction(ctx context.Context, transactionB64 string, confirm bool) (*walletutils.TransactionInfo, error) {
895+
return w.submitTransaction(ctx, defaultHTTPClient, transactionB64, confirm)
896+
}
897+
898+
func (w *Wallet) submitTransaction(
899+
ctx context.Context,
900+
client *http.Client,
901+
transactionB64 string,
902+
confirm bool,
903+
) (*walletutils.TransactionInfo, error) {
904+
if confirm && client != defaultHTTPClient {
905+
panic("TLS fingerprint checks must be enabled when confirming")
906+
}
850907
logger := logging.FromContext(ctx)
851908

852909
_, err := w.VerifyTransaction(ctx, transactionB64)
@@ -879,7 +936,7 @@ func (w *Wallet) SubmitTransaction(ctx context.Context, transactionB64 string, c
879936
return nil, err
880937
}
881938

882-
respBody, _, err := submit(logger, req)
939+
respBody, _, err := submit(logger, client, req)
883940
if err != nil {
884941
return nil, err
885942
}
@@ -901,7 +958,7 @@ func (w *Wallet) ConfirmTransaction(ctx context.Context, id string) (*walletutil
901958
if err != nil {
902959
return nil, err
903960
}
904-
body, _, err := submit(logger, req)
961+
body, _, err := submit(logger, defaultHTTPClient, req)
905962
if err != nil {
906963
return nil, err
907964
}
@@ -927,7 +984,7 @@ func (w *Wallet) GetTransaction(ctx context.Context, id string) (*walletutils.Tr
927984
if err != nil {
928985
return nil, err
929986
}
930-
body, _, err := submit(logger, req)
987+
body, _, err := submit(logger, defaultHTTPClient, req)
931988
if err != nil {
932989
return nil, err
933990
}
@@ -970,7 +1027,7 @@ func (w *Wallet) ListTransactions(ctx context.Context, limit int, startDate time
9701027
var body []byte
9711028
var resp *http.Response
9721029
for i := 0; i < listTransactionsRetries; i++ {
973-
body, resp, err = submit(logger, req)
1030+
body, resp, err = submit(logger, defaultHTTPClient, req)
9741031
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
9751032
logger.Debug().
9761033
Str("path", "github.com/brave-intl/bat-go/wallet/provider/uphold").
@@ -1070,7 +1127,7 @@ func (w *Wallet) CreateCardAddress(ctx context.Context, network string) (string,
10701127
return "", err
10711128
}
10721129

1073-
body, _, err := submit(logger, req)
1130+
body, _, err := submit(logger, defaultHTTPClient, req)
10741131
if err != nil {
10751132
return "", err
10761133
}

libs/wallet/provider/uphold/uphold_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ func TestFingerprintCheck(t *testing.T) {
307307
var proxy func(*http.Request) (*url.URL, error)
308308
wrongFingerprint := "IYSLsapSKlkofKfi6M2hmS4gzXbQKGIX/DHBWIgstw3="
309309

310-
client = &http.Client{
310+
client := &http.Client{
311311
Timeout: time.Second * 60,
312312
// remove middleware calls
313313
Transport: &http.Transport{

services/skus/controllers_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -653,14 +653,11 @@ func (suite *ControllersTestSuite) TestE2EOrdersUpholdTransactions() {
653653
ctx := context.Background()
654654
// setup debug for client
655655
ctx = context.WithValue(ctx, appctx.DebugLoggingCTXKey, true)
656-
// setup debug log level
657-
ctx = context.WithValue(ctx, appctx.LogLevelCTXKey, "debug")
658656

659657
// setup a new logger, add to context as well
660-
_, logger := logutils.SetupLogger(ctx)
658+
ctx, _ = logutils.SetupLogger(ctx)
661659

662660
w := uphold.Wallet{
663-
Logger: logger,
664661
Info: info,
665662
PrivKey: privKey,
666663
PubKey: publicKey,
@@ -827,7 +824,11 @@ func generateWallet(ctx context.Context, t *testing.T) *uphold.Wallet {
827824
t.Fatal(err)
828825
}
829826
info.PublicKey = hex.EncodeToString(publicKey)
830-
newWallet := &uphold.Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey}
827+
newWallet := &uphold.Wallet{
828+
Info: info,
829+
PrivKey: privateKey,
830+
PubKey: publicKey,
831+
}
831832
err = newWallet.Register(ctx, "bat-go test card")
832833
if err != nil {
833834
t.Fatal(err)

tools/vault/cmd/create_wallet.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func CreateWallet(command *cobra.Command, args []string) error {
110110

111111
state.WalletInfo.PublicKey = signer.String()
112112

113-
wallet := &uphold.Wallet{Logger: logger, Info: state.WalletInfo, PrivKey: signer, PubKey: signer}
113+
wallet := &uphold.Wallet{Info: state.WalletInfo, PrivKey: signer, PubKey: signer}
114114

115115
reg, err := wallet.PrepareRegistration(name)
116116
if err != nil {
@@ -138,7 +138,7 @@ func CreateWallet(command *cobra.Command, args []string) error {
138138
return err
139139
}
140140

141-
wallet := uphold.Wallet{Logger: logger, Info: state.WalletInfo, PrivKey: ed25519.PrivateKey{}, PubKey: publicKey}
141+
wallet := uphold.Wallet{Info: state.WalletInfo, PrivKey: ed25519.PrivateKey{}, PubKey: publicKey}
142142

143143
err = wallet.SubmitRegistration(ctx, state.Registration)
144144
if err != nil {

0 commit comments

Comments
 (0)