Every now and then I try to explore a new programming language. Last year, I tried Scala, this year, I’m fiddling around with Grails. My first attempt was to try something I already implemented in other languages, like verifying Facebook’s signed_request. It’s fun to see what I tried to get it running and how a short version by a more experienced Groovy coder looks like.
Compare both versions after the break if you like.
First, here’s my attempt. Note the many references to plain old Java methods.
class CanvasController {
def index = {
if( params.signed_request == null ) {
render( 'signed_request is empty!' )
return
}
def dot = params.signed_request.indexOf( "." )
if( dot == -1 ) {
render( "Invalid signed_request!" )
return
}
// Unescape base64url: '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_',
def unescapedRequest = params.signed_request.replaceAll( "-", "+" ).replaceAll( "_", "/" )
def signatureRaw = unescapedRequest.substring( 0, dot )
def payloadRaw = unescapedRequest.substring( dot + 1 )
// Padding for base64 decoding
def jsonBase64 = payloadRaw
while( jsonBase64.length() % 4 != 0 ) {
jsonBase64 += "=";
}
def signatureBase64 = signatureRaw
while( signatureBase64.length() % 4 != 0 ) {
signatureBase64 += "=";
}
def jsonBytes = jsonBase64.decodeBase64()
def jsonDecoded = new String( jsonBytes )
def signatureBytes = signatureBase64.decodeBase64()
def signature = new String( signatureBytes )
def json = new JSON().parse( jsonDecoded )
// Check for correct algorithm
if( json.algorithm != "HMAC-SHA256" ) {
render( "Unknown algorithm: " + json.algorithm )
return;
}
// Check signature
def mac = Mac.getInstance( "HmacSHA256" )
mac.init( new SecretKeySpec( ConfigurationHolder.config.app.facebook.secret.getBytes(), "HmacSHA256" ) )
mac.update( payloadRaw.bytes )
def calculatedSignature = new String( mac.doFinal() );
// Render for visual comparison
render( calculatedSignature + " --- " + signature + " xxxxxxxxxxxxxxx " )
}
}
Now here’s the much slimmer version (credits) with some adaptions by me, like logging and setting the HTTP response code, two other things I learned on the way.
class CanvasController {
def index = {
def (rawSig, rawPayload) = params.signed_request.split( /./, 2 )
def (sig,payload) = [ rawSig, rawPayload ]*.tr( '-_', '+/' )*.decodeBase64()
def data = JSON.parse( new String( payload ) )
if( data.algorithm != "HMAC-SHA256" ) {
log.debug( "Unknown algorithm: ${data.algorithm}" )
response.status = 500
return
}
def secret = grailsApplication.config.app.facebook.secret
def expectedSig = Mac.getInstance( "HmacSHA256" ).with {
init( new SecretKeySpec( secret.bytes, "HmacSHA256" ) )
update( rawPayload.bytes )
doFinal()
}
if( sig != expectedSig ) {
log.debug( "signatures don't match: ${sig.encodeAsBase64()} != ${expectedSig.encodeAsBase64()}" )
response.status = 500
return
}
render( new String( sig ) + " ---- " + new String( expectedSig ) )
}
}
