Created
March 17, 2018 09:05
-
-
Save silbinarywolf/8816b87a72531e1c755d60f179e89bde to your computer and use it in GitHub Desktop.
hmac_md5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// @function hmac_md5 | |
/// @param {buffer} buffer The buffer to get a hash of | |
/// @param {number} offset | |
/// @param {number} size | |
/// @param {string} key Shared secret key used for generating the HMAC variant of the message digest. | |
/// @description Create a hash-based message authentication code using a provided key | |
/// @source https://en.wikipedia.org/wiki/HMAC | |
var buffer = argument0 | |
var buffer_offset = argument1 | |
var buffer_size = argument2 | |
var key = argument3 | |
var key_size = string_byte_length(key) | |
var hash_block_size = 64 // MD5 and SHA1 are 512 bits, ie. 64 bytes | |
if key_size > hash_block_size { | |
// Other language equivalents: | |
// - PHP - md5('Test_Japanese_昨夜のコ') == ff772e31410ae7f31b93c2109f496006 | |
// - GML - md5_string_utf8("Test_Japanese_昨夜のコ") == ff772e31410ae7f31b93c2109f496006 | |
key = md5_string_utf8(key) | |
key_size = string_byte_length(key) | |
} | |
// NOTE(Jake): 2018-03-17 | |
// | |
// Implementation on wiki notes you need to pad the end of the buffer with zero bytes: | |
// https://en.wikipedia.org/wiki/HMAC | |
// | |
// This is done implicitly by creating a buffer of hash_block_size as the final bytes | |
// that aren't written over will remain at zero byte values. | |
// | |
var o_key_pad_buffer = buffer_create(hash_block_size, buffer_fast, 1) | |
var i_key_pad_buffer = buffer_create(hash_block_size, buffer_fast, 1) | |
for (var i = 0; i < key_size; i++) { | |
var byte = string_byte_at(key, i) | |
// i_key_pad = key xor [0x36 * blockSize] | |
buffer_write(i_key_pad_buffer, buffer_u8, byte ^ 0x36) | |
// o_key_pad = key xor [0x5c * blockSize] | |
buffer_write(o_key_pad_buffer, buffer_u8, byte ^ 0x5c) | |
} | |
// Pad if the key is smaller than hash block size | |
for (var i = key_size; i < hash_block_size; i++) { | |
buffer_write(i_key_pad_buffer, buffer_u8, 0 ^ 0x36) | |
buffer_write(o_key_pad_buffer, buffer_u8, 0 ^ 0x5c) | |
} | |
var output_cap_size = hash_block_size+buffer_size | |
var output = buffer_create(output_cap_size, buffer_fixed, 1) | |
// Concat ipad + buffer | |
// - hash(opad_buffer + hash(ipad_buffer + buffer)) | |
// ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
buffer_copy(i_key_pad_buffer, 0, buffer_tell(i_key_pad_buffer), output, buffer_tell(output)) | |
buffer_seek(output, buffer_seek_relative, buffer_tell(i_key_pad_buffer)) | |
buffer_copy(buffer, buffer_offset, buffer_size, output, buffer_tell(output)) | |
buffer_seek(output, buffer_seek_relative, buffer_size) | |
var ipad_hash = buffer_md5(output, 0, buffer_tell(output)) | |
var ipad_hash_size = string_byte_length(ipad_hash) | |
show_debug_message("Ipad Hash: " + ipad_hash) | |
// NOTE(Jake): 2018-03-17 | |
// | |
// In GM Runtime 2.1.3.189, there seems to be a bug where buffer_write() | |
// doesn't move the "seek" pointer for buffer_string or buffer_text | |
// | |
// Using buffer_seek manually afterwards to work around this | |
// ---------------------------------------------------------------------- | |
// | |
// Concat opad + hash(ipad + message) | |
// - hash(opad_buffer + ihash) | |
// ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
buffer_seek(output, buffer_seek_start, 0) // reset / reuse buffer to get final hash | |
buffer_copy(o_key_pad_buffer, 0, buffer_tell(o_key_pad_buffer), output, buffer_tell(output)) | |
buffer_seek(output, buffer_seek_relative, buffer_tell(o_key_pad_buffer)) | |
buffer_write(output, buffer_text, ipad_hash) | |
buffer_seek(output, buffer_seek_relative, ipad_hash_size) | |
var result_hash = buffer_md5(output, 0, buffer_tell(output)) | |
// Clear buffers used to calculate hashes | |
buffer_delete(i_key_pad_buffer) | |
buffer_delete(o_key_pad_buffer) | |
buffer_delete(output) | |
return result_hash; | |
/*Function hmac | |
Inputs: | |
key: Bytes array of bytes | |
message: Bytes array of bytes to be hashed | |
hash: Function the hash function to use (e.g. SHA-1) | |
blockSize: Integer the block size of the underlying hash function (e.g. 64 bytes for SHA-1) | |
outputSize: Integer the output size of the underlying hash function (e.g. 20 bytes for SHA-1) | |
Keys longer than blockSize are shortened by hashing them | |
if (length(key) > blockSize) then | |
key ← hash(key) //Key becomes outputSize bytes long | |
Keys shorter than blockSize are padded to blockSize by padding with zeros on the right | |
if (length(key) < blockSize) then | |
key ← Pad(key, blockSize) //pad key with zeros to make it blockSize bytes long | |
o_key_pad = key xor [0x5c * blockSize] //Outer padded key | |
i_key_pad = key xor [0x36 * blockSize] //Inner padded key | |
return hash(o_key_pad ∥ hash(i_key_pad ∥ message)) //Where ∥ is concatenation | |
*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment