Crypto.HMAC issues

Greetings,

I am trying to have the Whatsapp API implemented on my side for a web app ,and on the docs they say and i quote

Validating Payloads

We sign all Event Notification payloads with a SHA256 signature and include the signature in the request’s X-Hub-Signature-256 header, preceded with sha256=. You don’t have to validate the payload, but you should.

To validate the payload:

  1. Generate a SHA256 signature using the payload and your app’s App Secret.
  2. Compare your signature to the signature in the X-Hub-Signature-256 header (everything after sha256=). If the signatures match, the payload is genuine.

found on this Link

Now, on my side i get the payload and i use it to generate the hash but, the payload is wrong

Var payloadMB As MemoryBlock = payload
Var hmac As MemoryBlock = Crypto.HMAC(wpT, payloadMB, Crypto.HashAlgorithms.SHA256)
Var hmacString As String = hmac.StringValue(0, hmac.Size)
Var generatedSignature As String = EncodeHex(hmacString).Lowercase

As a sample data i have the signature

signature256 : 4591c0e7d6f4107932b3c497153a4b9845b99e6893ed308e6224e2a879ad680f

the payload

payload : {“object”:“whatsapp_business_account”,“entry”:[{“id”:“0”,“changes”:[{“field”:“message_echoes”,“value”:{“messaging_product”:“whatsapp”,“metadata”:{“display_phone_number”:“16505551111”,“phone_number_id”:“123456123”},“message_echoes”:[{“from”:“16315551181”,“to”:“11234567890”,“id”:“ABGGFlA5Fpa”,“timestamp”:“1504902988”,“type”:“text”,“text”:{“body”:“this is a text message”}}]}}]}]}

the hmacString

hmacString : –uá Qó÷Ù{v§ì°,æ% E&oÙªfÁe

and the generated signature

generatedSignature : 129675e16011a05160f32a03f7d97b76a7ecb02ce6252045266fd9aa66c1652a

Which they don’t really match, so the question would be, What XOJO is using as HMAC ? is there a different version in the Crypto module ? as it seems that the signature does not match the generated one.

Thanks

The hmacString you posted is binary. What does it look like if you EncodeHex?

Hi Kem, Payload is a JSON hmacString is the restul of the Crypto.HMAC based on their requirements, and generatedSignature is the EncodeHex value so what should i encode in hex ?

That value.

i’m confuse, did you see the code i posted ? the last 2 lines ?

Var hmacString As String = hmac.StringValue(0, hmac.Size)
Var generatedSignature As String = EncodeHex(hmacString).Lowercase

should i do more than that ?

The confusion is mine. I did see but misunderstood.

i did tried the example from XOJO in Cryptography → Crypto and same result so i guess XOJO is using either something old or the way they doit does not match .

seeing their Java example on the Meta website, in theory code is same

// Import dependencies and set up http server
const express = require("express"),
  bodyParser = require("body-parser"),
  { urlencoded, json } = require("body-parser"),
  app = express().use(bodyParser.json());
    
    ...


// Verify that the callback came from Facebook.
function verifyRequestSignature(req, res, buf) {
  var signature = req.headers["x-hub-signature-256"];

  if (!signature) {
    console.warn(`Couldn't find "x-hub-signature-256" in headers.`);
  } else {
    var elements = signature.split("=");
    var signatureHash = elements[1];
    var expectedHash = crypto
      .createHmac("sha256", config.appSecret)
      .update(buf)
      .digest("hex");
    if (signatureHash != expectedHash) {
      throw new Error("Couldn't validate the request signature.");
    }
  }
}

but apparently it does output different result.

I’d try a few things:

  • Include or exclude the payload: key.
  • In the HMAC call, switch the parameters for private key and data.

If you’re willing to share the private key (or some private key), I’d be happy to test here.

BTW, you don’t have to assign to a MemoryBlock first, you can assign directly to a String variable. The conversion in both directions is automatic.

Var hmacString As String = _
    Crypto.HMAC(wpT, payload, Crypto.HashAlgorithms.SHA256)

I don’t have issues validating signatures with HMAC SHA256 in Web 1.0 2019r1.1.

I wonder what your payload variable is and the transformations it might have had by the point you pass it to HMAC. Passing the WebRequest.Body should work since it’s immutable.

1 Like

well that is the actual payload, WebRequest.Body, payload was just for debugging so i see what i get, but so far nothing else .

that’s odd , as in the docs it requires a memoryblock to be passed and it spits out a memory block , at least this is what the docs say

HMAC (key As String, data As MemoryBlock, hashAlgorithm As Crypto.HashAlgorithms) As MemoryBlock

Right, it does, but Xojo will convert to/from String for you.

Xojo’s HMAC implementation works correctly, the issue is something to do with the inputs. Perhaps there is a newline added to the end of WebRequest.Body that needs to be trimmed? Or maybe the key needs to be de-hexed?

1 Like

Did you happen to split the payload/header value?

Payload = payload.Nthfield(“=”,2)

Before using it?

I’m not sure i get it, why should i split the payload when the actual data that is. there is just the JSON, please see above, remove the payload : and what is in the right of it is the actual payload, just the JSON,

And to make it clear, test code below

Var key As String


Select Case cbHex.Value
Case True
  If cbLC.Value Then
    key = EncodeHex(tfKey.Text.Trim).Lowercase
  Else
    key = EncodeHex(tfKey.Text.Trim)
  End If
  
Case False
  key = tfKey.Text
End Select

If taPayload.Text <> "" Then
  Var hmac As MemoryBlock = Crypto.HMAC(key, taPayload.Text.Trim, Crypto.HashAlgorithms.SHA256)
  Var hmacString As String = hmac.StringValue(0, hmac.Size)
  lgenSig.Text = EncodeHex(hmacString).Lowercase
  
  
End If

Result with key in clear text

Result with Key as HEX

Result with Key as Hex.Lowercase

None match unfortunately.

and i just saw this in heir memo

Please note that we generate the signature using an escaped unicode version of the payload, with lowercase hex digits. If you just calculate against the decoded bytes, you will end up with a different signature. For example, the string äöå should be escaped to \u00e4\u00f6\u00e5 .

but in my case i made sure that all the special characters are removed for test purposes, so not sure that this is related here, on the key side and on the payload side i did a method to escape all those but the result is same

Your key is always being hex encoded. The only change your checkbox makes is whether or not it is lowercase. But I’m confident that both cases are wrong. If your key is hex encoded, you would need to decode it, not encode it again. So if the checkbox is checked, use DecodeHex, and if it is not, use the field value as-is. Since we can’t see your key, I really can’t tell if the key is hex encoded or not.

Next, try with and without the trim on the body. Every single bit will change the result, so we’re trying to figure out what the difference is.

Thom,

The key is generated by me as it is needed for the webhook part and it is not HexEncoded, i just encode it to do a test to see if result will change but in the docs does not say anything about hex encoding of the key and in my account side i add that as normal not encoded.

then in my case the code Encodes it and makes it lower case to test all the scenarios.

And indeed , removing the Trim, changed few things and to make it easier , i changed the key so this is the old key that should match the payload

ywLwFaCyD9DyGHWsDP2uUPnpH3KwaQ

Did you read theyr docs?
The header value has something like “sha256=” which you need to ditch. Check the java value.

Since i don’t see you removing that i asked it

well, does the payload i just provided has that ? i guess not, so it means that it was removed and what it was stored was just the needed payload. Maybe there is something bad there. But so far i work with what i have .