Home >Backend Development >Golang >hmac.New(h func() hash.Hash, key byte) hash.Hash equivalent in JavaScript
In PHP, we often need to use encryption algorithms to protect data security. HMAC (Hash-based Message Authentication Code) is a commonly used encryption algorithm used to verify data integrity and identity authentication. In PHP, we can create an HMAC instance using the hmac.New() function, which requires specifying a hash function and a key. Similarly, in JavaScript, we can use equivalent methods to achieve the same functionality. In this article, I'll show you how to create an HMAC instance using equivalent methods in JavaScript, as well as how to encrypt and decrypt data between PHP and JavaScript.
I almost got stuck in the js implementation of go lang hmac.new for several days. However, there was no success. I use the crypto
, crypto-js
and stablelib
modules for implementation. The problem is that in go lang version, hmac
instances can be created from hmac
instances. For example (code block is correct and tested):
hmacf := hmac.New(func() hash.Hash { return hmac.New(func() hash.Hash { return hmac.New(func() hash.Hash { return hmac.New(sha256.New, []byte(SALT)) }, []byte(path[0])) }, []byte(path[1])) }, []byte(path[2]))
Actually, I don’t know how it works either! Because in all javascript related modules you cannot create hmac
from hmac
and they accept string
values that determine the hashing algorithm.
Maybe it's better to ask how to create hmac
from hmac
in javascript.
When the output of the go version is the same as the output of your implementation; your solution is correct.
According to the specification (rfc 2104), hmac uses digest functions internally, such as sha256.
However, your implementation applies (and is actually incompatible with) the hmac that uses another hmac internally instead of digest, where only the lowest level hmac uses regular digest internally. This creates a nested structure.
Based on the specification of regular hmac (with digest), this can be extended to hmac with hmac (instead of digest) used in go code:
hmac(k xor opad, hmac(k xor ipad, text))
s. rfc2104, Section 2. Definition of hmac
Due to differences from the specification, it may not be that easy to find a javascript library that supports such functionality out of the box.
While most libraries certainly support hmac, only allow specifying the digest (not hmac), e.g. nodejs's crypto module's crypto.createhmac()
, see also Others Answer. I don't think this approach can be used to implement logic in go code.
If the other answers' approaches don't work, and you can't find another javascript library with the functionality you want, you can implement the logic in javascript yourself, since hmac's specification is relatively simple (see above).
The following code is an example implementation of the crypto module of nodejs:
var crypto = require('crypto') const digest = 'sha256' const blocksize = 64 // block size of the digest // define input parameter var salt = buffer.from('salt') var path = [ buffer.from('alfa'), buffer.from('beta'), buffer.from('gamma') ] var data = buffer.from('data') // calculate hmac var hmac = hmac(data, salt, path) console.log(hmac.tostring('hex')) function hmac(data, salt, path) { // create keylist var keylist = [] keylist.push(salt) keylist = keylist.concat(path) // determine hmac recursively var result = hmac_rec(data, keylist) return result } function hmac_rec(data, keylist) { // adjust key (according to hmac specification) var key = keylist.pop() if (key.length > blocksize) { k = buffer.allocunsafe(blocksize).fill('\x00'); if (keylist.length > 0) { hmac_rec(key, [...keylist]).copy(k) } else { gethash(key).copy(k) } } else if (key.length < blocksize) { k = buffer.allocunsafe(blocksize).fill('\x00'); key.copy(k) } else { k = key } // create 'key xor ipad' and 'key xor opad' (according to hmac specification) var ik = buffer.allocunsafe(blocksize) var ok = buffer.allocunsafe(blocksize) k.copy(ik) k.copy(ok) for (var i = 0; i < ik.length; i++) { ik[i] = 0x36 ^ ik[i] ok[i] = 0x5c ^ ok[i] } // calculate hmac if (keylist.length > 0) { var innerhmac = hmac_rec(buffer.concat([ ik, data ]), [...keylist]) var outerhmac = hmac_rec(buffer.concat([ ok, innerhmac ]), [...keylist]) } else { var innerhmac = gethash(buffer.concat([ik, data])) var outerhmac = gethash(buffer.concat([ok, innerhmac])) } return outerhmac } // calculate sha256 hash function gethash(data){ var hash = crypto.createhash(digest); hash.update(data) return hash.digest() }
result:
2e631dcb4289f8256861a833ed985fa945cd714ebe7c3bd4ed4b4072b107b073
test:
The following go code produces the same result:
package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "hash" ) func main() { salt := "salt" path := []string{"alfa", "beta", "gamma"} hmacf := hmac.new(func() hash.hash { return hmac.new(func() hash.hash { return hmac.new(func() hash.hash { return hmac.new(sha256.new, []byte(salt)) }, []byte(path[0])) }, []byte(path[1])) }, []byte(path[2])) hmacf.write([]byte("data")) result := hmacf.sum(nil) fmt.println(hex.encodetostring(result)) // 2e631dcb4289f8256861a833ed985fa945cd714ebe7c3bd4ed4b4072b107b073 }
edit:
Inspired by this article, here is a more compact/efficient implementation of hmac_rec()
that uses hmac for the regular last iteration step (which also makes gethash()
Obsolete):
function hmac_rec(data, keyList) { var key = keyList.pop() if (keyList.length > 0) { // adjust key (according to HMAC specification) if (key.length > blockSize) { k = Buffer.allocUnsafe(blockSize).fill('\x00'); hmac_rec(key, [...keyList]).copy(k) } else if (key.length < blockSize) { k = Buffer.allocUnsafe(blockSize).fill('\x00'); key.copy(k) } else { k = key } // create 'key xor ipad' and 'key xor opad' (according to HMAC specification) var ik = Buffer.allocUnsafe(blockSize) var ok = Buffer.allocUnsafe(blockSize) k.copy(ik) k.copy(ok) for (var i = 0; i < ik.length; i++) { ik[i] = 0x36 ^ ik[i] ok[i] = 0x5c ^ ok[i] } // calculate HMAC var innerHMac = hmac_rec(Buffer.concat([ ik, data ]), [...keyList]) var outerHMac = hmac_rec(Buffer.concat([ ok, innerHMac ]), [...keyList]) } else { var outerHMac = crypto.createHmac(digest, key).update(data).digest(); } return outerHMac }
The above is the detailed content of hmac.New(h func() hash.Hash, key byte) hash.Hash equivalent in JavaScript. For more information, please follow other related articles on the PHP Chinese website!