I'm fairly new to Nim, and I suspect I'm just doing something wrong here. I'm using Jester (for routing, etc) and Nimcrytpo (for hmac) but something isn't adding up. Here's how I'm attempting to verify a signature:
import jester
import dotenv
import os, strutils, times
import nimcrypto
const timestampHeader = "X-Slack-Request-Timestamp"
const slackSignatureHeader = "X-Slack-Signature"
const signatureVersion = "v0"
const signingSecret = os.getEnv("SLACK_SIGNING_SECRET")
proc isTimestampRecent(timestamp: int): bool =
abs(getTime().toUnix - timestamp) <= (60 * 5)
proc verifySignature*(request: Request): bool =
if (not request.headers.hasKey timestampHeader) or
(not request.headers.hasKey slackSignatureHeader):
return false
let timestamp = request.headers[timestampHeader].parseInt
if not timestamp.isTimestampRecent():
return false
let baseString = signatureVersion & ':' & $timestamp & ':' & $request.body
let mySignature = sha256.hmac(signingSecret, baseString)
let slackSignature = MDigest[256].fromHex(request.headers[slackSignatureHeader])
mySignature == slackSignature
A few things I'm running into:
v0=
in the comparison, but I'm not quite sure how to do that with the time independent comparison (whether I should be skipping that part or not in the comparison, etc)My best guess at this point is that somehow the Jester/Httpbeast request body isn't "raw" enough (though it's just plain json...?) or is somehow processed.
Any help or suggestions on how to debug would be greatly appreciated. Thank you in advance!
After fussing with this for a while, I found I was doing a number of things incorrectly! Hopefully this helps others:
signingSecret
is pulled from the env, so it shouldn't be a constant--I moved that into the proc itself defined with let
instead.v0=
, which makes it the wrong length for MDigest[256].fromHex()
, so that was ending up as the null value (0000...
) instead of what should've been.Here's a working version, now in case anyone else should need one. Please let me know if you see anything that could be improved as well.
import jester
import dotenv
import os, strutils, times
import nimcrypto
const timestampHeader = "X-Slack-Request-Timestamp"
const slackSignatureHeader = "X-Slack-Signature"
const signatureVersion = "v0"
proc isTimestampRecent(timestamp: int): bool =
abs(getTime().toUnix - timestamp) <= (60 * 5)
proc verifySignature*(request: Request): bool =
let signingSecret = os.getEnv("SLACK_SIGNING_SECRET")
if (not request.headers.hasKey timestampHeader) or
(not request.headers.hasKey slackSignatureHeader):
return false
let timestamp = request.headers[timestampHeader].parseInt
if not timestamp.isTimestampRecent():
return false
let baseString = signatureVersion & ':' & $timestamp & ':' & $request.body
let mySignature = sha256.hmac(signingSecret, baseString)
var rawSlackSignature: string = $request.headers[slackSignatureHeader]
rawSlackSignature.removePrefix(signatureVersion & '=')
let slackSignature = MDigest[256].fromHex(rawSlackSignature)
mySignature == slackSignature