Secure Message Authentication with HMAC in Go
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:
- Generating HMAC: A message, along with a secret key and other elements (such as a nonce and timestamp), is hashed to produce an HMAC.
- 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
-
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.
- The
-
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.
- The
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.