Mining/pkg/ueps/reader.go
Claude 6704ac781c
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
ax(mining): replace prose comments with usage examples in settings_manager.go
Seven setter/getter comments restated the function signature rather than
showing a concrete call (AX Principle 2). Replaced all with real invocation
examples so agents know exactly how to call each method.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 08:26:55 +01:00

130 lines
3.5 KiB
Go

package ueps
import (
"bufio"
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"io"
)
// packet, err := ReadAndVerify(r, secret)
// if errors.Is(err, errMissingHMAC) { /* no HMAC tag found in frame */ }
var errMissingHMAC = tlvError("UEPS packet missing HMAC signature")
// packet, err := ReadAndVerify(r, wrongSecret)
// if errors.Is(err, errIntegrityViolation) { /* HMAC mismatch — threat score incremented */ }
var errIntegrityViolation = tlvError("integrity violation: HMAC mismatch (ThreatScore +100)")
// packet, err := ueps.ReadAndVerify(r, 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) {
// Buffer to reconstruct the data for HMAC verification
// We have to "record" what we read to verify the signature later.
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
value := make([]byte, length)
if _, err := io.ReadFull(reader, value); err != nil {
return nil, err
}
// Store for processing
switch tag {
case TagVersion:
header.Version = value[0]
// Reconstruct signed data: Tag + Len + Val
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(value)
case TagCurrentLayer:
header.CurrentLayer = value[0]
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(value)
case TagTargetLayer:
header.TargetLayer = value[0]
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(value)
case TagIntent:
header.IntentID = value[0]
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(value)
case TagThreatScore:
header.ThreatScore = binary.BigEndian.Uint16(value)
signedData.WriteByte(tag)
signedData.WriteByte(byte(length))
signedData.Write(value)
case TagHMAC:
signature = value
// 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(value)
}
}
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) {
// Log this. This is a Threat Event.
// "Axiom Violation: Integrity Check Failed"
return nil, errIntegrityViolation
}
return &ParsedPacket{
Header: header,
Payload: payload,
}, nil
}