7
$\begingroup$

I am trying to implement the HMAC algorithm in Mathematica. The algorithm is a relatively simple way of signing messages with a key.

Here is some pseudocode from Wikipedia:

enter image description here

My attempt:

hash[string_String, method_String : "SHA256"] := FromCharacterCode @ 
    Interpreter["HexInteger"] @ StringPartition[IntegerString[Hash[string, method], 16], 2];

HMAC[key_String, message_String, method_String : "SHA256", blockSize_Integer : 64] :=
    Module[
    {char54, char92, key2, ipad, opad},
    {char54, char92} = FromCharacterCode /@ {54, 92};
    key2 = Switch[StringLength @ key,
        blockSize, key,
        l_ /; l > blockSize, hash[key, method],
        _, StringPadRight[key, blockSize, FromCharacterCode @ 0]];
    ipad = FromCharacterCode[BitXor @@ Map[ToCharacterCode, {StringRepeat[char54, blockSize], key2}]];
    opad = FromCharacterCode[BitXor @@ Map[ToCharacterCode, {StringRepeat[char92, blockSize], key2}]];
    hash @ StringJoin[opad, hash @ StringJoin[ipad, message]]];

A Java analog for testing:

Needs["JLink`"];
LoadJavaClass["javax.crypto.Mac"];

javaHMAC[key_, message_] := Module[
    {mac, keySpec, bytes},
    JavaBlock[
        mac = Mac`getInstance["HmacSHA256"];
        keySpec = JavaNew["javax.crypto.spec.SecretKeySpec", ToCharacterCode[key], "HmacSHA256"];
        mac @ init[keySpec];
        bytes = mac @ doFinal[ToCharacterCode[message]] + 256;
        FromCharacterCode @ bytes]];

In my particular case I am using this to communicate with Amazon Web Services. At the bottom of this page they provide a test vector which involves repeated HMAC computations. For these inputs (using outputs as the key for the next input)

key = "AWS4" <> "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
messages = {"20120215", "us-east-1", "iam", "aws4_request"};

we should obtain

"kSecret  = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate    = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion  = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'";
StringSplit[StringSplit[%, "\n"], "= "][[All, 2]];
correct = StringReplace[%, "'" -> ""];

My function passes this test. Swapping it out for the Java method will show that the same results are obtained.

FoldList[HMAC, key, messages];
StringJoin /@ IntegerString[ToCharacterCode @ %, 16, 2];
MapThread[SequenceAlignment, {%, correct}];
% // Column

{41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559}

{969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d}

{69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c}

{f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa}

{f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d}

But on a slightly different set of messages, from the bottom of this page, my function fails on one of the steps.

messages2 = {"20110909", "us-east-1", "iam", "aws4_request"};

FoldList[HMAC, key, messages2];
hex2 = StringJoin /@ IntegerString[ToCharacterCode @ %, 16, 2];

FoldList[javaHMAC, key, messages2];
javaHex2 = StringJoin /@ IntegerString[ToCharacterCode @ %, 16, 2];

MapThread[SequenceAlignment, {hex2, javaHex2}] // Column

{41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559}

{a83ed188be5f4b074d7f66349f5077fbcdf797bf3471fb9d5f32730f936d41a5}

{957b5875f33834a85374b750011dc2d6f0e1d6896eeb891d36a73c711961ad6e}

{{,0},116249d060bff83faa1a627e85a4c6f83ce50d89c334765878dcf76e28bfc6,{,e}}

{{a4b,98f1},d,{,889f},e,{1,c},4,{b2f,f44},21,{2,adc},5,{8145,22b},a,{,b0ce1},f,{6,},8,{1,2e6},9,{d7,29},c,{4e4,262},e,{17a88,d},1,{31,5e},5a9,{f85,4c9},0,{33,ef},d,{b,},1,{d858,},e,{6,},3,{f52,b0e},7,{51,}}

In this case the Java output matches the answer provided by AWS.

I cannot get this implementation to work either.

$\endgroup$
  • $\begingroup$ I don't understand your custom hash as the pseudocode from Wikipedia $\endgroup$ – yode Oct 18 '16 at 11:49
  • $\begingroup$ I don't understand your custom hash as the pseudocode from Wikipedia $\endgroup$ – yode Oct 18 '16 at 11:49
4
$\begingroup$

Try something like

hash[string_String, method_String: "SHA256"] :=
    FromCharacterCode@IntegerDigits[Hash[string, method], 256, 32]

The original method you used will drop leading zeros. You can even see that in your example data set:

{{,0},116249d060bff83faa1a627e85a4c6f83ce50d89c334765878dcf76e28bfc6,{,e}}

ETA: I should have mentioned that passing in the algorithm but then hard-coding the length of the expected hash to 32 bytes is extremely sloppy of me. Do not do that in production code.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.