diff options
Diffstat (limited to 'internal/submit/validation_test.go')
| -rw-r--r-- | internal/submit/validation_test.go | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/internal/submit/validation_test.go b/internal/submit/validation_test.go new file mode 100644 index 0000000..630b647 --- /dev/null +++ b/internal/submit/validation_test.go @@ -0,0 +1,111 @@ +package submit + +import ( + "crypto/ed25519" + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "fmt" + "net/url" + "strings" + "testing" + "time" + + "n2usenet/internal/config" +) + +func TestParseAndValidateSignedSubmission(t *testing.T) { + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + body := "This is a valid signed test message." + sig := ed25519.Sign(priv, []byte(body)) + sigB64 := base64.StdEncoding.EncodeToString(sig) + pubB64 := base64.StdEncoding.EncodeToString(pub) + + form := url.Values{} + form.Set("from", "Tester <tester@example.net>") + form.Set("newsgroups", "alt.test") + form.Set("subject", "Test") + form.Set("xhashcash", mineTestHashcash(t, "tester@example.net", 8)) + form.Set("message", body+"\n\n--- Digital Signature ---\n"+sigB64) + form.Set("x-ed25519-pub", pubB64) + form.Set("x-ed25519-sig", sigB64) + + sub, err := ParseAndValidate(form, config.SecurityConfig{ + MinHashcashBits: 8, + MinMessageBytes: 10, + MaxMessageBytes: 1024, + MaxNewsgroups: 3, + }) + if err != nil { + t.Fatalf("ParseAndValidate returned error: %v", err) + } + if sub.SignedText != body { + t.Fatalf("signed payload mismatch: %q", sub.SignedText) + } +} + +func TestParseAndValidateRejectsBadSignature(t *testing.T) { + pub, _, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + _, otherPriv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + body := "This is a valid sized test message." + sig := ed25519.Sign(otherPriv, []byte("different")) + form := url.Values{} + form.Set("from", "Tester <tester@example.net>") + form.Set("newsgroups", "alt.test") + form.Set("subject", "Test") + form.Set("xhashcash", mineTestHashcash(t, "tester@example.net", 8)) + form.Set("message", body+"\n\n--- Digital Signature ---\n"+base64.StdEncoding.EncodeToString(sig)) + form.Set("x-ed25519-pub", base64.StdEncoding.EncodeToString(pub)) + form.Set("x-ed25519-sig", base64.StdEncoding.EncodeToString(sig)) + + _, err = ParseAndValidate(form, config.SecurityConfig{ + MinHashcashBits: 8, + MinMessageBytes: 10, + MaxMessageBytes: 1024, + MaxNewsgroups: 3, + }) + if err == nil { + t.Fatal("expected bad signature to be rejected") + } +} + +func TestBuildMessageRequiresFaceWhenConfigured(t *testing.T) { + sub := Submission{ + From: "Tester <tester@example.net>", + FromAddress: "tester@example.net", + Newsgroups: []string{"alt.test"}, + Subject: "Test", + Message: "This is a body.", + PublicKeyB64: base64.StdEncoding.EncodeToString(make([]byte, ed25519.PublicKeySize)), + SignatureB64: base64.StdEncoding.EncodeToString(make([]byte, ed25519.SignatureSize)), + } + _, _, err := BuildMessage(sub, "mail2news@mail2news.tcpreset.net", "example.net", "/missing/identicons-cli", true) + if err == nil { + t.Fatal("expected missing Face generator to reject message") + } +} + +func mineTestHashcash(t *testing.T, resource string, bits int) string { + t.Helper() + date := time.Now().UTC().Format("060102150405") + prefix := fmt.Sprintf("1:%d:%s:%s::test:", bits, date, resource) + target := bits / 4 + for i := 0; i < 1_000_000; i++ { + token := fmt.Sprintf("%s%d", prefix, i) + sum := sha1.Sum([]byte(token)) + if strings.HasPrefix(fmt.Sprintf("%x", sum[:]), strings.Repeat("0", target)) { + return token + } + } + t.Fatal("failed to mine test hashcash") + return "" +} |
