2

I'm attempting to integrate the Coinbase Pro API into my program with Delphi 10.2.

The Coinbase Pro API requires four different headers, one of them being a signature that's encrypted. I'm having issues forming a valid signature. According to the Coinbase Pro API Documentation:

The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp + method + requestPath + body (where + represents string concatenation) and base64-encode the output. The timestamp value is the same as the CB-ACCESS-TIMESTAMP header.The body is the request body string or omitted if there is no request body (typically for GET requests).The method should be UPPER CASE.

I know the timestamp is correct because I'm getting it from the coinbase server first, then passing it into the signature. So I'm guessing it has something to do with the base64 decoding/encoding or the sha256 hmac.

My signature functions is:

function Signature(RequestPath, TimeStamp, Method : String) : ANSIString;
var
  skeydecode : AnsiString;
  sha256     : AnsiString;
  prehash    : AnsiString;
  Body       : String;

begin
  Body := '';

  prehash    :=  TimeStamp + Method + RequestPath + Body ;

////Option 1 - [Coinbase] Invalid Signature
  skeydecode := TIdDecoderMIME.DecodeString(APISecret);
  sha256     := trim(CalculateHMACSHA256(prehash ,skeydecode));

  result     := Tidencodermime.EncodeString(sha256);

end;

And then the HMAC function that I'm trying to use is:

function CalculateHMACSHA256(const value, salt: String): String;
var
  hmac: TIdHMACSHA256;
  hash: TIdBytes;
begin
  LoadOpenSSLLibrary;
  if not TIdHashSHA256.IsAvailable then
    raise Exception.Create('SHA256 hashing is not available!');
  hmac := TIdHMACSHA256.Create;
  try
    hmac.Key := IndyTextEncoding_UTF8.GetBytes(salt);
    hash := hmac.HashValue(IndyTextEncoding_UTF8.GetBytes(value));
    Result := ToHex(hash);
  finally
    hmac.Free;
  end;
end;

I'm successfully connecting to the Coinbase API through a REST request because I was able to connect to the time endpoint, but that didn't require the signature header.

When trying to include the signature header in other REST requests, The coinbase api returns Invalid Signature.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Mike W
  • 21
  • 2
  • 1
    Welcome to StackOverflow. You need to edit your q to make it clear exactly what you are asking for help with, i.e. state what the symptoms of your problem are, what if any error messages you're getting, etc. – MartynA May 19 '20 at 17:01

1 Answers1

3

Don't use AnsiString for binary data. Your data is going through Ansi<->Unicode conversions.

Decode the base64 key to bytes and use them as-is for the hmac key, then base64 encode the resulting bytes as-is, not a hex-encoded representation of the bytes.

Try this:

function CalculateHMACSHA256(const value: String; const salt: TIdBytes): TIdBytes;
var
  hmac: TIdHMACSHA256;
begin
  LoadOpenSSLLibrary;
  if not TIdHashSHA256.IsAvailable then
    raise Exception.Create('SHA256 hashing is not available!');
  hmac := TIdHMACSHA256.Create;
  try
    hmac.Key := salt;
    Result := hmac.HashValue(IndyTextEncoding_UTF8.GetBytes(value));
  finally
    hmac.Free;
  end;
end;

function Signature(const RequestPath, TimeStamp, Method : String) : String;
var
  key : TIdBytes;
  sha256 : TIdBytes;
  prehash : String;
  Body : String;
begin
  Body := '';
  prehash := TimeStamp + UpperCase(Method) + RequestPath + Body;
  key := TIdDecoderMIME.DecodeBytes(APISecret);
  sha256 := CalculateHMACSHA256(prehash, key);
  Result := TIdEncoderMIME.EncodeBytes(sha256);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770