Mining/pkg/ueps/reader.go
Claude 7aada5fc78
ax(ueps): remove duplicate inline usage comment from ReadAndVerify body
AX-2 violation: usage examples belong at the declaration site, not
scattered inside function bodies. The errIntegrityViolation usage hint
was already present at the variable declaration (line 17) and was
duplicated inside ReadAndVerify, creating noise in the implementation.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 09:01:28 +01:00

126 lines
3.4 KiB
Go

package ueps
import (
"bufio"
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"io"
)
// packet, err := ReadAndVerify(bufio.NewReader(conn), secret)
// if errors.Is(err, errMissingHMAC) { /* no HMAC tag found in frame */ }
var errMissingHMAC = tlvError("UEPS packet missing HMAC signature")
// packet, err := ReadAndVerify(bufio.NewReader(conn), wrongSecret)
// if errors.Is(err, errIntegrityViolation) { /* HMAC mismatch — threat score incremented */ }
var errIntegrityViolation = tlvError("integrity violation: HMAC mismatch (ThreatScore +100)")
// packet, err := ueps.ReadAndVerify(bufio.NewReader(conn), sharedSecret)
// if err == nil { _ = packet.Header.IntentID; _ = packet.Header.ThreatScore; _ = packet.Payload }
type ParsedPacket struct {
Header UEPSHeader
Payload []byte
}
// packet, err := ueps.ReadAndVerify(bufio.NewReader(conn), []byte("my-shared-secret"))
// if err != nil { /* integrity violation or truncated frame */ }
func ReadAndVerify(reader *bufio.Reader, sharedSecret []byte) (*ParsedPacket, error) {
var signedData bytes.Buffer
header := UEPSHeader{}
var signature []byte
var payload []byte
// Loop through TLVs until we hit Payload (0xFF) or EOF
for {
// 1. Read Tag
tag, err := reader.ReadByte()
if err != nil {
return nil, err
}
// 2. Handle Payload Tag (0xFF) - The Exit Condition
if tag == TagPayload {
// Payload is length-prefixless; caller frames the stream.
// HMAC covers signedData (header TLVs) + raw payload bytes, not the 0xFF tag.
remaining, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
payload = remaining
break
}
// 3. Read Length (Standard TLV)
lengthByte, err := reader.ReadByte()
if err != nil {
return nil, err
}
length := int(lengthByte)
// 4. Read Value
tagValue := make([]byte, length)
if _, err := io.ReadFull(reader, tagValue); err != nil {
return nil, err
}
// Store for processing
switch tag {
case TagVersion:
header.Version = tagValue[0]
// Reconstruct signed data: Tag + Len + Val
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(tagValue)
case TagCurrentLayer:
header.CurrentLayer = tagValue[0]
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(tagValue)
case TagTargetLayer:
header.TargetLayer = tagValue[0]
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(tagValue)
case TagIntent:
header.IntentID = tagValue[0]
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(tagValue)
case TagThreatScore:
header.ThreatScore = binary.BigEndian.Uint16(tagValue)
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(tagValue)
case TagHMAC:
signature = tagValue
// We do NOT add the HMAC itself to signedData
default:
// Unknown tag (future proofing), verify it but ignore semantics
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(tagValue)
}
}
if len(signature) == 0 {
return nil, errMissingHMAC
}
// 5. Verify HMAC
// Reconstruct: Headers (signedData) + Payload
messageAuthCode := hmac.New(sha256.New, sharedSecret)
messageAuthCode.Write(signedData.Bytes())
messageAuthCode.Write(payload)
expectedMAC := messageAuthCode.Sum(nil)
if !hmac.Equal(signature, expectedMAC) {
return nil, errIntegrityViolation
}
return &ParsedPacket{
Header: header,
Payload: payload,
}, nil
}