AX principle 1 — predictable names over short names. The usage example comment in TestPacket_MarshalAndSign_Ugly used `secret` (abbreviated) where all other examples in the package consistently use `sharedSecret`. Co-Authored-By: Charon <charon@lethean.io>
241 lines
7.6 KiB
Go
241 lines
7.6 KiB
Go
package ueps
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"testing"
|
|
)
|
|
|
|
// builder := NewPacketBuilder(0x01, []byte("hello"))
|
|
// builder.Header.Version == 0x09; builder.Header.CurrentLayer == 5
|
|
func TestPacket_NewPacketBuilder_Good(t *testing.T) {
|
|
intentID := uint8(0x01)
|
|
payload := []byte("hello")
|
|
|
|
builder := NewPacketBuilder(intentID, payload)
|
|
|
|
if builder == nil {
|
|
t.Fatal("NewPacketBuilder returned nil")
|
|
}
|
|
if builder.Header.Version != 0x09 {
|
|
t.Errorf("expected Version 0x09, got 0x%02x", builder.Header.Version)
|
|
}
|
|
if builder.Header.CurrentLayer != 5 {
|
|
t.Errorf("expected CurrentLayer 5, got %d", builder.Header.CurrentLayer)
|
|
}
|
|
if builder.Header.TargetLayer != 5 {
|
|
t.Errorf("expected TargetLayer 5, got %d", builder.Header.TargetLayer)
|
|
}
|
|
if builder.Header.IntentID != intentID {
|
|
t.Errorf("expected IntentID 0x%02x, got 0x%02x", intentID, builder.Header.IntentID)
|
|
}
|
|
if builder.Header.ThreatScore != 0 {
|
|
t.Errorf("expected ThreatScore 0, got %d", builder.Header.ThreatScore)
|
|
}
|
|
if !bytes.Equal(builder.Payload, payload) {
|
|
t.Errorf("expected Payload %q, got %q", payload, builder.Payload)
|
|
}
|
|
}
|
|
|
|
// builder := NewPacketBuilder(0x04, nil)
|
|
// frame, err := builder.MarshalAndSign([]byte("shared-secret")) // succeeds with zero-length payload
|
|
func TestPacket_NewPacketBuilder_Bad(t *testing.T) {
|
|
sharedSecret := []byte("shared-secret")
|
|
builder := NewPacketBuilder(0x04, nil)
|
|
|
|
if builder == nil {
|
|
t.Fatal("NewPacketBuilder returned nil for nil payload")
|
|
}
|
|
if builder.Payload != nil {
|
|
t.Errorf("expected nil Payload, got %v", builder.Payload)
|
|
}
|
|
|
|
frame, err := builder.MarshalAndSign(sharedSecret)
|
|
if err != nil {
|
|
t.Fatalf("MarshalAndSign with nil payload failed: %v", err)
|
|
}
|
|
if len(frame) == 0 {
|
|
t.Error("expected non-empty frame for nil payload")
|
|
}
|
|
}
|
|
|
|
// builder := NewPacketBuilder(0x00, largePayload)
|
|
// frame, err := builder.MarshalAndSign([]byte("shared-secret")) // succeeds; 0xFF tag appended without length prefix
|
|
func TestPacket_NewPacketBuilder_Ugly(t *testing.T) {
|
|
sharedSecret := []byte("shared-secret")
|
|
largePayload := bytes.Repeat([]byte("x"), 300)
|
|
builder := NewPacketBuilder(0x00, largePayload)
|
|
|
|
if builder.Header.IntentID != 0x00 {
|
|
t.Errorf("expected IntentID 0x00, got 0x%02x", builder.Header.IntentID)
|
|
}
|
|
|
|
frame, err := builder.MarshalAndSign(sharedSecret)
|
|
if err != nil {
|
|
t.Fatalf("MarshalAndSign with 300-byte payload failed: %v", err)
|
|
}
|
|
if !bytes.Contains(frame, largePayload) {
|
|
t.Error("frame does not contain the large payload")
|
|
}
|
|
}
|
|
|
|
// builder := NewPacketBuilder(0x01, []byte("ping"))
|
|
// frame, err := builder.MarshalAndSign([]byte("my-shared-secret"))
|
|
func TestPacket_MarshalAndSign_Good(t *testing.T) {
|
|
builder := NewPacketBuilder(0x01, []byte("ping"))
|
|
sharedSecret := []byte("my-shared-secret")
|
|
|
|
frame, err := builder.MarshalAndSign(sharedSecret)
|
|
|
|
if err != nil {
|
|
t.Fatalf("MarshalAndSign failed: %v", err)
|
|
}
|
|
if len(frame) == 0 {
|
|
t.Fatal("expected non-empty frame")
|
|
}
|
|
|
|
// bytes.Contains(frame, []byte{TagHMAC}) → true (all 7 mandatory tags embedded)
|
|
for _, expectedTag := range []byte{TagVersion, TagCurrentLayer, TagTargetLayer, TagIntent, TagThreatScore, TagHMAC, TagPayload} {
|
|
if !bytes.Contains(frame, []byte{expectedTag}) {
|
|
t.Errorf("frame missing tag 0x%02x", expectedTag)
|
|
}
|
|
}
|
|
}
|
|
|
|
// frameWithSecretA, _ := builder.MarshalAndSign([]byte("secret-a"))
|
|
// frameWithSecretB, _ := builder.MarshalAndSign([]byte("secret-b"))
|
|
// frameWithSecretA != frameWithSecretB (HMAC tag differs)
|
|
func TestPacket_MarshalAndSign_Bad(t *testing.T) {
|
|
builder := NewPacketBuilder(0x02, []byte("data"))
|
|
|
|
frameWithSecretA, err := builder.MarshalAndSign([]byte("secret-a"))
|
|
if err != nil {
|
|
t.Fatalf("MarshalAndSign(secret-a) failed: %v", err)
|
|
}
|
|
|
|
frameWithSecretB, err := builder.MarshalAndSign([]byte("secret-b"))
|
|
if err != nil {
|
|
t.Fatalf("MarshalAndSign(secret-b) failed: %v", err)
|
|
}
|
|
|
|
if bytes.Equal(frameWithSecretA, frameWithSecretB) {
|
|
t.Error("expected different frames for different secrets, got identical frames")
|
|
}
|
|
}
|
|
|
|
// frame, _ := builder.MarshalAndSign(sharedSecret)
|
|
// corrupted[len(frame)-1] ^= 0xFF
|
|
// _, err := ReadAndVerify(bufio.NewReader(bytes.NewReader(corrupted)), sharedSecret)
|
|
// err == errIntegrityViolation (HMAC mismatch detected)
|
|
func TestPacket_MarshalAndSign_Ugly(t *testing.T) {
|
|
builder := NewPacketBuilder(0x03, []byte("sensitive"))
|
|
sharedSecret := []byte("my-secret")
|
|
|
|
frame, err := builder.MarshalAndSign(sharedSecret)
|
|
if err != nil {
|
|
t.Fatalf("MarshalAndSign failed: %v", err)
|
|
}
|
|
|
|
corrupted := make([]byte, len(frame))
|
|
copy(corrupted, frame)
|
|
corrupted[len(corrupted)-1] ^= 0xFF
|
|
|
|
_, err = ReadAndVerify(bufio.NewReader(bytes.NewReader(corrupted)), sharedSecret)
|
|
if err == nil {
|
|
t.Error("expected HMAC integrity violation for corrupted frame, got nil")
|
|
}
|
|
}
|
|
|
|
// writeTLV(buffer, TagVersion, []byte{0x09})
|
|
// buffer contains: [0x01, 0x01, 0x09]
|
|
func TestPacket_writeTLV_Good(t *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
|
|
err := writeTLV(buffer, TagVersion, []byte{0x09})
|
|
|
|
if err != nil {
|
|
t.Fatalf("writeTLV failed: %v", err)
|
|
}
|
|
encodedTLV := buffer.Bytes()
|
|
if len(encodedTLV) != 3 {
|
|
t.Fatalf("expected 3 bytes, got %d", len(encodedTLV))
|
|
}
|
|
if encodedTLV[0] != TagVersion {
|
|
t.Errorf("expected tag 0x%02x, got 0x%02x", TagVersion, encodedTLV[0])
|
|
}
|
|
if encodedTLV[1] != 0x01 {
|
|
t.Errorf("expected length 1, got %d", encodedTLV[1])
|
|
}
|
|
if encodedTLV[2] != 0x09 {
|
|
t.Errorf("expected value 0x09, got 0x%02x", encodedTLV[2])
|
|
}
|
|
}
|
|
|
|
// writeTLV(buffer, TagPayload, bytes.Repeat([]byte("x"), 256))
|
|
// err == errTLVValueTooLarge (sentinel identity preserved)
|
|
func TestPacket_writeTLV_Bad(t *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
oversizedValue := bytes.Repeat([]byte("x"), 256)
|
|
|
|
err := writeTLV(buffer, TagPayload, oversizedValue)
|
|
|
|
if err == nil {
|
|
t.Fatal("expected errTLVValueTooLarge for oversized TLV value, got nil")
|
|
}
|
|
if err != errTLVValueTooLarge {
|
|
t.Errorf("expected errTLVValueTooLarge, got %v", err)
|
|
}
|
|
}
|
|
|
|
// writeTLV(buffer, TagIntent, []byte{})
|
|
// buffer contains: [0x04, 0x00] (tag + zero length, no value bytes)
|
|
func TestPacket_writeTLV_Ugly(t *testing.T) {
|
|
buffer := new(bytes.Buffer)
|
|
|
|
err := writeTLV(buffer, TagIntent, []byte{})
|
|
|
|
if err != nil {
|
|
t.Fatalf("writeTLV with empty value failed: %v", err)
|
|
}
|
|
encodedTLV := buffer.Bytes()
|
|
if len(encodedTLV) != 2 {
|
|
t.Fatalf("expected 2 bytes for empty value, got %d", len(encodedTLV))
|
|
}
|
|
if encodedTLV[0] != TagIntent {
|
|
t.Errorf("expected tag 0x%02x, got 0x%02x", TagIntent, encodedTLV[0])
|
|
}
|
|
if encodedTLV[1] != 0x00 {
|
|
t.Errorf("expected length 0, got %d", encodedTLV[1])
|
|
}
|
|
}
|
|
|
|
// var sentinel sentinelError = "record not found"; sentinel.Error() == "record not found"
|
|
func TestPacket_sentinelError_Error_Good(t *testing.T) {
|
|
sentinel := sentinelError("record not found")
|
|
|
|
if sentinel.Error() != "record not found" {
|
|
t.Errorf("expected %q, got %q", "record not found", sentinel.Error())
|
|
}
|
|
}
|
|
|
|
// var sentinel sentinelError = ""; sentinel.Error() == "" (empty message is valid sentinel)
|
|
func TestPacket_sentinelError_Error_Bad(t *testing.T) {
|
|
sentinel := sentinelError("")
|
|
|
|
if sentinel.Error() != "" {
|
|
t.Errorf("expected empty string, got %q", sentinel.Error())
|
|
}
|
|
}
|
|
|
|
// var sentinelFirst, sentinelSecond sentinelError = "x", "x"; sentinelFirst == sentinelSecond (sentinel identity: same message = same error)
|
|
func TestPacket_sentinelError_Error_Ugly(t *testing.T) {
|
|
sentinelFirst := sentinelError("integrity violation")
|
|
sentinelSecond := sentinelError("integrity violation")
|
|
|
|
if sentinelFirst != sentinelSecond {
|
|
t.Errorf("expected sentinel identity: %q == %q", sentinelFirst, sentinelSecond)
|
|
}
|
|
if sentinelFirst.Error() != sentinelSecond.Error() {
|
|
t.Errorf("expected identical Error() output, got %q vs %q", sentinelFirst.Error(), sentinelSecond.Error())
|
|
}
|
|
}
|