[Go] Try HMAC-SHA512 implementation
Tags: go
Published: 2022-06-17 15:30:52

## Intro
This time, I will try implementing HMAC-SHA512.
I will use the SHA-512 function what I wrote last time.

* [[Go] Try SHA-512 implementation](https://dev.to/masanori_msl/go-try-sha-512-implementation-33gd)

## Secret Key
HMAC needs a key and a message to get a hash value.

The key has several features.

* It is shared only by the sender and the receiver
* It can be of any length
* If its length is shorter than the byte-length of the hash function, it is first filled with zeros to make it that length
* If its length is longer than the byte-length of the hash function, it will be hashed first

It seems that "PasswordHasher" also uses the password(message) as a key.

* [aspnetcore/PasswordHasher.cs - dotnet/aspnetcore - GitHub](https://github.com/dotnet/AspNetCore/blob/main/src/Identity/Extensions.Core/src/PasswordHasher.cs)
* [aspnetcore/ManagedPbkdf2Provider.cs - dotnet/aspnetcore - GitHub](https://github.com/dotnet/aspnetcore/blob/main/src/DataProtection/Cryptography.KeyDerivation/src/PBKDF2/ManagedPbkdf2Provider.cs)

## Update SHA-512
Last time, I returned the result as a string value.

But I want to get the value as byte array to append the key, so I change the SHA-512 function.

#### sha512Hasher.go
```go
...
func Hash(inputValues []byte) []byte {
	formattedMessage := formatInput(inputValues)
	computed := compute(formattedMessage)
	return computed
}
...
func compute(messages []byte) []byte {
...
	// get H[N]
	var results []byte
	for _, h := range H {
		b := make([]byte, 8)
		binary.BigEndian.PutUint64(b, h)
		results = append(results, b...)
	}
	return results
}
```
* [How can I convert an int64 into a byte array in go? - StackOverflow](https://stackoverflow.com/questions/35371385/how-can-i-convert-an-int64-into-a-byte-array-in-go)
* [Concatenate 2 slices in golang - StackOverflow](https://stackoverflow.com/questions/46305812/concatenate-2-slices-in-golang)

## Examples
#### main.go
```go
package main

import (
	"crypto/hmac"
	"crypto/sha512"
	"fmt"
	"log"
)

func main() {
	inputData := []byte("hello")
	hmacSHA512Results := HashHMACSHA512(inputData, inputData)
	var hresult string
	for _, r := range hmacSHA512Results {
		hresult += fmt.Sprintf("%02X", r)
	}
	log.Println(hresult)

	h := hmac.New(sha512.New, inputData)
	h.Write(inputData)
	results := h.Sum(nil)

	var result string
	for _, r := range results {
		result += fmt.Sprintf("%02X", r)
	}
	log.Println(result)
}
```
#### hmacSHA512Hasher.go
```go
package main

const byteLength int = 128

/* compute H(K XOR opad, H(K XOR ipad, text)) */
func HashHMACSHA512(inputData, keyData []byte) []byte {
	formattedKey := formatKey(keyData)

	ipad := xorIPAD(formattedKey)
	// append the stream of input data to the result of (K XOR ipad)
	// and hash the value
	innerData := Hash(append(ipad, inputData...))

	opad := xorOPAD(formattedKey)
	// append the H result to the result of (K XOR opad)
	// and hash the value
	return Hash(append(opad, innerData...))
}
func formatKey(keyData []byte) []byte {
	// If its length is longer than the byte-length,
	// it will be hashed first
	if len(keyData) >= byteLength {
		return Hash(keyData)
	}
	// If its length is shorter than the byte-length,
	// it is first filled with zeros to make it that length
	results := make([]byte, byteLength)
	copy(results, keyData)
	for i := len(keyData); i < byteLength; i++ {
		results[i] = 0x00
	}
	return results
}

/* K XOR ipad(ipad = the byte 0x36 repeated B times) */
func xorIPAD(k []byte) []byte {
	results := make([]byte, len(k))
	for i, key := range k {
		results[i] = key ^ 0x36
	}
	return results
}

/* K XOR opad(opad = the byte 0x5C repeated B times) */
func xorOPAD(k []byte) []byte {
	results := make([]byte, len(k))
	for i, key := range k {
		results[i] = key ^ 0x5C
	}
	return results
}
```

#### Result
```
8F9909C45E601A31A2E6949FE6E4C739ADE74F3A0A5F9489D4E5F8BC5B71C08C998C78E14AB4C524E884A308E1E4B9902E7E76D9E1328E5A603B7DFA42604D74
8F9909C45E601A31A2E6949FE6E4C739ADE74F3A0A5F9489D4E5F8BC5B71C08C998C78E14AB4C524E884A308E1E4B9902E7E76D9E1328E5A603B7DFA42604D74
```

## Resources
* [RFC2104 - HMAC: Keyed-Hashing for Message Authentication](https://datatracker.ietf.org/doc/html/rfc2104)
* [FIPS198-1 - The Keyed-Hash Message Authentication Code (HMAC)](https://csrc.nist.gov/publications/fips/)
* [RFC6234 - US Secure Hash Algorithms(SHA and SHA-based HMAC and HKDF)](https://datatracker.ietf.org/doc/html/rfc6234)
* [hmac package - crypto/hmac - Go packages](https://pkg.go.dev/crypto/hmac)