HMAC function implementation
Posted: Fri Aug 19, 2016 9:54 pm
				
				Hi, I just finished my HMAC thing for PB.
It depends on PB hashing functions, so theoretically any of PB "ciphers" should work well, but currently tested only with MD5, SHA-1 and SHA-2 (SHA256).
Also would be nice if someone can compare its results with results of that php hash_hmac() function (as this hashing usually used with those kid-or-monkey-oriented web-apis :3).
Any feedback welcome
			It depends on PB hashing functions, so theoretically any of PB "ciphers" should work well, but currently tested only with MD5, SHA-1 and SHA-2 (SHA256).
Also would be nice if someone can compare its results with results of that php hash_hmac() function (as this hashing usually used with those kid-or-monkey-oriented web-apis :3).
Any feedback welcome
Code: Select all
EnableExplicit
; 	HMAC function implementation
;	2016			(c) Luna Sole
; convert hex string into raw bytes
; Out()		unsigned char array to receive result
; Hex$		string with hex data
; RETURN:	decimal value is placed to Out() array
Procedure Hex2Dec (Array Out.a (1), Hex$)
	Protected i2, max = Len(Hex$)
	ReDim Out((max + 1) / 2)
	For i2 = 1 To max Step 2
		Out(i2 / 2) = Val("$" + Mid(Hex$, i2, 2))
	Next i2
EndProcedure
; generates HMAC signature for specified message and key
; NOTE:			This function forces strings conversion to ASCII, both for key and message
; 				I'm not sure how right to do that, but let it be for now (I don't want to do a painful debug of unicode version also ^^)
; PB_Cipher		what hashing to use (MD5, SHA1, SHA256 and some others)
; Message$		data to hash
; Key$			a very secret key
; RETURN:		string, representing HMAC hash
Procedure$ StringHMAC (PB_Cipher, Message$, Key$)
	UseMD5Fingerprint() : UseSHA1Fingerprint() : UseSHA2Fingerprint() ; currently only verified with these algorithms
	#HMAC_BLOCKSIZE = 64 ; blocksize is 64 (bytes) when using one of the following hash functions: SHA-1, MD5, RIPEMD-128/160.
	; First of all, convert key from string to binary
	; If key is longer than block size, replace it with hash(key)
	Protected Dim key_bdata.a (#HMAC_BLOCKSIZE)
	If (StringByteLength(Key$, #PB_Ascii) > #HMAC_BLOCKSIZE)
		PokeS(@key_bdata(0), StringFingerprint(Key$, PB_Cipher), -1, #PB_Ascii | #PB_String_NoZero)
	Else
		PokeS(@key_bdata(0), Key$, -1, #PB_Ascii | #PB_String_NoZero)
	EndIf
	
	; Now introduce o_key_pad/i_key_pad and XOR them with some magic numbers
	Protected Dim i_key_pad.a (0)
	Protected Dim o_key_pad.a (0)
	Protected Tmp
	CopyArray(key_bdata(), i_key_pad())
	CopyArray(key_bdata(), o_key_pad())
	For Tmp = 0 To #HMAC_BLOCKSIZE
		i_key_pad(Tmp) ! $36
		o_key_pad(Tmp) ! $5c
	Next Tmp
	
	; At last, start hashing
	Protected Hash_i$, Hash_o$			; there are two steps, those variables storing result for step 1 and 2
	Protected hHash						; handle to initiated hash routine
	Protected Dim TempRaw.a (0)			; a temporary buffer for data transfer
	
	; First, hash using i_key_pad() and data
	ReDim TempRaw(StringByteLength(Message$, #PB_Ascii))
	PokeS(@TempRaw(0), Message$, -1, #PB_Ascii | #PB_String_NoZero)
	hHash = StartFingerprint (#PB_Any, PB_Cipher)
	AddFingerprintBuffer (hHash, @i_key_pad(0), #HMAC_BLOCKSIZE)
	If ArraySize(TempRaw())
		AddFingerprintBuffer (hHash, @TempRaw(0), ArraySize(TempRaw()))
	EndIf
	Hash_i$ = FinishFingerprint(hHash)
	
	; Finally, hash once more using o_key_pad() + result of previous hashing
	Hex2Dec(TempRaw(), Hash_i$) 
	hHash = StartFingerprint (#PB_Any, PB_Cipher)
	AddFingerprintBuffer (hHash, @o_key_pad(0), #HMAC_BLOCKSIZE)
	AddFingerprintBuffer (hHash, @TempRaw(0), ArraySize(TempRaw()))
	Hash_o$ = FinishFingerprint(hHash)
	
	ProcedureReturn Hash_o$
EndProcedure
; some tests
Debug "* HMAC of empty data and empty key *"
Debug "MD5:   " + StringHMAC(#PB_Cipher_MD5, "", "")  ; MD5 = 0x74e6f7298a9c2d168935f58c001bad88
Debug "SHA1:  " + StringHMAC(#PB_Cipher_SHA1, "", "") ; SHA1 = 0xfbdb1d1b18aa6c08324b7d64b71fb76370690e1d
Debug "SHA2:  " + StringHMAC(#PB_Cipher_SHA2, "", "") ; SHA256 = 0xb613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad
Debug ""
Debug "* HMAC of control data to check is it OK *"
Debug "MD5:   " + StringHMAC(#PB_Cipher_MD5,  "The quick brown fox jumps over the lazy dog", "key") ; MD5 = 0x80070713463e7749b90c2dc24911e275
Debug "SHA1:  " + StringHMAC(#PB_Cipher_SHA1, "The quick brown fox jumps over the lazy dog", "key")	; SHA1 = 0xde7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9
Debug "SHA2:  " + StringHMAC(#PB_Cipher_SHA2, "The quick brown fox jumps over the lazy dog", "key")	; SHA256 = 0xf7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8
Debug "* fin *"
; Control data and resulting HMAC-hashes for it taken from wiki page:
; 	 https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Examples