diff options
| author | Gab Virebent <gabriel1@virebent.art> | 2026-07-06 16:34:47 +0200 |
|---|---|---|
| committer | Gab Virebent <gabriel1@virebent.art> | 2026-07-06 16:34:47 +0200 |
| commit | 16854e8b24a3b00d9d5dbd285bbe3b93ee47ce1c (patch) | |
| tree | 7ac7c321cacd62795cc23939bebdc9c48975377e /cmd/nymdrop-reader/memguard_verify_test.go | |
| parent | 4fd6b4fefa7243f68fae7e24df3da46ee1a8e356 (diff) | |
| download | nymdrop-16854e8b24a3b00d9d5dbd285bbe3b93ee47ce1c.tar.gz nymdrop-16854e8b24a3b00d9d5dbd285bbe3b93ee47ce1c.tar.xz nymdrop-16854e8b24a3b00d9d5dbd285bbe3b93ee47ce1c.zip | |
Guard key material in RAM with memguard, add Markdown preview for message field
Reader now keeps the private key bytes and the per-submission ECDH/HKDF
secrets in memguard.LockedBuffer (mlock + guaranteed wipe) instead of a
manual zero loop. Added a regression test for the decrypt path.
Message field gets a self-hosted Markdown toggle preview (no external
library): headers, bold/italic, inline code, code blocks, blockquotes,
lists, links. Output is HTML-escaped and link schemes are whitelisted
before rendering. Submitted content is still the raw Markdown source,
unchanged on the wire.
Diffstat (limited to 'cmd/nymdrop-reader/memguard_verify_test.go')
| -rw-r--r-- | cmd/nymdrop-reader/memguard_verify_test.go | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/cmd/nymdrop-reader/memguard_verify_test.go b/cmd/nymdrop-reader/memguard_verify_test.go new file mode 100644 index 0000000..3ab5fce --- /dev/null +++ b/cmd/nymdrop-reader/memguard_verify_test.go @@ -0,0 +1,65 @@ +package main + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdh" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "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)) + ":" + dataB64 := base64.StdEncoding.EncodeToString(append([]byte(noncePrefix), packet...)) + + got, err := decryptMessage(dataB64, readerPriv) + if err != nil { + t.Fatalf("decryptMessage: %v", err) + } + if got != want { + t.Fatalf("plaintext mismatch: got %q want %q", got, want) + } +} |
