About Monkey 2 › Forums › Monkey 2 Programming Help › Help porting HMAC-SHA256 code
This topic contains 8 replies, has 3 voices, and was last updated by Danilo 2 days, 3 hours ago.
-
AuthorPosts
-
January 13, 2019 at 7:58 am #15894
jondecker76
ParticipantI’m trying to port some fairly simple code to make my own HMAC-SHA256 function, but I’m not getting expected results (I’m sure it’s a string vs byte issue). Here is information on the algorithm:
https://datatracker.ietf.org/doc/html/rfc2104
https://en.wikipedia.org/wiki/HMAC
Here is where my tests are so far…
Monkey
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960#Import"<std>"Using std..Function HMAC256:String(key:String, message:String)Local blocksize:=64Local opad:UByte[]= New UByte[blocksize]' $5c Where blocksize is that of the underlying hash FunctionLocal ipad:UByte[] = New UByte[blocksize]' $36' zero out the inner and outer padsFor Local i:=0 To blocksize-1opad[i]=$00ipad[i]=$00Next' Reset the key to blocksize lengthIf (key.Length > blocksize) Thenkey = SHA256(key) 'Where 'hash' is the underlying hash functionEnd If' Copy the keys to the pads. We need the actual byte valuesFor Local i:=0 To key.Length-1opad[i]=UByte(key[i])ipad[i]=UByte(key[i])NextFor Local i:=0 To key.Length - 1 Step 1ipad[i] = ipad[i] ~ $36opad[i] = opad[i] ~ $5cend forLocal opadStr:StringLocal ipadStr:StringFor Local this:=Eachin opadopadStr+=thisNextFor Local this:=Eachin ipadipadStr+=thisNextReturn SHA256(opadStr + SHA256(ipadStr + message)) ' Where || is concatenationend functionFunction Main()Local key:="Jefe"Local data:= "what do ya want for nothing?"Local test:="750c783e6ab0b503eaa86e310a5db738"Local digest:=HMAC256(key,data)If digest=testPrint "The expected digest was returned!"ElsePrint "The digest " + digest + " is incorrect!"EndifEndAny pointers?
Thanks
January 14, 2019 at 6:33 am #15898
jondecker76
ParticipantStill working on this. I’m much happier with the state of the code, but it still isn’t returning expected results :/
Monkey
12345678910111213141516171819Function HMAC256:String(key:String,message:String)Local B:=64 ' Blocksize (bytes)Local K:=New DataBuffer(B) ' KeyLocal ipad:=New DataBuffer(B) ' Inner pad stringLocal opad:=New DataBuffer(B) ' Outer pad string' Lets handle our key first!' Truncate the key if over bloccksizeIf key.Length>B Then key=SHA256(key)K.PokeString(0,key)' Lets fill ipad and opadFor Local i:=0 To B-1ipad.PokeUByte(i,$36~K.PeekUByte(i))opad.PokeUByte(i,$5c~K.PeekUByte(i))NextReturn SHA256(opad.PeekString(0) + SHA256(ipad.PeekString(0)+ message))EndJanuary 14, 2019 at 8:58 am #15900
jondecker76
ParticipantHere is a full project to show the issue. Everything looks right, but I can’t figure out why it’s not returning expected results
Monkey
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192#Import"<std>"#Import"<libc>"Using std..Using libc..' Simple HMAC-SHA256 function' See:' https://en.wikipedia.org/wiki/HMAC' https://datatracker.ietf.org/doc/html/rfc2104' https://www.freeformatter.com/hmac-generator.htmlFunction HMAC256:String(key:String,message:String)Local B:=64 ' Blocksize (bytes)Local K:=New DataBuffer(B) ' KeyLocal ipad:=New DataBuffer(B) ' Inner padLocal opad:=New DataBuffer(B) ' Outer pad' Lets handle our key first!' Truncate the key if over blocksize,If key.Length>B Then key=SHA256(key)Zero(K.Data,B)K.PokeString(0,key)Display(K.Data,B,"KEY BYTES")' Lets fill ipad and opadFor Local i:=0 To B-1ipad.PokeUByte(i,$36~K.PeekUByte(i))opad.PokeUByte(i,$5c~K.PeekUByte(i))NextDisplay(ipad.Data,B,"IPAD BYTES AFTER XOR")Display(opad.Data,B,"OPAD BYTES AFTER XOR")Return SHA256(opad.PeekString(0) + SHA256(ipad.PeekString(0)+message))EndFunction Zero(db:UByte Ptr,len:Int)For Local i:=0 To len-1db[i]=$0NextEndFunction LPad:String(str:String,len:Int,char:String)While str.Length < lenstr=char+strEndReturn strEndFunction Display(arr:UByte Ptr,len:Int,title:String="")Local lineHex:StringLocal lineAscii:StringLocal retVal:StringLocal j:=0If title<>""Print titlePrint "==========================================================="EndFor Local i:=0 To len-1lineHex+=LPad(Hex(arr[i]),2,"0") + " "lineAscii+=String.FromChar(arr[i]) + " "If (i Mod 8)+1=8lineHex += "| "EndifIf (i Mod 16)+1=16Print LPad(Hex(j),4,"0") + ": "+ lineHex + lineAscii'retVal+=lineHex + "|| "+lineAscii+"~n"lineAscii=""lineHex=""j+=16EndifNextIf lineHex<>"" Then Print LPad(Hex(j),4,"0") + ": "+lineHex + lineAsciiPrint "~n"End FunctionFunction Main()Local key:="jondecker76"Local data:= "hello"Local expectedResult:="ab9ce9ef1575259bdb2beb438d8501479b5a63298bc33e4f4fdee87760fd3b75"Local result:=HMAC256(key,data)If result=expectedResultPrint "The expected result was returned!"ElsePrint "~nThe result " + result + " is incorrect!"EndifEndJanuary 14, 2019 at 1:25 pm #15903
abakoboParticipantI ported part of a library to get this done. Code is from https://create.stephan-brumme.com/hash-library/ . Please check for the license(MIT I think).
I had to remove the call to endian.h because it’s a mess with OSX. This result in this port working on “Little Endian” OS/Hardware ONLY (which is all common now but I preffer to tell). (Networks seems to be “Big Endian” though!)
Sorry not to review your code but for me it’s easyer to Import C++ code..I’m using a libc.char_t array to get my CString to avoid memory leaks.
Hope it works well for you.Code in attached zip.
PS: I’ve seen on StackOverflow that the most common problem with hashes is a confusion with chars and bytes.
PS2: you could check the code in std.digest to see how the types are managed in there if you want to continue your mx2 education..Attachments:
- hmac256_64.zip
January 16, 2019 at 2:00 pm #15923
jondecker76ParticipantThank you very much, this is very helpful.
I’ll definitely still work on figuring out what is causing it to fail in pure Mx2, but at least it won’t hold me up on my project!
January 17, 2019 at 1:09 am #15930
jondecker76ParticipantOuch, this keeps getting stranger.
It’s easy to see the first obvious problem with this test.. MX2 text can’t represent characters $80-$FF? Strange! The strangest thing is that $FF isn’t even possible, and equates to $00? Run the test below and look at the last entry. Yes, I see that the first byte is $FF for >$80… But that stops at $FFFE. Where is $FFFF? Is this expected behavior or a bug?
Monkey
1234567891011121314151617#Import "<std>"Using std..Function Main()Local db:DataBuffer=New DataBuffer(255)For Local i:=0 To 255db.PokeUByte(i,i)NextLocal test:=db.PeekString(0)For Local i:= 0 To 255Print test[i] + " : " + Hex(test[i])NextEndJanuary 17, 2019 at 1:16 am #15931
DaniloParticipant0 To 255 requires a DataBuffer of 256 bytes.
January 17, 2019 at 11:38 am #15939
Danilo
ParticipantStrings in Monkey2 are 16-bit Unicode Strings.
Please try the following code to catch ASCII strings from DataBuffer and convert it to Unicode-Monkey2-Strings.
Monkey
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364#Import "<std>"Using std..Function Main()Local db:DataBuffer=New DataBuffer(256)For Local i:=0 To 255db.PokeUByte(i,i)NextLocal test:=db.PeekString(0, StringFormat.ASCII)For Local i:= 0 To 255Print test[i] + " : " + Hex(test[i])NextEndEnum StringFormatASCII' UTF8' UTF16EndClass DataBuffer ExtensionMethod PeekString:String( offset:Int, type:StringFormat )Local ret:String = ""Select typeCase StringFormat.ASCIILocal len:Int = Self.Length - offsetIf len < 1 Then Return ""For Local i:Int = 0 Until lenret += String.FromChar( Self.PeekUByte(offset+i) )NextEnd SelectReturn retEndMethod PeekString:String( offset:Int, count:Int, type:StringFormat )Local ret:String = ""Local len :IntSelect typeCase StringFormat.ASCIIlen = Self.Length - offsetlen = Min(len,count)If len < 1 Then Return ""For Local i:Int = 0 Until lenret += String.FromChar( Self.PeekUByte(offset+i) )NextEnd SelectReturn retEndEndJanuary 17, 2019 at 2:14 pm #15941
DaniloParticipant@jondecker76: Could it be that your expected results comes from routines that use SHA256() on ASCII strings? All std.digest functions in Monkey2 use 16-bit Unicode strings.
The SHA(), MD5(), SHA256() with Unicode string buffers are probably much different from using the same functions with ASCII buffers.
-
AuthorPosts
You must be logged in to reply to this topic.