Secure Message Authentication with HMAC in Go

2 min read .

In this post, we’ll explore how to implement a secure message authentication system using HMAC (Hash-based Message Authentication Code) in Go. HMAC is commonly used to ensure both the integrity and authenticity of a message. We’ll utilize the SHA-512 hashing algorithm in combination with a secret key to generate and verify HMACs.

Introduction to HMAC

HMAC (Hash-based Message Authentication Code) combines a cryptographic hash function with a secret key to verify data integrity and authenticity. The process includes:

  1. Generating HMAC: A message, along with a secret key and other elements (such as a nonce and timestamp), is hashed to produce an HMAC.
  2. Verifying HMAC: On the receiving end, the message and key are used to regenerate the HMAC, which is then compared with the received HMAC to ensure the message’s integrity.

Implementing HMAC in Go

Here’s a detailed implementation of HMAC in Go:

package main

import (
	"crypto/hmac"
	"crypto/sha512"
	"encoding/hex"
	"fmt"
	"time"
)

func main() {
	message := "this is a sample message"
	key := "secret"
	nonce := "random"
	timestamp := time.Now().Unix()
	allowedTimestampDiff := int64(60) // 60 seconds

	usedNonces := make(map[string]bool)

	fmt.Println("message:", message)
	fmt.Println("key:", key)
	fmt.Println("nonce:", nonce)
	fmt.Println("timestamp:", timestamp)
	fmt.Println("allowedTimestampDiff:", allowedTimestampDiff)
	fmt.Println("usedNonces:", usedNonces)

	// Generate HMAC
	generatedHMAC := generateMAC(message, key, nonce, timestamp)
	fmt.Println("generated HMAC:", generatedHMAC)

	// Verify HMAC
	isValid := verifyHMAC(message, key, generatedHMAC, nonce, timestamp, allowedTimestampDiff, usedNonces)
	fmt.Println("isValid:", isValid)

	// Verify with a fake HMAC
	fakeHMAC := verifyHMAC(message, key, "incorrect", nonce, timestamp, allowedTimestampDiff, usedNonces)
	fmt.Println("fakeHMAC:", fakeHMAC)

	// Verify with the same nonce
	isValidSameNonce := verifyHMAC(message, key, generatedHMAC, nonce, timestamp, allowedTimestampDiff, usedNonces)
	fmt.Println("isValidSameNonce:", isValidSameNonce)
}

// Function to generate HMAC
func generateMAC(message, key, nonce string, timestamp int64) string {
	keyBytes := []byte(key)
	data := fmt.Sprintf("%s:%d:%s", message, timestamp, nonce)
	dataBytes := []byte(data)

	h := hmac.New(sha512.New, keyBytes)
	h.Write(dataBytes)

	hmacBytes := h.Sum(nil)
	hmacString := hex.EncodeToString(hmacBytes)

	return hmacString
}

// Function to verify HMAC
func verifyHMAC(message, key, receivedHMAC, nonce string, timestamp int64, allowedTimestampDiff int64, usedNonces map[string]bool) bool {
	expectedHMAC := generateMAC(message, key, nonce, timestamp)

	expectedHMACBytes, _ := hex.DecodeString(expectedHMAC)
	receivedHMACBytes, _ := hex.DecodeString(receivedHMAC)

	// Check if HMACs match
	if !hmac.Equal(expectedHMACBytes, receivedHMACBytes) {
		return false
	}

	// Check timestamp validity
	currentTimestamp := time.Now().Unix()
	if timestamp < currentTimestamp-allowedTimestampDiff || timestamp > currentTimestamp+allowedTimestampDiff {
		return false
	}

	// Check if nonce has been used
	if _, ok := usedNonces[nonce]; ok {
		return false
	}

	usedNonces[nonce] = true

	return true
}

Explanation of the Code

  1. Generating HMAC:

    • The generateMAC function creates an HMAC using SHA-512. It concatenates the message, timestamp, and nonce, then hashes this data with the secret key.
  2. Verifying HMAC:

    • The verifyHMAC function compares the received HMAC with the expected HMAC. It checks if the HMACs match, verifies the timestamp to ensure it is within an acceptable range, and ensures that the nonce has not been reused.

Testing the Implementation

In the main function, we demonstrate how to use these functions. We generate an HMAC for a given message and then verify it. We also test the verification function with an incorrect HMAC and check the behavior with a reused nonce.

Conclusion

This implementation of HMAC in Go ensures that messages are authenticated and that any tampering or replay attacks are detected. By using HMAC with SHA-512 and including a nonce and timestamp, we add layers of security that protect the integrity and authenticity of our messages.

Tags:
Golang

See Also

chevron-up