Skip to main content
Tweeted twitter.com/StackMma/status/910636961458786304
edited title
Link
Ray Troy
  • 1.3k
  • 7
  • 15

Cryptocurrencies Exchange (GDAX) API HTTP Request Signing using base64 encoding/decoding and HMAC (Cryptocurrencies Exchange, GDAX)

Source Link
Ray Troy
  • 1.3k
  • 7
  • 15

Cryptocurrencies Exchange (GDAX) API Request Signing using base64 encoding/decoding and HMAC

I am trying to access the GDAX API private methods which can be seen here, GDAX Documentation

There is some great Python code that does it that I am trying to recreate in Mathematica. That code can be seen here and below.

import base64, hashlib, hmac, time
timestamp = str(time.time())
message = timestamp + request.method + request.path_url + (request.body or '')
hmac_key = base64.b64decode(secret_key)
signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
signature_b64 = base64.b64encode(signature.digest())
headers = {
    'CB-ACCESS-SIGN': signature_b64,
    'CB-ACCESS-TIMESTAMP': timestamp,
    'CB-ACCESS-KEY': api_key,
    'CB-ACCESS-PASSPHRASE': passphrase,
    'Content-Type': 'application/json'
}
# add in headers when we create a request
requests.post(order_url, data=data, headers=headers)

Here is what I have so far:

gdaxGetBalancesF[APIKey_, APISecret_, passphrase_] := Module[
  {URL1, nonce, response, body},
  URL1 = URLBuild[{"https://api.gdax.com", "accounts"}];
  nonce = ToString[UnixTime[]];
  body = "";
  response = 
   gdaxPayloadResponseF[URL1, "GET", nonce, body, APIKey, APISecret,passphrase]
  ]

gdaxPayloadResponseF[url_, type_, nonce_, body_, APIKey_, APISecret_, passphrase_] := Module[
  {query, message, gdaxAPISecret, request, response},
  query = URLParse[url];
  message = nonce <> type <> URLBuild[query[[{"Path"}]]] <> body;
  (*decode APISecret*)
  gdaxAPISecret = ImportString[APISecret, {"Base64", "String"}];
  request =
   HTTPRequest[ 
    URLBuild[query[[{"Scheme", "Domain", "Path"}]]],
    <|
     Method -> type, 
     "ContentType" -> "application/json",
     "Body" -> body,
     "Headers" -> 
      <|
       "CB-ACCESS-KEY" -> APIKey,
       "CB-ACCESS-SIGN" -> fixBase64[ExportString[hmacF["SHA256", message,gdaxAPISecret,{"Base64","String"}]],
       "CB-ACCESS-TIMESTAMP" -> nonce,
       "CB-ACCESS-PASSPHRASE" -> passphrase
       |>
     |>
    ];
  response = URLExcute[request];
  ]

fixBase64[string_] := StringTrim[StringReplace[string, {"\n"} -> ""]]

hmacF[method_, message_, key_] := Module[
  {keyLen, dkey, opad, ipad, blocksize},
  blocksize =If[method === "SHA384" || method === "SHA512",128,64];
  keyLen = StringLength[key];
  dkey = If[keyLen > blocksize,
    IntegerString[Hash[key, method], 16],
    StringPadRight[key, blocksize, FromCharacterCode[0]]
    ];
  {opad, ipad} = FromCharacterCode[BitXor[ToCharacterCode@StringRepeat[FromCharacterCode[FromDigits[#, 16]], blocksize],ToCharacterCode@dkey]] & /@ {"5c", "36"};   
 IntegerString[Hash[StringJoin[opad,ExportString[IntegerDigits[Hash[StringJoin[ipad, message], method], 256],"Binary"]],method],16]
       ]

I always get the signing error invalid signature.

<|StatusCode->400,Body->{"message":"invalid signature"}|>

I think the errors are in two places that I can't seem to figure out. First is decoding the APISecret, see here and then getting that to work with a HMAC mathematica implementation. So far this hmac function has worked on all my requests that need to be signed but none of those request required base64 encoding and decoding. It looks like the python implementation works on byte arrays while this current MMA implementation uses strings. I am really stuck and would like to avoid doing this in JLink/Java.