mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-01 16:48:19 +00:00
any new channel added to config will get backfilled if there are any messages to decrypt closes: #68
149 lines
4.8 KiB
Go
149 lines
4.8 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package ingest
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/MeshCore-Beacon/beacon-server/internal/keystore"
|
|
"github.com/meshcore-go/meshcore-go"
|
|
)
|
|
|
|
// encryptedGroupText returns the raw GRP_TXT payload bytes for a message encrypted under psk
|
|
// -- a real round trip through meshcore-go's own Encrypt, not a hand-rolled fixture.
|
|
func encryptedGroupText(t *testing.T, channelHash byte, psk []byte, sender, text string) []byte {
|
|
t.Helper()
|
|
grpTxt, err := (&meshcore.GroupTextPayload{
|
|
Timestamp: 1000,
|
|
Sender: sender,
|
|
Text: text,
|
|
}).Encrypt(channelHash, psk)
|
|
if err != nil {
|
|
t.Fatalf("encrypt group text: %v", err)
|
|
}
|
|
raw, err := grpTxt.ToBytes()
|
|
if err != nil {
|
|
t.Fatalf("group text to bytes: %v", err)
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func TestDecryptGroupText_Success(t *testing.T) {
|
|
db := &stubDB{insertChannelMessageResult: true}
|
|
psk := make([]byte, 16)
|
|
channelHash := byte(0x11)
|
|
keys := &mapKeys{entries: map[byte][]keystore.Entry{
|
|
channelHash: {{Key: psk, Fingerprint: []byte{0xAA}, Name: "Public", Hashtag: "public"}},
|
|
}}
|
|
raw := encryptedGroupText(t, channelHash, psk, "ded", "hello")
|
|
|
|
result, err := DecryptGroupText(context.Background(), db, keys, []byte{0x01}, raw)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatal("expected a non-nil result for a decryptable packet")
|
|
}
|
|
if result.Payload.Sender != "ded" || result.Payload.Text != "hello" {
|
|
t.Errorf("expected decrypted sender=ded text=hello, got sender=%s text=%s", result.Payload.Sender, result.Payload.Text)
|
|
}
|
|
if !result.NewMessage {
|
|
t.Error("expected NewMessage true when InsertChannelMessage reports a new insert")
|
|
}
|
|
if db.upsertChannelCalls != 1 {
|
|
t.Errorf("expected UpsertChannel to be called once, got %d", db.upsertChannelCalls)
|
|
}
|
|
}
|
|
|
|
func TestDecryptGroupText_NoMatchingKey(t *testing.T) {
|
|
db := &stubDB{}
|
|
channelHash := byte(0x22)
|
|
// Encrypted under a key the store doesn't have -- keys is otherwise empty for this hash.
|
|
raw := encryptedGroupText(t, channelHash, make([]byte, 16), "ded", "hello")
|
|
keys := &mapKeys{entries: map[byte][]keystore.Entry{}}
|
|
|
|
result, err := DecryptGroupText(context.Background(), db, keys, []byte{0x02}, raw)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result != nil {
|
|
t.Errorf("expected a nil result when no known key decrypts the packet, got %+v", result)
|
|
}
|
|
if db.upsertChannelCalls != 0 {
|
|
t.Errorf("expected UpsertChannel NOT to be called, got %d calls", db.upsertChannelCalls)
|
|
}
|
|
}
|
|
|
|
func TestDecryptGroupText_WrongKeyForHash(t *testing.T) {
|
|
db := &stubDB{}
|
|
channelHash := byte(0x33)
|
|
raw := encryptedGroupText(t, channelHash, make([]byte, 16), "ded", "hello")
|
|
// A key IS registered for this hash, but it's the wrong one (1-byte hash collisions are
|
|
// expected; DecryptStruct's MAC check is what actually distinguishes them).
|
|
wrongKey := make([]byte, 16)
|
|
wrongKey[0] = 0xFF
|
|
keys := &mapKeys{entries: map[byte][]keystore.Entry{
|
|
channelHash: {{Key: wrongKey, Fingerprint: []byte{0xBB}}},
|
|
}}
|
|
|
|
result, err := DecryptGroupText(context.Background(), db, keys, []byte{0x03}, raw)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result != nil {
|
|
t.Errorf("expected a nil result for a MAC mismatch, got %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestDecryptGroupText_MalformedPayload(t *testing.T) {
|
|
db := &stubDB{}
|
|
keys := &mapKeys{}
|
|
_, err := DecryptGroupText(context.Background(), db, keys, []byte{0x04}, []byte{})
|
|
if err == nil {
|
|
t.Fatal("expected an error for an empty/malformed payload")
|
|
}
|
|
}
|
|
|
|
func TestBackfillChannelMessages_DecryptsAndCounts(t *testing.T) {
|
|
psk := make([]byte, 16)
|
|
channelHash := byte(0x44)
|
|
decryptable := encryptedGroupText(t, channelHash, psk, "ded", "hi")
|
|
stillUnknown := encryptedGroupText(t, byte(0x55), make([]byte, 16), "someone", "bye")
|
|
|
|
db := &stubDB{
|
|
insertChannelMessageResult: true,
|
|
undecryptedPackets: []UndecryptedPacket{
|
|
{PacketHash: []byte{0x01}, RawPayload: decryptable},
|
|
{PacketHash: []byte{0x02}, RawPayload: stillUnknown},
|
|
},
|
|
}
|
|
keys := &mapKeys{entries: map[byte][]keystore.Entry{
|
|
channelHash: {{Key: psk, Fingerprint: []byte{0xAA}, Name: "Public"}},
|
|
}}
|
|
|
|
n, err := BackfillChannelMessages(context.Background(), db, keys)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if n != 1 {
|
|
t.Errorf("expected 1 packet newly decrypted (the other has no known key yet), got %d", n)
|
|
}
|
|
if db.upsertChannelCalls != 1 {
|
|
t.Errorf("expected UpsertChannel called once (only for the decryptable packet), got %d", db.upsertChannelCalls)
|
|
}
|
|
}
|
|
|
|
func TestBackfillChannelMessages_NoUndecryptedPackets(t *testing.T) {
|
|
db := &stubDB{}
|
|
keys := &mapKeys{}
|
|
n, err := BackfillChannelMessages(context.Background(), db, keys)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if n != 0 {
|
|
t.Errorf("expected 0, got %d", n)
|
|
}
|
|
}
|