Files
waste-go/internal/crypto/crypto_test.go
Fredrik Johansson b47f659b7d feat: identity export/import (yaw-key-backup-1)
Portable encrypted identity backup compatible with the sister project's
format — argon2id KDF + nacl secretbox, peer ID visible in plaintext for
pre-import verification.

- crypto: ExportIdentity, ImportIdentity, SaveIdentity
- proto: export_identity / import_identity IPC commands + events
- ipc: export_identity returns encrypted blob; import_identity validates
  and decrypts (read-only — on-disk write requires daemon restart)
- netmgr: MasterIdentity() accessor
- daemon: --import-identity / --import-passphrase flags write identity.json
  and exit, enabling scripted account migration
- tests: roundtrip and wrong-passphrase rejection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 21:44:48 +02:00

110 lines
2.8 KiB
Go

package crypto
import (
"encoding/hex"
"strings"
"testing"
)
func TestPeerIDIsHex(t *testing.T) {
id := newTestIdentity(t)
peerID := string(id.PeerID())
if len(peerID) != 64 {
t.Fatalf("PeerID length = %d, want 64", len(peerID))
}
if strings.ToLower(peerID) != peerID {
t.Fatal("PeerID is not lowercase")
}
if _, err := hex.DecodeString(peerID); err != nil {
t.Fatalf("PeerID is not valid hex: %v", err)
}
}
func TestSignVerifyHex(t *testing.T) {
id := newTestIdentity(t)
msg := []byte("test message")
sig := id.Sign(msg)
if err := Verify(string(id.PeerID()), msg, sig); err != nil {
t.Fatalf("Verify failed: %v", err)
}
}
func TestSignalingBoxRoundTrip(t *testing.T) {
alice := newTestIdentity(t)
bob := newTestIdentity(t)
plaintext := []byte(`{"kind":"offer","sdp":"v=0..."}`)
sealed := SignalingBox(plaintext, bob.CurvePublicKey(), alice.CurvePrivateKey())
got, err := SignalingOpen(sealed, alice.CurvePublicKey(), bob.CurvePrivateKey())
if err != nil {
t.Fatalf("SignalingOpen failed: %v", err)
}
if string(got) != string(plaintext) {
t.Fatalf("got %q, want %q", got, plaintext)
}
}
func TestSignalingBoxTamperedFails(t *testing.T) {
alice := newTestIdentity(t)
bob := newTestIdentity(t)
sealed := SignalingBox([]byte("hello"), bob.CurvePublicKey(), alice.CurvePrivateKey())
// wrong sender public key
carol := newTestIdentity(t)
if _, err := SignalingOpen(sealed, carol.CurvePublicKey(), bob.CurvePrivateKey()); err == nil {
t.Fatal("expected error with wrong sender key, got nil")
}
}
func TestExportImportRoundtrip(t *testing.T) {
id := newTestIdentity(t)
passphrase := "correct horse battery staple"
blob, err := ExportIdentity(id, passphrase)
if err != nil {
t.Fatalf("ExportIdentity: %v", err)
}
// Exported blob must contain the public peer ID in plaintext.
if !strings.Contains(string(blob), string(id.PeerID())) {
t.Fatal("exported blob does not contain peer ID")
}
imported, err := ImportIdentity(blob, passphrase)
if err != nil {
t.Fatalf("ImportIdentity: %v", err)
}
if imported.PeerID() != id.PeerID() {
t.Fatalf("peer ID mismatch: got %s, want %s", imported.PeerID(), id.PeerID())
}
if imported.Alias != id.Alias {
t.Fatalf("alias mismatch: got %q, want %q", imported.Alias, id.Alias)
}
}
func TestImportWrongPassphraseFails(t *testing.T) {
id := newTestIdentity(t)
blob, err := ExportIdentity(id, "correct")
if err != nil {
t.Fatalf("ExportIdentity: %v", err)
}
if _, err := ImportIdentity(blob, "wrong"); err == nil {
t.Fatal("expected error with wrong passphrase, got nil")
}
}
// newTestIdentity creates a fresh in-memory identity for testing.
func newTestIdentity(t *testing.T) *Identity {
t.Helper()
id, err := LoadOrCreate(t.TempDir(), "test")
if err != nil {
t.Fatalf("LoadOrCreate: %v", err)
}
return id
}