3
$\begingroup$

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.

$\endgroup$
  • 1
    $\begingroup$ There are a couple of typos in the posted Wolfram Language code. I think it should read: ExportString[hmacF["SHA256", message,gdaxAPISecret],{"Base64","String"}]] and URLExecute. Also, you might try using DeveloperEncodeBase64` instead of ExportString. It's difficult to reproduce the issue without having a GDAX account. Comparing the output of fixBase64[...] with that from python might be useful. $\endgroup$ – user1473011 Sep 21 '17 at 14:14
1
$\begingroup$

Try changing

IntegerString[Hash[StringJoin[opad,ExportString[IntegerDigits[Hash[StringJoin[ipad, message], method], 256],"Binary"]],method],16]

to

Developer`EncodeBase64[
 FromCharacterCode[
  IntegerDigits[
   Hash[
    StringJoin[
     opad,
     ExportString[
      IntegerDigits[
        Hash[
        StringJoin[ipad, message],
        "SHA512"
        ],
       256
       ],
      "Binary"
      ]
     ],
    "SHA512"
    ],
   256
   ]
  ]
 ]

This returns a signature that is Base64 encoded. It's what my API required

Also, I would consider changing all your ExportString[#,{"Base64","String"}] to Developer`EncodeBase64[#]. The ExportString function looks like it adds some formatting to the output.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.