From 41d3abd30cb30be8a8e95c8396da5bb84ab72881 Mon Sep 17 00:00:00 2001 From: Gab Virebent Date: Sun, 12 Jul 2026 19:48:39 +0200 Subject: Add Fyne GUI and portable --data-dir mode to nymdrop-reader Extract reader logic (key management, embedded nym-client lifecycle, receive loop, decrypt, save) into internal/reader as a Hooks-driven package, shared by both the existing headless CLI and a new --gui mode built with Fyne. Add --data-dir to make a reader instance fully self-contained: privkey, inbox, and the embedded nym-client's own state (normally fixed to $HOME/.nym) all live under the given directory via a HOME override on the subprocess, enabling zero-trace USB operation. CLI default behavior is unchanged (same paths, same protocol) so the pietro deployment is unaffected. --- internal/reader/reader_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 internal/reader/reader_test.go (limited to 'internal/reader/reader_test.go') diff --git a/internal/reader/reader_test.go b/internal/reader/reader_test.go new file mode 100644 index 0000000..896f8c0 --- /dev/null +++ b/internal/reader/reader_test.go @@ -0,0 +1,64 @@ +package reader + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdh" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "io" + "testing" + + "golang.org/x/crypto/hkdf" +) + +// Verifies the decryptMessage path still produces the correct plaintext after +// wrapping the ECDH/HKDF secrets in memguard.LockedBuffer, using the exact +// wire format the browser client (static/crypto.js) and relay produce. +func TestDecryptMessageAfterMemguard(t *testing.T) { + readerPriv, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + ephPriv, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + + shared, err := ephPriv.ECDH(readerPriv.PublicKey()) + if err != nil { + t.Fatal(err) + } + aesKey := make([]byte, 32) + hkdfR := hkdf.New(sha256.New, shared, make([]byte, 32), []byte("nymdrop-v1")) + if _, err := io.ReadFull(hkdfR, aesKey); err != nil { + t.Fatal(err) + } + block, err := aes.NewCipher(aesKey) + if err != nil { + t.Fatal(err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + t.Fatal(err) + } + iv := make([]byte, 12) + if _, err := rand.Read(iv); err != nil { + t.Fatal(err) + } + want := "E2E memguard verification message" + ciphertext := gcm.Seal(nil, iv, []byte(want), nil) + + packet := append(append(append([]byte{}, ephPriv.PublicKey().Bytes()...), iv...), ciphertext...) + noncePrefix := hex.EncodeToString(make([]byte, 16)) + ":" + raw := append([]byte(noncePrefix), packet...) + + got, err := decryptMessage(raw, readerPriv) + if err != nil { + t.Fatalf("decryptMessage: %v", err) + } + if got != want { + t.Fatalf("plaintext mismatch: got %q want %q", got, want) + } +} -- cgit v1.2.3