Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HMAC MD5 Validation with Node.js, Express and Trialpay

I'm trying to authenticate a message sent from TrialPay using Node.js and Express. TrialPay signs requests with an HMAC-MD5 hash, and provides these instructions on validating.

This is my code:

app.post('/trialpay', function(req, res) {

    var key = "[MY MERCHANT KEY]";
    var hash = req.header("TrialPay-HMAC-MD5");
    var data = req.body.toString();

    var crypted = require("crypto").createHmac("md5", key)
        .update(data)
        .digest("hex");

    if (hash == crypted) {
        res.writeHead(200, {"Content-Type": "plain/text"});
        res.end("Success!");
    } else {
        throw new Error("Invalid TrialPay Hash");
    }  
});

This is, obviously, not working (hash doesn't match).

Disclaimer: I'm extremely new to Node.js, and have little Javascript experience, to begin with.

UPDATE

I did not realize that the link was protected.

TrialPay uses your Notification-Key (set in your account information) as the secret key to sign the HMAC. For GET requests the query string that follows the question mark (in the URL) is signed. For POST requests the entire POST body is signed.

Here is an example of how TrialPay instructs you to validate in Google App Engine (Python):

class MyHandler(webapp.RequestHandler):
  def post(self):
  key = '[YOUR MERCHANT KEY]'
  tphash = self.request.headers['TrialPay-HMAC-MD5'] 
  if hmacmd5(key,self.request.body) != tphash:
    logging.info('invalid trialpay hash')
    return 

UPDATE 2

The req.body prints out as:

{ 
  oid: 'sample-order-id',
  sid: 'customer-sid',
  order_date: '04/24/2012',
  timestamp: '04/24/2012 16:28:46',
  first_name: 'customer-firstname',
  last_name: 'customer-lastname',
  email: '[email protected]',
  revenue: '10.00',
  zip_code: '94041',
  country: 'US' 
}
like image 438
Paul Burke Avatar asked Apr 24 '12 19:04

Paul Burke


1 Answers

This should do the trick:

var crypto = require('crypto');

function calculateSignature(key) {
    return function(req, res, next) {
        var hash = req.header("TrialPay-HMAC-MD5"),
            hmac = crypto.createHmac("md5", key);

        req.on("data", function(data) {
            hmac.update(data);
        });

        req.on("end", function() {
            var crypted = hmac.digest("hex");

            if(crypto.timingSafeEqual(
              Buffer.from(crypted),
              Buffer.from(hash.padEnd(crypted.length))
            )) {
                // Valid request
                return res.send("Success!", { "Content-Type": "text/plain" });
            } else {
                // Invalid request
                return res.send("Invalid TrialPay hash", { "Content-Type": "text/plain" }, 403);
            }
        });

        req.on("error", function(err) {
            return next(err);
        });
    }
}

app.post("/trialpay", calculateSignature("[MY MERCHANT KEY]"));
like image 117
Linus Thiel Avatar answered Oct 19 '22 05:10

Linus Thiel