diff --git a/libs/httpsignature/ed25519.go b/libs/httpsignature/ed25519.go index 464072400..c9ed4f09f 100644 --- a/libs/httpsignature/ed25519.go +++ b/libs/httpsignature/ed25519.go @@ -2,33 +2,59 @@ package httpsignature import ( "crypto" + "crypto/ed25519" "encoding/hex" - "errors" - "io" - "strconv" - - "golang.org/x/crypto/ed25519" + "fmt" ) // Ed25519PubKey a wrapper type around ed25519.PublicKey to fulfill interface Verifier type Ed25519PubKey ed25519.PublicKey +// Ed25519PubKey a wrapper type around ed25519.PublicKey to fulfill interface Signator +type Ed25519PrivKey ed25519.PrivateKey + +// ED25519-specific signator with access to the corresponding public key +type Ed25519Signator interface { + Signator + Public() Ed25519PubKey +} + // Verify the signature sig for message using the ed25519 public key pk // Returns true if the signature is valid, false if not and error if the key provided is not valid -func (pk Ed25519PubKey) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) { +func (pk Ed25519PubKey) VerifySignature(message, sig []byte) error { if l := len(pk); l != ed25519.PublicKeySize { - return false, errors.New("ed25519: bad public key length: " + strconv.Itoa(l)) + return fmt.Errorf("ed25519: bad public key length: %d", l) } - return ed25519.Verify(ed25519.PublicKey(pk), message, sig), nil + if !ed25519.Verify(ed25519.PublicKey(pk), message, sig) { + return ErrBadSignature + } + return nil } func (pk Ed25519PubKey) String() string { return hex.EncodeToString(pk) } -// GenerateEd25519Key generate an ed25519 keypair and return it -func GenerateEd25519Key(rand io.Reader) (Ed25519PubKey, ed25519.PrivateKey, error) { - publicKey, privateKey, err := ed25519.GenerateKey(nil) - return Ed25519PubKey(publicKey), privateKey, err +func (privKey Ed25519PrivKey) SignMessage( + message []byte, +) (signature []byte, err error) { + return ed25519.PrivateKey(privKey).Sign(nil, message, crypto.Hash(0)) +} + +func (privKey Ed25519PrivKey) Public() Ed25519PubKey { + pubKey := ed25519.PrivateKey(privKey).Public().(ed25519.PublicKey) + return Ed25519PubKey(pubKey) +} + +// Get the public key encoded as hexadecimal string +func (privKey Ed25519PrivKey) PublicHex() string { + pubKey := ed25519.PrivateKey(privKey).Public().(ed25519.PublicKey) + return hex.EncodeToString(pubKey) +} + +// GenerateEd25519Key generate an ed25519 private key +func GenerateEd25519Key() (Ed25519PrivKey, error) { + _, privateKey, err := ed25519.GenerateKey(nil) + return Ed25519PrivKey(privateKey), err } diff --git a/libs/httpsignature/hmac.go b/libs/httpsignature/hmac.go index a87156d08..b272ea848 100644 --- a/libs/httpsignature/hmac.go +++ b/libs/httpsignature/hmac.go @@ -1,18 +1,16 @@ package httpsignature import ( - "crypto" "crypto/hmac" "crypto/sha512" "crypto/subtle" - "io" ) // HMACKey is a symmetric key that can be used for HMAC-SHA512 request signing and verification type HMACKey string // Sign the message using the hmac key -func (key HMACKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { +func (key HMACKey) SignMessage(message []byte) (signature []byte, err error) { hhash := hmac.New(sha512.New, []byte(key)) // writing the message (HTTP signing string) to it _, err = hhash.Write(message) @@ -24,14 +22,17 @@ func (key HMACKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) } // Verify the signature sig for message using the hmac key -func (key HMACKey) Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) { - hashSum, err := key.Sign(nil, message, nil) +func (key HMACKey) VerifySignature(message, sig []byte) error { + hashSum, err := key.SignMessage(message) if err != nil { - return false, err + return err } // Return bool by checking whether or not the calculated hash is equal to // sig pulled out of the header. Check if returned int is equal to 1 to return a bool - return subtle.ConstantTimeCompare(hashSum, sig) == 1, nil + if subtle.ConstantTimeCompare(hashSum, sig) != 1 { + return ErrBadSignature + } + return nil } func (key HMACKey) String() string { diff --git a/libs/httpsignature/httpsignature.go b/libs/httpsignature/httpsignature.go index 75b373e07..133e4a061 100644 --- a/libs/httpsignature/httpsignature.go +++ b/libs/httpsignature/httpsignature.go @@ -6,12 +6,10 @@ import ( "bytes" "context" "crypto" - "crypto/rand" "encoding/base64" "errors" "fmt" "io" - "io/ioutil" "net/http" "regexp" "strings" @@ -37,12 +35,14 @@ type signature struct { // Signator is an interface for cryptographic signature creation // NOTE that this is a subset of the crypto.Signer interface type Signator interface { - Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) + SignMessage(message []byte) (signature []byte, err error) } // Verifier is an interface for cryptographic signature verification type Verifier interface { - Verify(message, sig []byte, opts crypto.SignerOpts) (bool, error) + // Should return ErrBadSignature if the verification fails due to signature + // mismatch. + VerifySignature(message, sig []byte) error String() string } @@ -50,7 +50,6 @@ type Verifier interface { type ParameterizedSignator struct { SignatureParams Signator Signator - Opts crypto.SignerOpts } // Keystore provides a way to lookup a public key based on the keyID a request was signed with @@ -68,7 +67,6 @@ type StaticKeystore struct { type ParameterizedKeystoreVerifier struct { SignatureParams Keystore Keystore - Opts crypto.SignerOpts } const ( @@ -82,6 +80,8 @@ const ( var ( signatureRegex = regexp.MustCompile(`(\w+)="([^"]*)"`) + + ErrBadSignature = errors.New("Signature mismatch") ) // LookupVerifier by returning a static verifier @@ -140,7 +140,7 @@ func (sp *SignatureParams) BuildSigningString(req *http.Request) (out []byte, er if err != nil { return out, err } - req.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + req.Body = io.NopCloser(bytes.NewBuffer(body)) d.Update(body) } req.Header.Add("Digest", d.String()) @@ -167,13 +167,13 @@ func (sp *SignatureParams) BuildSigningString(req *http.Request) (out []byte, er return out, nil } -// Sign the included HTTP request req using signator and options opts -func (sp *SignatureParams) Sign(signator Signator, opts crypto.SignerOpts, req *http.Request) error { +// Sign the included HTTP request req using the signator +func (sp *SignatureParams) SignRequest(signator Signator, req *http.Request) error { ss, err := sp.BuildSigningString(req) if err != nil { return err } - sig, err := signator.Sign(rand.Reader, ss, opts) + sig, err := signator.SignMessage(ss) if err != nil { return err } @@ -190,29 +190,29 @@ func (sp *SignatureParams) Sign(signator Signator, opts crypto.SignerOpts, req * return nil } -// SignRequest using signator and options opts in the parameterized signator +// SignRequest using signator in the parameterized signator func (p *ParameterizedSignator) SignRequest(req *http.Request) error { - return p.SignatureParams.Sign(p.Signator, p.Opts, req) + return p.SignatureParams.SignRequest(p.Signator, req) } // Verify the HTTP signature s over HTTP request req using verifier with options opts -func (sp *SignatureParams) Verify(verifier Verifier, opts crypto.SignerOpts, req *http.Request) (bool, error) { +func (sp *SignatureParams) VerifyRequest(verifier Verifier, req *http.Request) error { signingStr, err := sp.BuildSigningString(req) if err != nil { - return false, err + return err } var tmp signature err = tmp.UnmarshalText([]byte(req.Header.Get("Signature"))) if err != nil { - return false, err + return err } sig, err := base64.StdEncoding.DecodeString(tmp.Sig) if err != nil { - return false, err + return err } - return verifier.Verify(signingStr, sig, opts) + return verifier.VerifySignature(signingStr, sig) } // VerifyRequest using keystore to lookup verifier with options opts @@ -236,13 +236,9 @@ func (pkv *ParameterizedKeystoreVerifier) VerifyRequest(req *http.Request) (cont sp.Algorithm = pkv.SignatureParams.Algorithm sp.Headers = pkv.SignatureParams.Headers - valid, err := sp.Verify(verifier, pkv.Opts, req) - if err != nil { + if err := sp.VerifyRequest(verifier, req); err != nil { return nil, "", err } - if !valid { - return nil, "", errors.New("signature is not valid") - } return ctx, sp.KeyID, nil } diff --git a/libs/httpsignature/httpsignature_test.go b/libs/httpsignature/httpsignature_test.go index 4c2c5142a..7459ce4c8 100644 --- a/libs/httpsignature/httpsignature_test.go +++ b/libs/httpsignature/httpsignature_test.go @@ -1,13 +1,12 @@ package httpsignature import ( - "crypto" "encoding/hex" "net/http" "reflect" "testing" - "golang.org/x/crypto/ed25519" + "github.com/stretchr/testify/assert" ) func TestBuildSigningString(t *testing.T) { @@ -48,7 +47,7 @@ func TestBuildSigningString(t *testing.T) { func TestSign(t *testing.T) { // ED25519 Test - var privKey ed25519.PrivateKey + var privKey Ed25519PrivKey privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" privKey, err := hex.DecodeString(privHex) if err != nil { @@ -66,7 +65,7 @@ func TestSign(t *testing.T) { } r.Header.Set("Foo", "bar") - err = s.Sign(privKey, crypto.Hash(0), r) + err = s.SignRequest(privKey, r) if err != nil { t.Error("Unexpected error while building ED25519 signing string:", err) } @@ -83,7 +82,7 @@ func TestSign(t *testing.T) { func TestSignRequest(t *testing.T) { // ED25519 Test - var privKey ed25519.PrivateKey + var privKey Ed25519PrivKey privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" privKey, err := hex.DecodeString(privHex) if err != nil { @@ -98,7 +97,6 @@ func TestSignRequest(t *testing.T) { ps := ParameterizedSignator{ SignatureParams: sp, Signator: privKey, - Opts: crypto.Hash(0), } r, err := http.NewRequest("GET", "http://example.org/foo", nil) @@ -131,7 +129,6 @@ func TestSignRequest(t *testing.T) { ps2 := ParameterizedSignator{ SignatureParams: sp2, Signator: HMACKey(privHex), - Opts: crypto.Hash(0), } r2, reqErr := http.NewRequest("GET", "http://example.org/foo2", nil) @@ -178,24 +175,14 @@ func TestVerify(t *testing.T) { r.Header.Set("Foo", "bar") r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest",signature="`+s.Sig+`"`) - valid, err := s.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if !valid { - t.Error("The signature should be valid") - } + err = s.VerifyRequest(pubKey, r) + assert.NoError(t, err) s.Sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" r.Header.Set("Signature", `keyId="primary",algorithm="ed25519",headers="digest",signature="`+s.Sig+`"`) - valid, err = s.Verify(pubKey, crypto.Hash(0), r) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } + err = s.VerifyRequest(pubKey, r) + assert.ErrorIs(t, err, ErrBadSignature) var hmacVerifier HMACKey = "yyqz64U$eG?eUAp24Pm!Fn!Cn" var s2 signature @@ -212,24 +199,14 @@ func TestVerify(t *testing.T) { req.Header.Set("Foo", "bar") req.Header.Set("Signature", `keyId="secondary",algorithm="hs2019",headers="digest",signature="`+sig+`"`) - valid, err = s2.Verify(hmacVerifier, nil, req) - if err != nil { - t.Error("Unexpected error while building signing string:", err) - } - if !valid { - t.Error("The signature should be valid") - } + err = s2.VerifyRequest(hmacVerifier, req) + assert.NoError(t, err) sig = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" req.Header.Set("Signature", `keyId="secondary",algorithm="hs2019",headers="digest",signature="`+sig+`"`) - valid, err = s2.Verify(hmacVerifier, nil, req) - if err != nil { - t.Error("Unexpected error while building signing string") - } - if valid { - t.Error("The signature should be invalid") - } + err = s2.VerifyRequest(hmacVerifier, req) + assert.ErrorIs(t, err, ErrBadSignature) } func TestVerifyRequest(t *testing.T) { @@ -247,7 +224,6 @@ func TestVerifyRequest(t *testing.T) { pkv := ParameterizedKeystoreVerifier{ SignatureParams: sp, Keystore: &StaticKeystore{pubKey}, - Opts: crypto.Hash(0), } sig := "RbGSX1MttcKCpCkq9nsPGkdJGUZsAU+0TpiXJYkwde+0ZwxEp9dXO3v17DwyGLXjv385253RdGI7URbrI7J6DQ==" @@ -288,7 +264,6 @@ func TestVerifyRequest(t *testing.T) { pkv2 := ParameterizedKeystoreVerifier{ SignatureParams: sp2, Keystore: &StaticKeystore{hmacVerifier}, - Opts: crypto.Hash(0), } sig = "3RCLz6TH2I32nj1NY5YaUWDSCNPiKsAVIXjX4merDeNvrGondy7+f3sWQQJWRwEo90FCrthWrrVcgHqqFevS9Q==" @@ -402,7 +377,7 @@ func TestTextUnmarshal(t *testing.T) { } func TestSignatureParamsFromRequest(t *testing.T) { - var privKey ed25519.PrivateKey + var privKey Ed25519PrivKey privHex := "96aa9ec42242a9a62196281045705196a64e12b15e9160bbb630e38385b82700e7876fd5cc3a228dad634816f4ec4b80a258b2a552467e5d26f30003211bc45d" privKey, err := hex.DecodeString(privHex) if err != nil { @@ -420,7 +395,7 @@ func TestSignatureParamsFromRequest(t *testing.T) { } r.Header.Set("Foo", "bar") - err = s.Sign(privKey, crypto.Hash(0), r) + err = s.SignRequest(privKey, r) if err != nil { t.Error("Unexpected error while building ED25519 signing string:", err) } diff --git a/libs/middleware/http_signed.go b/libs/middleware/http_signed.go index b5a80b524..cea2ad4a3 100644 --- a/libs/middleware/http_signed.go +++ b/libs/middleware/http_signed.go @@ -2,7 +2,6 @@ package middleware import ( "context" - "crypto" "errors" "net/http" "time" @@ -43,7 +42,6 @@ func HTTPSignedOnly(ks httpsignature.Keystore) func(http.Handler) http.Handler { Headers: []string{"digest", "(request-target)"}, }, Keystore: ks, - Opts: crypto.Hash(0), } return VerifyHTTPSignedOnly(verifier) diff --git a/libs/middleware/http_signed_test.go b/libs/middleware/http_signed_test.go index 8e3f093f4..3fc941cce 100644 --- a/libs/middleware/http_signed_test.go +++ b/libs/middleware/http_signed_test.go @@ -3,9 +3,8 @@ package middleware import ( "bytes" "context" - "crypto" "encoding/json" - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -27,12 +26,12 @@ func (m *mockKeystore) LookupVerifier(ctx context.Context, keyID string) (contex } func TestHTTPSignedOnly(t *testing.T) { - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) + privKey, err := httpsignature.GenerateEd25519Key() assert.NoError(t, err) - _, wrongKey, err := httpsignature.GenerateEd25519Key(nil) + wrongKey, err := httpsignature.GenerateEd25519Key() assert.NoError(t, err) - keystore := mockKeystore{publicKey} + keystore := mockKeystore{privKey.Public()} fn1 := func(w http.ResponseWriter, r *http.Request) { t.Errorf("Should not have gotten here") @@ -61,7 +60,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -71,7 +70,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) - err = s.Sign(wrongKey, crypto.Hash(0), req) + err = s.SignRequest(wrongKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -81,7 +80,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -91,9 +90,9 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) - req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("hello world"))) + req.Body = io.NopCloser(bytes.NewBuffer([]byte("hello world"))) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code, "request with signature from right key but wrong digest should fail") @@ -109,7 +108,7 @@ func TestHTTPSignedOnly(t *testing.T) { // Host is not in the header, but on the request itself req.Host = "localhost" assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -121,14 +120,13 @@ func TestHTTPSignedOnly(t *testing.T) { Headers: []string{"digest", "(request-target)", "date", "host"}, // make sure host is in signing string }, Keystore: &keystore, - Opts: crypto.Hash(0), } handler = VerifyHTTPSignedOnly(verifier)(http.HandlerFunc(fn2)) req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -139,7 +137,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) req.Header.Set("Date", "foo") - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -148,7 +146,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) req.Header.Set("Date", time.Now().Add(time.Minute*60).Format(time.RFC1123)) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -157,7 +155,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) req.Header.Set("Date", time.Now().Add(time.Minute*-60).Format(time.RFC1123)) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) @@ -166,7 +164,7 @@ func TestHTTPSignedOnly(t *testing.T) { req, err = http.NewRequest("GET", "/hello-world", nil) assert.NoError(t, err) req.Header.Set("Date", time.Now().Format(time.RFC1123)) - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) assert.NoError(t, err) rr = httptest.NewRecorder() handler.ServeHTTP(rr, req) diff --git a/libs/requestutils/requestutils.go b/libs/requestutils/requestutils.go index 41c14dc8e..5f7e58c8d 100644 --- a/libs/requestutils/requestutils.go +++ b/libs/requestutils/requestutils.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "io" - "io/ioutil" "net/http" "github.com/brave-intl/bat-go/libs/closers" @@ -30,7 +29,7 @@ var ( // ReadWithLimit reads an io reader with a limit and closes func ReadWithLimit(ctx context.Context, body io.Reader, limit int64) ([]byte, error) { defer closers.Panic(ctx, body.(io.Closer)) - return ioutil.ReadAll(io.LimitReader(body, limit)) + return io.ReadAll(io.LimitReader(body, limit)) } // Read an io reader diff --git a/libs/wallet/provider/uphold/uphold.go b/libs/wallet/provider/uphold/uphold.go index 8a1b2d9b1..4753d48f3 100644 --- a/libs/wallet/provider/uphold/uphold.go +++ b/libs/wallet/provider/uphold/uphold.go @@ -3,7 +3,6 @@ package uphold import ( "bytes" "context" - "crypto" "crypto/tls" "encoding/base64" "encoding/hex" @@ -39,14 +38,13 @@ import ( "github.com/rs/zerolog" uuid "github.com/satori/go.uuid" "github.com/shopspring/decimal" - "golang.org/x/crypto/ed25519" ) // Wallet a wallet information using Uphold as the provider // A wallet corresponds to a single Uphold "card" type Wallet struct { walletutils.Info - PrivKey crypto.Signer + PrivKey httpsignature.Signator PubKey httpsignature.Verifier } @@ -155,7 +153,7 @@ func init() { // New returns an uphold wallet constructed using the provided parameters // NOTE that it does not register a wallet with Uphold if it does not already exist -func New(ctx context.Context, info walletutils.Info, privKey crypto.Signer, pubKey httpsignature.Verifier) (*Wallet, error) { +func New(ctx context.Context, info walletutils.Info, privKey httpsignature.Signator, pubKey httpsignature.Verifier) (*Wallet, error) { if info.Provider != "uphold" { return nil, errors.New("the wallet provider or deposit account must be uphold") } @@ -186,7 +184,7 @@ func FromWalletInfo(ctx context.Context, info walletutils.Info) (*Wallet, error) return nil, err } } - return New(ctx, info, ed25519.PrivateKey{}, publicKey) + return New(ctx, info, httpsignature.Ed25519PrivKey{}, publicKey) } func newRequest(method, path string, body io.Reader) (*http.Request, error) { @@ -287,8 +285,8 @@ func (w *Wallet) IsUserKYC(ctx context.Context, destination string) (string, boo Provider: "uphold", PublicKey: grantWalletPublicKey, }, - PrivKey: ed25519.PrivateKey([]byte(gwPrivateKey)), - PubKey: httpsignature.Ed25519PubKey([]byte(gwPublicKey)), + PrivKey: httpsignature.Ed25519PrivKey(gwPrivateKey), + PubKey: httpsignature.Ed25519PubKey(gwPublicKey), } // prepare a transaction by creating a payload @@ -394,7 +392,7 @@ func (w *Wallet) signRegistration(label string) (*http.Request, error) { s.KeyID = "primary" s.Headers = []string{"digest"} - err = s.Sign(w.PrivKey, crypto.Hash(0), req) + err = s.SignRequest(w.PrivKey, req) return req, err } @@ -582,7 +580,7 @@ func (w *Wallet) signTransfer(altc altcurrency.AltCurrency, probi decimal.Decima s.KeyID = "primary" s.Headers = []string{"digest"} - if err = s.Sign(w.PrivKey, crypto.Hash(0), req); err != nil { + if err := s.SignRequest(w.PrivKey, req); err != nil { return nil, fmt.Errorf("%w: %s", errorutils.ErrCreateTransferRequest, err.Error()) } return req, nil @@ -717,13 +715,10 @@ func (w *Wallet) decodeTransaction(transactionB64 string) (*transactionRequest, return nil, errors.New("a transaction signature must cover the request body via digest") } - valid, err := sigParams.Verify(w.PubKey, crypto.Hash(0), &req) + err = sigParams.VerifyRequest(w.PubKey, &req) if err != nil { return nil, err } - if !valid { - return nil, errors.New("the signature is invalid") - } var transactionRecode transactionRequestRecode err = json.Unmarshal([]byte(signedTx.Body), &transactionRecode) @@ -1153,7 +1148,7 @@ func FundWallet(ctx context.Context, destWallet *Wallet, amount decimal.Decimal) donorWalletPublicKeyHex := os.Getenv("DONOR_WALLET_PUBLIC_KEY") donorWalletPrivateKeyHex := os.Getenv("DONOR_WALLET_PRIVATE_KEY") var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey + var donorPrivateKey httpsignature.Ed25519PrivKey donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) if err != nil { return zero, err diff --git a/libs/wallet/provider/uphold/uphold_test.go b/libs/wallet/provider/uphold/uphold_test.go index 84d5f023f..9dd416f4c 100644 --- a/libs/wallet/provider/uphold/uphold_test.go +++ b/libs/wallet/provider/uphold/uphold_test.go @@ -17,7 +17,6 @@ import ( "github.com/brave-intl/bat-go/libs/wallet" uuid "github.com/satori/go.uuid" "github.com/shopspring/decimal" - "golang.org/x/crypto/ed25519" "gotest.tools/assert" ) @@ -61,12 +60,12 @@ func TestRegister(t *testing.T) { info.AltCurrency = &tmp } - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() if err != nil { t.Fatal(err) } - destWallet := &Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} + destWallet := &Wallet{Info: info, PrivKey: key, PubKey: key.Public()} err = destWallet.Register(ctx, "bat-go test card") if err != nil { t.Error(err) @@ -156,19 +155,13 @@ func TestTransactions(t *testing.T) { donorInfo.AltCurrency = &tmp } - donorWalletPublicKeyHex := os.Getenv("DONOR_WALLET_PUBLIC_KEY") donorWalletPrivateKeyHex := os.Getenv("DONOR_WALLET_PRIVATE_KEY") - var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey - donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) + var donorPrivateKey httpsignature.Ed25519PrivKey + donorPrivateKey, err := hex.DecodeString(donorWalletPrivateKeyHex) if err != nil { t.Fatal(err) } - donorPrivateKey, err = hex.DecodeString(donorWalletPrivateKeyHex) - if err != nil { - t.Fatal(err) - } - donorWallet := &Wallet{Info: donorInfo, PrivKey: donorPrivateKey, PubKey: donorPublicKey} + donorWallet := &Wallet{Info: donorInfo, PrivKey: donorPrivateKey, PubKey: donorPrivateKey.Public()} var info wallet.Info info.Provider = "uphold" @@ -178,12 +171,12 @@ func TestTransactions(t *testing.T) { info.AltCurrency = &tmp } - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() if err != nil { t.Fatal(err) } - destWallet := &Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} + destWallet := &Wallet{Info: info, PrivKey: key, PubKey: key.Public()} err = destWallet.Register(ctx, "bat-go test transaction card") if err != nil { t.Error(err) @@ -344,10 +337,10 @@ func requireDonorWallet(t *testing.T) *Wallet { info.AltCurrency = &tmp } - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() if err != nil { t.Fatal(err) } - return &Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} + return &Wallet{Info: info, PrivKey: key, PubKey: key.Public()} } diff --git a/services/payments/security_review_statemachine_uphold_test.go b/services/payments/security_review_statemachine_uphold_test.go index 707eb05ca..7ed9fa120 100644 --- a/services/payments/security_review_statemachine_uphold_test.go +++ b/services/payments/security_review_statemachine_uphold_test.go @@ -2,7 +2,6 @@ package payments import ( "context" - "crypto/ed25519" "encoding/json" "fmt" "os" @@ -25,8 +24,8 @@ var ( Provider: "uphold", PublicKey: "", }, - PrivKey: ed25519.PrivateKey([]byte("")), - PubKey: httpsignature.Ed25519PubKey([]byte("")), + PrivKey: httpsignature.Ed25519PrivKey(""), + PubKey: httpsignature.Ed25519PubKey(""), } upholdSucceedTransaction = custodian.Transaction{ProviderID: "1234"} ) diff --git a/services/promotion/controllers_test.go b/services/promotion/controllers_test.go index d9f8bb063..9eea94d97 100644 --- a/services/promotion/controllers_test.go +++ b/services/promotion/controllers_test.go @@ -5,7 +5,6 @@ package promotion import ( "bytes" "context" - "crypto" "encoding/json" "fmt" "net/http" @@ -273,7 +272,7 @@ func (suite *ControllersTestSuite) TestGetPromotions() { } // ClaimPromotion helper that calls promotion endpoint and does assertions -func (suite *ControllersTestSuite) ClaimPromotion(service *Service, w walletutils.Info, privKey crypto.Signer, +func (suite *ControllersTestSuite) ClaimPromotion(service *Service, w walletutils.Info, privKey httpsignature.Signator, promotion *Promotion, blindedCreds []string, claimStatus int) *uuid.UUID { handler := middleware.HTTPSignedOnly(service)(ClaimPromotion(service)) @@ -297,7 +296,7 @@ func (suite *ControllersTestSuite) ClaimPromotion(service *Service, w walletutil s.KeyID = w.ID s.Headers = []string{"digest", "(request-target)"} - err = s.Sign(privKey, crypto.Hash(0), req) + err = s.SignRequest(privKey, req) suite.Require().NoError(err) rctx := chi.NewRouteContext() diff --git a/services/skus/controllers_test.go b/services/skus/controllers_test.go index d8efc75d2..903653e64 100644 --- a/services/skus/controllers_test.go +++ b/services/skus/controllers_test.go @@ -576,7 +576,7 @@ func (suite *ControllersTestSuite) TestE2EOrdersUpholdTransactions() { handler := CreateUpholdTransaction(suite.service) // create an uphold wallet - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err, "Failed to create wallet keypair") walletID := uuid.NewV4() @@ -586,7 +586,7 @@ func (suite *ControllersTestSuite) TestE2EOrdersUpholdTransactions() { Provider: "uphold", ProviderID: "-", AltCurrency: &bat, - PublicKey: hex.EncodeToString(publicKey), + PublicKey: key.PublicHex(), LastBalance: nil, } @@ -599,8 +599,8 @@ func (suite *ControllersTestSuite) TestE2EOrdersUpholdTransactions() { w := uphold.Wallet{ Info: info, - PrivKey: privKey, - PubKey: publicKey, + PrivKey: key, + PubKey: key.Public(), } err = w.Register(ctx, "drain-card-test") suite.Require().NoError(err, "Failed to register wallet") @@ -759,15 +759,15 @@ func generateWallet(ctx context.Context, t *testing.T) *uphold.Wallet { info.AltCurrency = &tmp } - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() if err != nil { t.Fatal(err) } - info.PublicKey = hex.EncodeToString(publicKey) + info.PublicKey = key.PublicHex() newWallet := &uphold.Wallet{ Info: info, - PrivKey: privateKey, - PubKey: publicKey, + PrivKey: key, + PubKey: key.Public(), } err = newWallet.Register(ctx, "bat-go test card") if err != nil { diff --git a/services/skus/key.go b/services/skus/key.go index a004f227a..5c3df53ce 100644 --- a/services/skus/key.go +++ b/services/skus/key.go @@ -2,7 +2,6 @@ package skus import ( "context" - "crypto" "crypto/rand" "encoding/base64" "encoding/hex" @@ -197,7 +196,6 @@ func NewAuthMwr(ks httpsignature.Keystore) func(http.Handler) http.Handler { }, }, Keystore: ks, - Opts: crypto.Hash(0), } // TODO: Keep only VerifyHTTPSignedOnly after migrating Subscriptions to this method. diff --git a/services/skus/key_test.go b/services/skus/key_test.go index 19a30623b..a81094d22 100644 --- a/services/skus/key_test.go +++ b/services/skus/key_test.go @@ -2,7 +2,6 @@ package skus import ( "context" - "crypto" "database/sql" "encoding/base64" "encoding/hex" @@ -221,7 +220,6 @@ func TestMerchantSignedMiddleware(t *testing.T) { ps := httpsignature.ParameterizedSignator{ SignatureParams: sp, Signator: httpsignature.HMACKey(attenuatedSecret), - Opts: crypto.Hash(0), } req, err = http.NewRequest("GET", "/hello-world", nil) req.Header.Set("Date", time.Now().Format(time.RFC1123)) diff --git a/services/wallet/controllers_v3.go b/services/wallet/controllers_v3.go index 4a7122caf..cf7924d87 100644 --- a/services/wallet/controllers_v3.go +++ b/services/wallet/controllers_v3.go @@ -1,8 +1,6 @@ package wallet import ( - "crypto" - "crypto/ed25519" "database/sql" "encoding/hex" "encoding/json" @@ -81,8 +79,8 @@ func CreateUpholdWalletV3(w http.ResponseWriter, r *http.Request) *handlers.AppE uwallet := uphold.Wallet{ Info: *info, - PrivKey: ed25519.PrivateKey{}, - PubKey: httpsignature.Ed25519PubKey([]byte(publicKey)), + PrivKey: httpsignature.Ed25519PrivKey{}, + PubKey: httpsignature.Ed25519PubKey(publicKey), } if err := uwallet.SubmitRegistration(ctx, ucReq.SignedCreationRequest); err != nil { return handlers.WrapError( @@ -109,7 +107,6 @@ func CreateBraveWalletV3(w http.ResponseWriter, r *http.Request) *handlers.AppEr Headers: []string{"digest", "(request-target)"}, }, Keystore: &DecodeEd25519Keystore{}, - Opts: crypto.Hash(0), } // perform validation based on public key that the user submits @@ -428,8 +425,8 @@ func LinkUpholdDepositAccountV3(s *Service) func(w http.ResponseWriter, r *http. } uwallet := uphold.Wallet{ Info: *wallet, - PrivKey: ed25519.PrivateKey{}, - PubKey: httpsignature.Ed25519PubKey([]byte(publicKey)), + PrivKey: httpsignature.Ed25519PrivKey{}, + PubKey: httpsignature.Ed25519PubKey(publicKey), } country, err := s.LinkUpholdWallet(ctx, uwallet, cuw.SignedLinkingRequest, &aa) diff --git a/services/wallet/controllers_v3_pvt_test.go b/services/wallet/controllers_v3_pvt_test.go index 48c83d1aa..da373936c 100644 --- a/services/wallet/controllers_v3_pvt_test.go +++ b/services/wallet/controllers_v3_pvt_test.go @@ -3,8 +3,6 @@ package wallet import ( "bytes" "context" - "crypto" - "crypto/ed25519" "crypto/sha256" "database/sql" "encoding/base64" @@ -64,10 +62,10 @@ func TestCreateBraveWalletV3(t *testing.T) { ctx = context.WithValue(ctx, appctx.NoUnlinkPriorToDurationCTXKey, "-P1D") // setup keypair - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() require.NoError(t, err) - err = signRequest(r, publicKey, privKey) + err = signRequest(r, key) require.NoError(t, err) r = r.WithContext(ctx) @@ -1019,12 +1017,12 @@ func TestIsAllowedOrigin(t *testing.T) { } } -func signRequest(req *http.Request, publicKey httpsignature.Ed25519PubKey, privateKey ed25519.PrivateKey) error { +func signRequest(req *http.Request, key httpsignature.Ed25519PrivKey) error { var s httpsignature.SignatureParams s.Algorithm = httpsignature.ED25519 - s.KeyID = hex.EncodeToString(publicKey) + s.KeyID = key.PublicHex() s.Headers = []string{"digest", "(request-target)"} - return s.Sign(privateKey, crypto.Hash(0), req) + return s.SignRequest(key, req) } type result struct{} diff --git a/services/wallet/controllers_v3_test.go b/services/wallet/controllers_v3_test.go index 5d266512a..7bcc2d38b 100644 --- a/services/wallet/controllers_v3_test.go +++ b/services/wallet/controllers_v3_test.go @@ -284,20 +284,19 @@ func (suite *WalletControllersTestSuite) createBody(tx string) string { } func (suite *WalletControllersTestSuite) NewWallet(service *wallet.Service, provider string) *uphold.Wallet { - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) - publicKeyString := hex.EncodeToString(publicKey) + key, err := httpsignature.GenerateEd25519Key() bat := altcurrency.BAT info := walletutils.Info{ ID: uuid.NewV4().String(), - PublicKey: publicKeyString, + PublicKey: key.PublicHex(), Provider: provider, AltCurrency: &bat, } w := &uphold.Wallet{ Info: info, - PrivKey: privKey, - PubKey: publicKey, + PrivKey: key, + PubKey: key.Public(), } reg, err := w.PrepareRegistration("Brave Browser Test Link") @@ -307,8 +306,7 @@ func (suite *WalletControllersTestSuite) NewWallet(service *wallet.Service, prov service, suite.createBody(reg), http.StatusCreated, - publicKey, - privKey, + key, true, ) @@ -326,7 +324,7 @@ func (suite *WalletControllersTestSuite) TestCreateBraveWalletV3() { service, _ := wallet.InitService(pg, nil, nil, nil, nil, nil, nil, nil, nil, nil, wallet.DAppConfig{}) - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() // assume 400 is already covered // fail because of lacking signature presence @@ -334,8 +332,7 @@ func (suite *WalletControllersTestSuite) TestCreateBraveWalletV3() { service, `{}`, http.StatusBadRequest, - publicKey, - privKey, + key, false, ) @@ -345,8 +342,7 @@ func (suite *WalletControllersTestSuite) TestCreateBraveWalletV3() { service, ``, http.StatusCreated, - publicKey, - privKey, + key, true, ) @@ -369,14 +365,13 @@ func (suite *WalletControllersTestSuite) TestCreateUpholdWalletV3() { service, _ := wallet.InitService(pg, nil, nil, nil, nil, nil, nil, nil, nil, nil, wallet.DAppConfig{}) - publicKey, privKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() badJSONBodyParse := suite.createUpholdWalletV3( service, ``, http.StatusBadRequest, - publicKey, - privKey, + key, true, ) suite.Assert().JSONEq(`{ @@ -395,8 +390,7 @@ func (suite *WalletControllersTestSuite) TestCreateUpholdWalletV3() { service, `{"signedCreationRequest":""}`, http.StatusBadRequest, - publicKey, - privKey, + key, true, ) @@ -409,8 +403,7 @@ func (suite *WalletControllersTestSuite) TestCreateUpholdWalletV3() { service, `{}`, http.StatusBadRequest, - publicKey, - privKey, + key, false, ) @@ -656,8 +649,7 @@ func (suite *WalletControllersTestSuite) createBraveWalletV3( service *wallet.Service, body string, code int, - publicKey httpsignature.Ed25519PubKey, - privateKey ed25519.PrivateKey, + key httpsignature.Ed25519PrivKey, shouldSign bool, ) string { @@ -672,11 +664,7 @@ func (suite *WalletControllersTestSuite) createBraveWalletV3( req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) if shouldSign { - suite.SignRequest( - req, - publicKey, - privateKey, - ) + suite.SignRequest(req, key) } rctx := chi.NewRouteContext() @@ -694,8 +682,7 @@ func (suite *WalletControllersTestSuite) createUpholdWalletV3( service *wallet.Service, body string, code int, - publicKey httpsignature.Ed25519PubKey, - privateKey ed25519.PrivateKey, + key httpsignature.Ed25519PrivKey, shouldSign bool, ) string { @@ -710,11 +697,7 @@ func (suite *WalletControllersTestSuite) createUpholdWalletV3( req = req.WithContext(context.WithValue(req.Context(), appctx.NoUnlinkPriorToDurationCTXKey, "-P1D")) if shouldSign { - suite.SignRequest( - req, - publicKey, - privateKey, - ) + suite.SignRequest(req, key) } rctx := chi.NewRouteContext() @@ -728,13 +711,13 @@ func (suite *WalletControllersTestSuite) createUpholdWalletV3( return rr.Body.String() } -func (suite *WalletControllersTestSuite) SignRequest(req *http.Request, publicKey httpsignature.Ed25519PubKey, privateKey ed25519.PrivateKey) { +func (suite *WalletControllersTestSuite) SignRequest(req *http.Request, key httpsignature.Ed25519PrivKey) { var s httpsignature.SignatureParams s.Algorithm = httpsignature.ED25519 - s.KeyID = hex.EncodeToString(publicKey) + s.KeyID = key.PublicHex() s.Headers = []string{"digest", "(request-target)"} - err := s.Sign(privateKey, crypto.Hash(0), req) + err := s.SignRequest(key, req) suite.Require().NoError(err) } diff --git a/services/wallet/controllers_v4.go b/services/wallet/controllers_v4.go index af6fbd89d..5c2100d76 100644 --- a/services/wallet/controllers_v4.go +++ b/services/wallet/controllers_v4.go @@ -1,7 +1,6 @@ package wallet import ( - "crypto" "encoding/json" "errors" "net/http" @@ -46,7 +45,6 @@ func CreateWalletV4(s *Service) func(w http.ResponseWriter, r *http.Request) *ha Headers: []string{"digest", "(request-target)"}, }, Keystore: &DecodeEd25519Keystore{}, - Opts: crypto.Hash(0), } ctx, publicKey, err := verifier.VerifyRequest(r) diff --git a/services/wallet/controllers_v4_test.go b/services/wallet/controllers_v4_test.go index 37474abab..36b6a05f7 100644 --- a/services/wallet/controllers_v4_test.go +++ b/services/wallet/controllers_v4_test.go @@ -5,7 +5,6 @@ package wallet_test import ( "bytes" "context" - "crypto" "crypto/ed25519" "encoding/hex" "encoding/json" @@ -94,10 +93,10 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_Success() { request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - err = signRequest(request, publicKey, privateKey) + err = signRequest(request, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -109,7 +108,7 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_Success() { err = json.NewDecoder(rw.Body).Decode(&response) suite.Require().NoError(err) - walletID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()) + walletID := uuid.NewV5(wallet.ClaimNamespace, key.PublicHex()) suite.Assert().Equal(walletID.String(), response.PaymentID) } @@ -141,10 +140,10 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_GeoCountryDis request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - err = signRequest(request, publicKey, privateKey) + err = signRequest(request, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -152,7 +151,7 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_GeoCountryDis suite.Assert().Equal(http.StatusForbidden, rw.Code) - walletID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()) + walletID := uuid.NewV5(wallet.ClaimNamespace, key.PublicHex()) info, err := suite.storage.GetWallet(ctx, walletID) suite.Require().NoError(err) @@ -193,19 +192,20 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_WalletAlready request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - err = signRequest(request, publicKey, privateKey) + err = signRequest(request, key) suite.Require().NoError(err) // create existing wallet - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() + pubkeyHex := key.PublicHex() + paymentID := uuid.NewV5(wallet.ClaimNamespace, pubkeyHex).String() var altCurrency = altcurrency.BAT info := &walletutils.Info{ ID: paymentID, Provider: "brave", - PublicKey: publicKey.String(), + PublicKey: pubkeyHex, AltCurrency: &altCurrency, } @@ -264,10 +264,10 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_ReputationCal request := httptest.NewRequest(http.MethodPost, "/v4/wallets", bytes.NewBuffer(payload)) - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - err = signRequest(request, publicKey, privateKey) + err = signRequest(request, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -275,7 +275,7 @@ func (suite *WalletControllersV4TestSuite) TestCreateBraveWalletV4_ReputationCal suite.Assert().Equal(http.StatusInternalServerError, rw.Code) - walletID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()) + walletID := uuid.NewV5(wallet.ClaimNamespace, key.PublicHex()) info, err := suite.storage.GetWallet(ctx, walletID) suite.Require().NoError(err) @@ -301,16 +301,17 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_Success() { suite.Require().NoError(err) // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() + pubkeyHex := key.PublicHex() + paymentID := uuid.NewV5(wallet.ClaimNamespace, pubkeyHex).String() var altCurrency = altcurrency.BAT info := &walletutils.Info{ ID: paymentID, Provider: "brave", - PublicKey: publicKey.String(), + PublicKey: pubkeyHex, AltCurrency: &altCurrency, } @@ -332,7 +333,7 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_Success() { request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), bytes.NewBuffer(payload)) - err = signUpdateRequest(request, paymentID, privateKey) + err = signUpdateRequest(request, paymentID, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -350,10 +351,10 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_VerificationM service, err := wallet.InitService(storage, nil, nil, nil, nil, nil, nil, backoff.Retry, nil, nil, wallet.DAppConfig{}) suite.Require().NoError(err) - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() + paymentID := uuid.NewV5(wallet.ClaimNamespace, key.PublicHex()).String() router := chi.NewRouter() wallet.RegisterRoutes(ctx, service, router, noOpHandler(), noOpMw()) @@ -370,7 +371,7 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_VerificationM request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), bytes.NewBuffer(payload)) - err = signUpdateRequest(request, paymentID, privateKey) + err = signUpdateRequest(request, paymentID, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -389,16 +390,17 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_PaymentIDMism suite.Require().NoError(err) // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() + pubkeyHex := key.PublicHex() + paymentID := uuid.NewV5(wallet.ClaimNamespace, pubkeyHex).String() var altCurrency = altcurrency.BAT info := &walletutils.Info{ ID: paymentID, Provider: "brave", - PublicKey: publicKey.String(), + PublicKey: pubkeyHex, AltCurrency: &altCurrency, } @@ -420,7 +422,7 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_PaymentIDMism request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", uuid.NewV4()), bytes.NewBuffer(payload)) - err = signUpdateRequest(request, paymentID, privateKey) + err = signUpdateRequest(request, paymentID, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -456,16 +458,17 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_GeoCountryAlr suite.Require().NoError(err) // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() + pubkeyHex := key.PublicHex() + paymentID := uuid.NewV5(wallet.ClaimNamespace, pubkeyHex).String() var altCurrency = altcurrency.BAT info := &walletutils.Info{ ID: paymentID, Provider: "brave", - PublicKey: publicKey.String(), + PublicKey: pubkeyHex, AltCurrency: &altCurrency, } @@ -487,7 +490,7 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_GeoCountryAlr request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), bytes.NewBuffer(payload)) - err = signUpdateRequest(request, paymentID, privateKey) + err = signUpdateRequest(request, paymentID, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -521,16 +524,17 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_ReputationCal suite.Require().NoError(err) // create rewards wallet with public key - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() suite.Require().NoError(err) - paymentID := uuid.NewV5(wallet.ClaimNamespace, publicKey.String()).String() + pubkeyHex := key.PublicHex() + paymentID := uuid.NewV5(wallet.ClaimNamespace, pubkeyHex).String() var altCurrency = altcurrency.BAT info := &walletutils.Info{ ID: paymentID, Provider: "brave", - PublicKey: publicKey.String(), + PublicKey: pubkeyHex, AltCurrency: &altCurrency, } @@ -552,7 +556,7 @@ func (suite *WalletControllersV4TestSuite) TestUpdateBraveWalletV4_ReputationCal request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/v4/wallets/%s", paymentID), bytes.NewBuffer(payload)) - err = signUpdateRequest(request, paymentID, privateKey) + err = signUpdateRequest(request, paymentID, key) suite.Require().NoError(err) server := &http.Server{Addr: ":8080", Handler: router} @@ -649,12 +653,12 @@ func (suite *WalletControllersTestSuite) TestGetWalletV4_Not_Whitelisted() { suite.Assert().Equal(false, resp.SelfCustodyAvailable["solana"]) } -func signUpdateRequest(req *http.Request, paymentID string, privateKey ed25519.PrivateKey) error { +func signUpdateRequest(req *http.Request, paymentID string, privateKey httpsignature.Ed25519PrivKey) error { var s httpsignature.SignatureParams s.Algorithm = httpsignature.ED25519 s.KeyID = paymentID s.Headers = []string{"digest", "(request-target)"} - return s.Sign(privateKey, crypto.Hash(0), req) + return s.SignRequest(privateKey, req) } func noOpHandler() middleware.InstrumentHandlerDef { @@ -671,10 +675,10 @@ func noOpMw() func(next http.Handler) http.Handler { } } -func signRequest(req *http.Request, publicKey httpsignature.Ed25519PubKey, privateKey ed25519.PrivateKey) error { +func signRequest(req *http.Request, key httpsignature.Ed25519PrivKey) error { var s httpsignature.SignatureParams s.Algorithm = httpsignature.ED25519 - s.KeyID = hex.EncodeToString(publicKey) + s.KeyID = key.PublicHex() s.Headers = []string{"digest", "(request-target)"} - return s.Sign(privateKey, crypto.Hash(0), req) + return s.SignRequest(key, req) } diff --git a/tools/settlement/settlement_test.go b/tools/settlement/settlement_test.go index b3a89910a..68e231fb8 100644 --- a/tools/settlement/settlement_test.go +++ b/tools/settlement/settlement_test.go @@ -13,7 +13,6 @@ import ( "github.com/brave-intl/bat-go/libs/wallet" "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" "github.com/shopspring/decimal" - "golang.org/x/crypto/ed25519" ) func TestTransactions(t *testing.T) { @@ -48,7 +47,7 @@ func TestTransactions(t *testing.T) { donorWalletPublicKeyHex := os.Getenv("DONOR_WALLET_PUBLIC_KEY") donorWalletPrivateKeyHex := os.Getenv("DONOR_WALLET_PRIVATE_KEY") var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey + var donorPrivateKey httpsignature.Ed25519PrivKey donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) if err != nil { t.Fatal(err) @@ -151,7 +150,7 @@ func TestDeterministicSigning(t *testing.T) { donorWalletPublicKeyHex := "10ba999b2b7b9eabc0f44fa26bf122ebbfa98dc6fef31e6251a9c1c58d60bb8d" donorWalletPrivateKeyHex := "8d6a620a566e094cebaec67edca32a68efce962890570157f0b8a5389cc5f6df10ba999b2b7b9eabc0f44fa26bf122ebbfa98dc6fef31e6251a9c1c58d60bb8d" var donorPublicKey httpsignature.Ed25519PubKey - var donorPrivateKey ed25519.PrivateKey + var donorPrivateKey httpsignature.Ed25519PrivKey donorPublicKey, err := hex.DecodeString(donorWalletPublicKeyHex) if err != nil { t.Fatal(err) diff --git a/tools/vault/cmd/create_wallet.go b/tools/vault/cmd/create_wallet.go index 458e4ff4b..c56cdb9ef 100644 --- a/tools/vault/cmd/create_wallet.go +++ b/tools/vault/cmd/create_wallet.go @@ -15,7 +15,6 @@ import ( "github.com/brave-intl/bat-go/libs/wallet/provider/uphold" vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" "github.com/spf13/cobra" - "golang.org/x/crypto/ed25519" ) // State contains the current state of the registration @@ -138,7 +137,7 @@ func CreateWallet(command *cobra.Command, args []string) error { return err } - wallet := uphold.Wallet{Info: state.WalletInfo, PrivKey: ed25519.PrivateKey{}, PubKey: publicKey} + wallet := uphold.Wallet{Info: state.WalletInfo, PrivKey: httpsignature.Ed25519PrivKey{}, PubKey: publicKey} err = wallet.SubmitRegistration(ctx, state.Registration) if err != nil { diff --git a/tools/vault/cmd/import_key.go b/tools/vault/cmd/import_key.go index 395af6cf0..a436226ce 100644 --- a/tools/vault/cmd/import_key.go +++ b/tools/vault/cmd/import_key.go @@ -10,6 +10,7 @@ import ( cmdutils "github.com/brave-intl/bat-go/cmd" appctx "github.com/brave-intl/bat-go/libs/context" + "github.com/brave-intl/bat-go/libs/httpsignature" vaultsigner "github.com/brave-intl/bat-go/tools/vault/signer" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -91,7 +92,6 @@ func ImportKey(command *cobra.Command, args []string) error { ReadConfig(command) walletRefs := viper.GetViper().GetStringSlice("wallet-refs") ed25519PrivateKey := viper.GetViper().GetString("ed25519-private-key") - ed25519PublicKey := viper.GetViper().GetString("ed25519-public-key") upholdProviderID := viper.GetViper().GetString("uphold-provider-id") geminiClientID := viper.GetViper().GetString("gemini-client-id") geminiClientKey := viper.GetViper().GetString("gemini-client-key") @@ -106,13 +106,12 @@ func ImportKey(command *cobra.Command, args []string) error { parts := strings.Split(key, "-") switch parts[0] { case "uphold": - if len(ed25519PrivateKey) != 0 && len(ed25519PublicKey) != 0 { + if len(ed25519PrivateKey) != 0 { err = upholdVaultImportKey( command.Context(), wrappedClient, key, ed25519PrivateKey, - ed25519PublicKey, upholdProviderID, ) if err != nil { @@ -148,7 +147,6 @@ func upholdVaultImportKey( wrappedClient *vaultsigner.WrappedClient, key string, ed25519PrivateKey string, - ed25519PublicKey string, upholdProviderID string, ) error { logger, err := appctx.GetLogger(ctx) @@ -156,12 +154,8 @@ func upholdVaultImportKey( return err } importName := Config.GetWalletKey(key) - privKey, err := hex.DecodeString(ed25519PrivateKey) - if err != nil { - return errors.New("ERROR: Key material must be passed as hex") - } - - pubKey, err := hex.DecodeString(ed25519PublicKey) + var privKey httpsignature.Ed25519PrivKey + privKey, err = hex.DecodeString(ed25519PrivateKey) if err != nil { return errors.New("ERROR: Key material must be passed as hex") } @@ -173,10 +167,9 @@ func upholdVaultImportKey( Str("provider", "uphold"). Str("config-key", key). Str("vault-key", importName). - Int("public-length", len(pubKey)). Int("private-length", len(privKey)). Msg("importing secret") - _, err = wrappedClient.FromKeypair(privKey, pubKey, importName) + _, err = wrappedClient.FromKey(privKey, importName) if err != nil { return err } diff --git a/tools/vault/signer/ed25519signer.go b/tools/vault/signer/ed25519signer.go index d03f5a976..2d79f0746 100644 --- a/tools/vault/signer/ed25519signer.go +++ b/tools/vault/signer/ed25519signer.go @@ -1,16 +1,14 @@ package vaultsigner import ( - "crypto" "encoding/base64" "encoding/hex" "fmt" - "io" "strconv" "strings" + "github.com/brave-intl/bat-go/libs/httpsignature" "github.com/hashicorp/vault/api" - "golang.org/x/crypto/ed25519" ) // Ed25519Signer signer / verifier that uses the vault transit backend @@ -21,7 +19,7 @@ type Ed25519Signer struct { } // Sign the included message using the vault held keypair. rand and opts are not used -func (vs *Ed25519Signer) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) { +func (vs *Ed25519Signer) SignMessage(message []byte) ([]byte, error) { response, err := vs.Client.Logical().Write("transit/sign/"+vs.KeyName, map[string]interface{}{ "input": base64.StdEncoding.EncodeToString(message), }) @@ -35,25 +33,29 @@ func (vs *Ed25519Signer) Sign(rand io.Reader, message []byte, opts crypto.Signer } // Verify the included signature over message using the vault held keypair. opts are not used -func (vs *Ed25519Signer) Verify(message, signature []byte, opts crypto.SignerOpts) (bool, error) { +func (vs *Ed25519Signer) VerifySignature(message, signature []byte) error { response, err := vs.Client.Logical().Write("transit/verify/"+vs.KeyName, map[string]interface{}{ "input": base64.StdEncoding.EncodeToString(message), "signature": fmt.Sprintf("vault:v%d:%s", vs.KeyVersion, base64.StdEncoding.EncodeToString(signature)), }) if err != nil { - return false, err + return err } - return response.Data["valid"].(bool), nil + valid := response.Data["valid"].(bool) + if !valid { + return httpsignature.ErrBadSignature + } + return nil } // String returns the public key as a hex encoded string func (vs *Ed25519Signer) String() string { - return hex.EncodeToString(vs.Public().(ed25519.PublicKey)) + return hex.EncodeToString(vs.Public()) } // Public returns the public key -func (vs *Ed25519Signer) Public() crypto.PublicKey { +func (vs *Ed25519Signer) Public() httpsignature.Ed25519PubKey { response, err := vs.Client.Logical().Read("transit/keys/" + vs.KeyName) if err != nil { panic(err) @@ -67,5 +69,5 @@ func (vs *Ed25519Signer) Public() crypto.PublicKey { panic(err) } - return ed25519.PublicKey(publicKey) + return httpsignature.Ed25519PubKey(publicKey) } diff --git a/tools/vault/signer/vaultsigner.go b/tools/vault/signer/vaultsigner.go index 18e72a303..67bb17b62 100644 --- a/tools/vault/signer/vaultsigner.go +++ b/tools/vault/signer/vaultsigner.go @@ -5,11 +5,11 @@ import ( "encoding/base64" "time" + "github.com/brave-intl/bat-go/libs/httpsignature" "github.com/hashicorp/vault/api" util "github.com/hashicorp/vault/command/config" "github.com/hashicorp/vault/sdk/helper/jsonutil" "github.com/hashicorp/vault/sdk/helper/keysutil" - "golang.org/x/crypto/ed25519" ) // WrappedClient holds an api client for interacting with vault @@ -17,14 +17,14 @@ type WrappedClient struct { Client *api.Client } -// FromKeypair create a new vault transit key by importing privKey and pubKey under importName -func (wc *WrappedClient) FromKeypair(privKey ed25519.PrivateKey, pubKey ed25519.PublicKey, importName string) (*Ed25519Signer, error) { +// FromKey create a new vault transit key by importing privKey under importName +func (wc *WrappedClient) FromKey(privKey httpsignature.Ed25519PrivKey, importName string) (*Ed25519Signer, error) { client := wc.Client key := keysutil.KeyEntry{} key.Key = privKey - pk := base64.StdEncoding.EncodeToString(pubKey) + pk := base64.StdEncoding.EncodeToString(privKey.Public()) key.FormattedPublicKey = pk { diff --git a/tools/vault/signer/vaultsigner_test.go b/tools/vault/signer/vaultsigner_test.go index 7e5b22f34..0a488b17f 100644 --- a/tools/vault/signer/vaultsigner_test.go +++ b/tools/vault/signer/vaultsigner_test.go @@ -4,75 +4,51 @@ package vaultsigner import ( - "crypto" - "crypto/rand" "testing" + "github.com/brave-intl/bat-go/libs/httpsignature" uuid "github.com/satori/go.uuid" - "golang.org/x/crypto/ed25519" + "github.com/stretchr/testify/assert" ) func TestSign(t *testing.T) { wrappedClient, err := Connect() - if err != nil { - t.Fatal(err) - } - - publicKey, privateKey, err := ed25519.GenerateKey(nil) - if err != nil { - t.Fatal(err) - } + assert.NoError(t, err) + + key, err := httpsignature.GenerateEd25519Key() + assert.NoError(t, err) + name := uuid.NewV4() - signer, err := wrappedClient.FromKeypair(privateKey, publicKey, "vaultsigner-test-"+name.String()) - if err != nil { - t.Fatal(err) - } + signer, err := wrappedClient.FromKey(key, "vaultsigner-test-"+name.String()) + assert.NoError(t, err) message := []byte("hello world") - signature, err := signer.Sign(rand.Reader, message, crypto.Hash(0)) - if err != nil { - t.Fatal(err) - } + signature, err := signer.SignMessage(message) + assert.NoError(t, err) - if !ed25519.Verify(publicKey, message, signature) { - t.Fatal("Signature did not match") - } + err = key.Public().VerifySignature(message, signature) + assert.NoError(t, err) } func TestVerify(t *testing.T) { wrappedClient, err := Connect() - if err != nil { - t.Fatal(err) - } - - publicKey, privateKey, err := ed25519.GenerateKey(nil) - if err != nil { - t.Fatal(err) - } + assert.NoError(t, err) + + key, err := httpsignature.GenerateEd25519Key() + assert.NoError(t, err) name := uuid.NewV4() - if err != nil { - t.Fatal(err) - } + assert.NoError(t, err) - signer, err := wrappedClient.FromKeypair(privateKey, publicKey, "vaultsigner-test-"+name.String()) - if err != nil { - t.Fatal(err) - } + signer, err := wrappedClient.FromKey(key, "vaultsigner-test-"+name.String()) + assert.NoError(t, err) message := []byte("hello world") - signature, err := privateKey.Sign(rand.Reader, message, crypto.Hash(0)) - if err != nil { - t.Fatal(err) - } - - valid, err := signer.Verify(message, signature, crypto.Hash(0)) - if err != nil { - t.Fatal(err) - } - if !valid { - t.Fatal("Signature should be valid") - } + signature, err := key.SignMessage(message) + assert.NoError(t, err) + + err = signer.VerifySignature(message, signature) + assert.NoError(t, err) } diff --git a/tools/wallet/cmd/create.go b/tools/wallet/cmd/create.go index 97ccd5663..794961fbd 100644 --- a/tools/wallet/cmd/create.go +++ b/tools/wallet/cmd/create.go @@ -69,13 +69,13 @@ func CreateOnUphold(ctx context.Context, name string) error { _, logger = logging.SetupLogger(ctx) } - publicKey, privateKey, err := httpsignature.GenerateEd25519Key(nil) + key, err := httpsignature.GenerateEd25519Key() if err != nil { return err } - publicKeyHex := hex.EncodeToString([]byte(publicKey)) + publicKeyHex := key.PublicHex() - privateKeyHex := hex.EncodeToString([]byte(privateKey)) + privateKeyHex := hex.EncodeToString(key) logger.Info(). Str("public_key", publicKeyHex). Str("private_key", privateKeyHex). @@ -91,7 +91,7 @@ func CreateOnUphold(ctx context.Context, name string) error { } info.PublicKey = publicKeyHex - wallet := &uphold.Wallet{Info: info, PrivKey: privateKey, PubKey: publicKey} + wallet := &uphold.Wallet{Info: info, PrivKey: key, PubKey: key.Public()} err = wallet.Register(ctx, name) if err != nil { diff --git a/tools/wallet/cmd/transfer_funds.go b/tools/wallet/cmd/transfer_funds.go index 8ff8ddc02..fd214008d 100644 --- a/tools/wallet/cmd/transfer_funds.go +++ b/tools/wallet/cmd/transfer_funds.go @@ -3,7 +3,6 @@ package cmd import ( "bufio" "context" - "crypto" "encoding/hex" "encoding/json" "errors" @@ -23,7 +22,6 @@ import ( "github.com/shopspring/decimal" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "golang.org/x/crypto/ed25519" ) var ( @@ -156,7 +154,7 @@ func RunTransferFunds(command *cobra.Command, args []string) error { ) } -func pullRequisiteSecrets(from string, usevault bool) (string, crypto.Signer, error) { +func pullRequisiteSecrets(from string, usevault bool) (string, httpsignature.Ed25519Signator, error) { if usevault { return pullRequisiteSecretsFromVault(from) } @@ -168,7 +166,7 @@ func pullRequisiteSecrets(from string, usevault bool) (string, crypto.Signer, er return providerID, privateKey, err } -func pullRequisiteSecretsFromPrompt(from string) (string, crypto.Signer, error) { +func pullRequisiteSecretsFromPrompt(from string) (string, httpsignature.Ed25519Signator, error) { log.Println("Enter your recovery phrase:") reader := bufio.NewReader(os.Stdin) recoveryPhrase, err := reader.ReadString('\n') @@ -186,17 +184,17 @@ func pullRequisiteSecretsFromPrompt(from string) (string, crypto.Signer, error) return "", nil, err } - return from, key, nil + return from, httpsignature.Ed25519PrivKey(key), nil } -func pullRequisiteSecretsFromEnv(from string) (string, crypto.Signer, error) { +func pullRequisiteSecretsFromEnv(from string) (string, httpsignature.Ed25519Signator, error) { privateKeyHex := os.Getenv("ED25519_PRIVATE_KEY") if len(privateKeyHex) == 0 { return "", nil, nil } - var privKey ed25519.PrivateKey + var privKey httpsignature.Ed25519PrivKey privKey, err := hex.DecodeString(privateKeyHex) if err != nil { return "", nil, errors.New("Key material must be passed as hex") @@ -205,7 +203,7 @@ func pullRequisiteSecretsFromEnv(from string) (string, crypto.Signer, error) { return from, privKey, nil } -func pullRequisiteSecretsFromVault(from string) (string, *vaultsigner.Ed25519Signer, error) { +func pullRequisiteSecretsFromVault(from string) (string, httpsignature.Ed25519Signator, error) { wrappedClient, err := vaultsigner.Connect() if err != nil { return "", nil, err @@ -260,7 +258,7 @@ func TransferFunds( walletc := altcurrency.BAT var info wallet.Info - info.PublicKey = hex.EncodeToString(signer.Public().(ed25519.PublicKey)) + info.PublicKey = hex.EncodeToString(signer.Public()) info.Provider = "uphold" info.ProviderID = providerID { @@ -268,13 +266,7 @@ func TransferFunds( info.AltCurrency = &tmp } - var pubKey httpsignature.Ed25519PubKey - pubKey, err = hex.DecodeString(info.PublicKey) - if err != nil { - return err - } - - w, err := uphold.New(ctx, info, signer, pubKey) + w, err := uphold.New(ctx, info, signer, signer.Public()) if err != nil { return err }