From 4a08497b52159cae277aa72261054ed0696634be Mon Sep 17 00:00:00 2001 From: Camille Meulien Date: Mon, 7 Dec 2020 22:42:16 +0200 Subject: [PATCH] Add the support of OpenDAX HMAC authentication method --- go.sum | 1 + pkg/auth/hmac.go | 47 +++++++++++++++++++++++++++++++++++++++++++ pkg/auth/hmac_test.go | 34 +++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 pkg/auth/hmac.go create mode 100644 pkg/auth/hmac_test.go diff --git a/go.sum b/go.sum index a4d830a..221f52a 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/pkg/auth/hmac.go b/pkg/auth/hmac.go new file mode 100644 index 0000000..ad8895e --- /dev/null +++ b/pkg/auth/hmac.go @@ -0,0 +1,47 @@ +package auth + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" + "net/http" + "time" +) + +// APIKeyHMAC contains API keys credential to authenticate to HTTP API using HMAC +type APIKeyHMAC struct { + AccessKey string + SecretKey string +} + +// NewAPIKeyHMAC creates an instance of APIKeyHMAC +func NewAPIKeyHMAC(accessKey, secretKey string) *APIKeyHMAC { + return &APIKeyHMAC{ + AccessKey: accessKey, + SecretKey: secretKey, + } +} + +// GetSignature return a signature for the given nonce, if nonce is zero it use the current time in millisecond +func (key *APIKeyHMAC) GetSignature(nonce int64) string { + if nonce == 0 { + nonce = int64(time.Now().UnixNano() * 1000000) + } + mac := hmac.New(sha256.New, []byte(key.SecretKey)) + mac.Write([]byte(fmt.Sprintf("%d%s", nonce, key.AccessKey))) + return hex.EncodeToString(mac.Sum(nil)) +} + +// GetSignedHeader returns a header with valid HMAC authorization fields +func (key *APIKeyHMAC) GetSignedHeader(nonce int64) http.Header { + if nonce == 0 { + nonce = int64(time.Now().UnixNano() * 1000000) + } + + return http.Header{ + "X-Auth-Apikey": {key.AccessKey}, + "X-Auth-Nonce": {fmt.Sprintf("%d", nonce)}, + "X-Auth-Signature": {key.GetSignature(nonce)}, + } +} diff --git a/pkg/auth/hmac_test.go b/pkg/auth/hmac_test.go new file mode 100644 index 0000000..546d741 --- /dev/null +++ b/pkg/auth/hmac_test.go @@ -0,0 +1,34 @@ +package auth + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAPIKeyHMACGetSignature(t *testing.T) { + accessKey := "61d025b8573501c2" + secretKey := "2d0b4979c7fe6986daa8e21d1dc0644f" + + k := NewAPIKeyHMAC(accessKey, secretKey) + nonce := int64(1584524005143) + signature := k.GetSignature(nonce) + assert.Equal(t, "bd42b945e095880e28d046846dbecf655fdf09d95a396a24fe6fe1df42f15d13", signature) +} + +func TestAPIKeyHMACGetSignedHeader(t *testing.T) { + accessKey := "61d025b8573501c2" + secretKey := "2d0b4979c7fe6986daa8e21d1dc0644f" + + k := NewAPIKeyHMAC(accessKey, secretKey) + nonce := int64(1584524005143) + headers := k.GetSignedHeader(nonce) + + assert.Equal(t, + http.Header{ + "X-Auth-Apikey": {accessKey}, + "X-Auth-Nonce": {"1584524005143"}, + "X-Auth-Signature": {"bd42b945e095880e28d046846dbecf655fdf09d95a396a24fe6fe1df42f15d13"}, + }, headers) +}