// TLV Types restated what Tag* names already made obvious (AX §2 violation). Replace with a concrete usage example showing the three-tag write sequence. Co-Authored-By: Charon <charon@lethean.io>
123 lines
4.1 KiB
Go
123 lines
4.1 KiB
Go
package ueps
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/hmac"
|
||
"crypto/sha256"
|
||
"encoding/binary"
|
||
"io"
|
||
)
|
||
|
||
// writeTLV(buffer, TagPayload, oversized) → errTLVValueTooLarge
|
||
// if err == errTLVValueTooLarge { /* value exceeded 255-byte TLV length limit */ }
|
||
var errTLVValueTooLarge = tlvError("TLV value too large for 1-byte length header")
|
||
|
||
// var errMyError = tlvError("my error message")
|
||
type tlvError string
|
||
|
||
func (e tlvError) Error() string { return string(e) }
|
||
|
||
// writeTLV(buf, TagVersion, []byte{0x09}); writeTLV(buf, TagHMAC, sig); buf.WriteByte(TagPayload)
|
||
const (
|
||
TagVersion = 0x01
|
||
TagCurrentLayer = 0x02
|
||
TagTargetLayer = 0x03
|
||
TagIntent = 0x04
|
||
TagThreatScore = 0x05
|
||
TagHMAC = 0x06 // writeTLV(buffer, TagHMAC, hmacSignature) — covers all preceding header TLVs + payload
|
||
TagPayload = 0xFF // buffer.WriteByte(TagPayload); buffer.Write(rawPayload) — no length prefix
|
||
)
|
||
|
||
// header := ueps.UEPSHeader{Version: 0x09, CurrentLayer: 5, TargetLayer: 3, IntentID: 0x01, ThreatScore: 0}
|
||
type UEPSHeader struct {
|
||
Version uint8 // 0x09 = IPv9
|
||
CurrentLayer uint8 // header.CurrentLayer = 5 // Application; 3 = Network, 4 = Transport
|
||
TargetLayer uint8 // header.TargetLayer = 3 // Network; 5 = Application (loopback)
|
||
IntentID uint8 // header.IntentID = 0x01 (ping), 0x02 (data), 0x03 (auth)
|
||
ThreatScore uint16 // 0–65535; elevated by integrity violations
|
||
}
|
||
|
||
// builder := ueps.NewPacketBuilder(intentID, payload); frame, _ := builder.MarshalAndSign(secret)
|
||
type PacketBuilder struct {
|
||
Header UEPSHeader
|
||
Payload []byte
|
||
}
|
||
|
||
// builder := ueps.NewPacketBuilder(0x01, []byte("hello"))
|
||
// builder.Header.ThreatScore = 100
|
||
// frame, err := builder.MarshalAndSign(sharedSecret)
|
||
func NewPacketBuilder(intentID uint8, payload []byte) *PacketBuilder {
|
||
return &PacketBuilder{
|
||
Header: UEPSHeader{
|
||
Version: 0x09, // IPv9
|
||
CurrentLayer: 5, // Application
|
||
TargetLayer: 5, // Application
|
||
IntentID: intentID,
|
||
ThreatScore: 0, // Assumed innocent until proven guilty
|
||
},
|
||
Payload: payload,
|
||
}
|
||
}
|
||
|
||
// frame, err := builder.MarshalAndSign([]byte("my-shared-secret"))
|
||
func (builder *PacketBuilder) MarshalAndSign(sharedSecret []byte) ([]byte, error) {
|
||
frameBuffer := new(bytes.Buffer)
|
||
|
||
// writeTLV(frameBuffer, TagVersion, []byte{builder.Header.Version}) → [0x01, 0x01, 0x09]
|
||
if err := writeTLV(frameBuffer, TagVersion, []byte{builder.Header.Version}); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := writeTLV(frameBuffer, TagCurrentLayer, []byte{builder.Header.CurrentLayer}); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := writeTLV(frameBuffer, TagTargetLayer, []byte{builder.Header.TargetLayer}); err != nil {
|
||
return nil, err
|
||
}
|
||
if err := writeTLV(frameBuffer, TagIntent, []byte{builder.Header.IntentID}); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// binary.BigEndian.PutUint16(threatScoreBytes, 100) → [0x00, 0x64]
|
||
threatScoreBytes := make([]byte, 2)
|
||
binary.BigEndian.PutUint16(threatScoreBytes, builder.Header.ThreatScore)
|
||
if err := writeTLV(frameBuffer, TagThreatScore, threatScoreBytes); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// messageAuthCode.Write(frameBuffer.Bytes()) → covers all header TLVs before the HMAC tag
|
||
messageAuthCode := hmac.New(sha256.New, sharedSecret)
|
||
messageAuthCode.Write(frameBuffer.Bytes())
|
||
messageAuthCode.Write(builder.Payload)
|
||
signature := messageAuthCode.Sum(nil)
|
||
|
||
// writeTLV(frameBuffer, TagHMAC, signature) → [0x06, 0x20, <32 bytes>]
|
||
if err := writeTLV(frameBuffer, TagHMAC, signature); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// frameBuffer.Bytes() → [...headerTLVs..., 0x06, 0x20, <hmac32>, 0xFF, <payload...>]
|
||
frameBuffer.WriteByte(TagPayload)
|
||
frameBuffer.Write(builder.Payload)
|
||
|
||
return frameBuffer.Bytes(), nil
|
||
}
|
||
|
||
// writeTLV(buffer, TagVersion, []byte{0x09})
|
||
// writeTLV(buffer, TagIntent, []byte{intentID})
|
||
func writeTLV(writer io.Writer, tag uint8, value []byte) error {
|
||
// writeTLV(w, tag, bytes.Repeat([]byte("x"), 256)) → errTLVValueTooLarge
|
||
if len(value) > 255 {
|
||
return errTLVValueTooLarge
|
||
}
|
||
|
||
if _, err := writer.Write([]byte{tag}); err != nil {
|
||
return err
|
||
}
|
||
if _, err := writer.Write([]byte{uint8(len(value))}); err != nil {
|
||
return err
|
||
}
|
||
if _, err := writer.Write(value); err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|