1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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", "n2usenet@virebent.art", "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 ""
}
|