CodeRigor

LESSON

Authenticate exact bytes with HMAC

Use HMAC-SHA-256 and hmac.Equal, verify published vectors and reject changed messages, wrong keys and truncated tags.

An HMAC tag authenticates exact bytes relative to a shared secret. The standard library provides the construction and comparison operation; application code should not invent either.

The exercise supplies HMAC256 and asks you to verify a complete tag. Published RFC 4231 cases check interoperability. Their keys are intentionally public test fixtures and must not be reused as real secrets.

Keyed authentication checks a byte relation
start / 3
The same secret and exact message reproduce a tag. Verification checks the complete tag with the library comparison operation.
Keyed authentication checks a byte relation
The same secret and exact message reproduce a tag. Verification checks the complete tag with the library comparison operation.

Use hmac.New with sha256.New, write the exact message and obtain Sum(nil). Repeated writes represent concatenated bytes; they do not create separate authenticated fields by themselves.

Verify the full tag length before hmac.Equal. Equal(nil,nil) is true as a byte comparison, but an empty tag is not a valid record tag under this contract.

Go
package main
import ("fmt";"crypto/hmac";"crypto/sha256")
func main(){
mac:=hmac.New(sha256.New,[]byte("public-test-key"));_,_=mac.Write([]byte("record"));tag:=mac.Sum(nil);fmt.Println(len(tag),hmac.Equal(tag,tag))
}

The low-level wrapper follows the primitive’s behavior so published vectors can use their specified key lengths. The final application signer is a stricter boundary: exactly 32 random key bytes, explicit context and bounded framed payloads.

A correct MAC does not hide the message, identify one person among shared-key holders or stop replay. A record protocol must add freshness/sequence state if that is needed. Use separate keys or deliberate domain separation across unrelated purposes.

Test a valid message, changed message, changed tag, wrong key, empty/truncated tag and binary input. A positive round trip alone would not prove the verifier rejects corruption.

Interoperability and rejection are separate evidence
start / 3
Published vectors establish the standard tag. Tampering cases establish that the verifier refuses wrong message/key/tag relations.
Interoperability and rejection are separate evidence
Published vectors establish the standard tag. Tampering cases establish that the verifier refuses wrong message/key/tag relations.
Go
package main
import ("fmt";"crypto/hmac";"crypto/sha256")
func main(){
key:=[]byte("test-key");a:=hmac.New(sha256.New,key);_,_=a.Write([]byte("one"));b:=hmac.New(sha256.New,key);_,_=b.Write([]byte("two"));fmt.Println(hmac.Equal(a.Sum(nil),b.Sum(nil)))
}

Complete verification and run both published and altered cases. Keep the fixture keys visibly separate from application key generation.

Next we practise authenticated encryption with a nonce-managing standard API rather than building an encryption scheme.

Actual Go verifier results · Public fixture key

A checksum can match while the record is rejected.

Choose an original or altered fixture. The replay recomputes an ordinary checksum of the shown payload, then runs the actual record verifier with its key, version and expected-context gates. This uses an intentionally public test key, never an application secret.

Payload bytes shown as textamount=99Caller expects lab.notes

Unkeyed checksum

Consistent

The advertised digest was computed from these bytes. This needs no secret and does not establish trusted origin.

Text
264c0e0d03dec862da5298c837428b4e1e5f6940f23234a51f7f371f062c06d6

Authenticated record

Rejected

The checksum matches the changed bytes, but the original HMAC no longer authenticates them.

Accepted payload outputNone — verification failed
Inspect the actual record fields
JSON
{
  "version": 1,
  "context": "lab.notes",
  "message": "YW1vdW50PTk5",
  "tag": "186780a104a57a54b70ce2db7f88b9e310874ecdd184a071e1af14fabde64b7e"
}

The record’s Base64 message remains readable. Acceptance means the complete format/context/MAC contract passed; rejection can occur before MAC comparison. This replay does not claim encryption, unique-sender identity or replay protection.

Download the CLI, published vectors and corruption tests →

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Optional expert discussion of how Go cryptography is engineered and tested. It supports the choice of maintained libraries; the course itself supplies the introductory explanations, vectors and complete lab.