Skip to content

HMAC Generator — Go source

Generate a keyed-hash HMAC (SHA-1/256/384/512) for a message and secret. Runs entirely in your browser via Web Crypto, with a shareable link to your exact input.

This is the Go implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Package hmacgenerator is the Go twin of CosmoDev's src/lib/hmac.ts (dual
// source: the web lib is TypeScript, the CLI lib is Go — kept in lock-step).
// Pure + deterministic, never panics. The table-driven tests in
// hmac-generator_test.go share vectors with src/lib/hmac.test.ts so the two
// implementations are held to the same contract.
//
// The TS lib computes keyed-hash HMAC through the WebCrypto API
// (crypto.subtle / SubtleCrypto); this twin maps that to the Go stdlib
// (crypto/hmac + crypto/sha1, crypto/sha256, crypto/sha512), producing the
// identical bytes and then lowercasing them to hex — the same surface as
// hmacHex() in the TS lib. Inputs are UTF-8 encoded, matching TextEncoder.
package hmacgenerator

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"fmt"
	"hash"
)

// Algorithm is the hash used for HMAC. It mirrors the TS HmacAlgorithm union
// ('SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512') as an int enum so the zero
// value carries the default (SHA256) — a string type could not, since its zero
// value would be "". This is the same pattern slugify uses for its Case union.
type Algorithm int

const (
	// SHA256 is the default algorithm (zero value), matching the TS lib's
	// default 'SHA-256'.
	SHA256 Algorithm = iota
	SHA1
	SHA384
	SHA512
)

// String returns the TS-style algorithm name (e.g. "SHA-256"), useful for error
// messages and the eventual cosmodev CLI surface.
func (a Algorithm) String() string {
	switch a {
	case SHA256:
		return "SHA-256"
	case SHA1:
		return "SHA-1"
	case SHA384:
		return "SHA-384"
	case SHA512:
		return "SHA-512"
	default:
		return fmt.Sprintf("SHA-(%d)", int(a))
	}
}

// newHasher returns a fresh hash.Hash factory for the algorithm, or an error if
// the algorithm is unsupported — mirroring SubtleCrypto rejecting an unknown
// hash name.
func newHasher(a Algorithm) (func() hash.Hash, error) {
	switch a {
	case SHA256:
		return sha256.New, nil
	case SHA1:
		return sha1.New, nil
	case SHA384:
		return sha512.New384, nil
	case SHA512:
		return sha512.New, nil
	default:
		return nil, fmt.Errorf("hmac: unsupported algorithm %s", a)
	}
}

// HmacHex computes the HMAC of message under secret and returns it as lowercase
// hex. It is the Go twin of hmacHex() in src/lib/hmac.ts and must agree with it
// on every shared vector. Both inputs are UTF-8 encoded, exactly like the TS
// lib's TextEncoder. The zero-value algorithm (SHA256) is the default.
//
// An empty secret returns an error — SubtleCrypto rejects a zero-length key in
// the TS lib, and the twin mirrors that contract. An unsupported algorithm
// likewise returns an error rather than panicking.
func HmacHex(message, secret string, alg Algorithm) (string, error) {
	if secret == "" {
		return "", errors.New("hmac: secret must be non-empty")
	}
	hasher, err := newHasher(alg)
	if err != nil {
		return "", err
	}
	mac := hmac.New(hasher, []byte(secret))
	mac.Write([]byte(message))
	return hex.EncodeToString(mac.Sum(nil)), nil
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →