DEV Community 👩‍💻👨‍💻

Masui Masanori
Masui Masanori

Posted on

[Go] Try HMAC-SHA512 implementation

#go

Intro

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

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.

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

...
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
}
Enter fullscreen mode Exit fullscreen mode

Examples

main.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)
}
Enter fullscreen mode Exit fullscreen mode

hmacSHA512Hasher.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
}
Enter fullscreen mode Exit fullscreen mode

Result

8F9909C45E601A31A2E6949FE6E4C739ADE74F3A0A5F9489D4E5F8BC5B71C08C998C78E14AB4C524E884A308E1E4B9902E7E76D9E1328E5A603B7DFA42604D74
8F9909C45E601A31A2E6949FE6E4C739ADE74F3A0A5F9489D4E5F8BC5B71C08C998C78E14AB4C524E884A308E1E4B9902E7E76D9E1328E5A603B7DFA42604D74
Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.