commit de01f1a197763aa731d07b1e768ef3f70e277f89
parent 9a4f6777e843dc847cffe4f4764b21fea1bbab22
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Sat, 27 May 2023 20:45:45 -0700
optional pow for registration
Diffstat:
7 files changed, 100 insertions(+), 6 deletions(-)
diff --git a/cmd/pow/main.go b/cmd/pow/main.go
@@ -9,19 +9,18 @@ import (
"strings"
)
+// Build with `go build -o pow main.go`
+// Use `./pow my_username`
func main() {
- // ./pow n0tr1v
- // 196598046 000000013f9472ce72d6ef66b10ff25db702a712484d72c157d52b260872686e
username := os.Args[1]
difficulty := 7
prefix := strings.Repeat("0", difficulty)
var nonce int
- var hashed string
for {
- h := sha256.Sum256([]byte(username + "_" + strconv.Itoa(nonce)))
- hashed = hex.EncodeToString(h[:])
+ h := sha256.Sum256([]byte(username + ":" + strconv.Itoa(nonce)))
+ hashed := hex.EncodeToString(h[:])
if strings.HasPrefix(hashed, prefix) {
- fmt.Printf("%d %s\n", nonce, hashed)
+ fmt.Printf("%s:%d -> %s\n", username, nonce, hashed)
return
}
nonce++
diff --git a/pkg/config/config.go b/pkg/config/config.go
@@ -49,6 +49,8 @@ const (
AnnouncementsRoomName = "announcements"
)
+const PowDifficulty = 7
+
const EditMessageTimeLimit = 2 * time.Minute
const NullUsername = "0"
@@ -66,6 +68,7 @@ var (
ForumEnabled = atom.NewBool(true) // either or not people can use the forum features
SilentSelfKick = atom.NewBool(true) // either or not self kick are silent
SignupEnabled = atom.NewBool(true) // either or not people can sign up
+ PowEnabled = atom.NewBool(false) // either or not pow is enabled to signup
SignupFakeEnabled = atom.NewBool(true) // either or not signup is faked to be enabled
ProtectHome = atom.NewBool(true) // enable "dynamic login url" to prevent ddos on the login page
HomeUsersList = atom.NewBool(true) // either or not to display the online users on the login page
diff --git a/pkg/web/handlers/data.go b/pkg/web/handlers/data.go
@@ -73,6 +73,7 @@ type signupData struct {
Username string
Password string
RePassword string
+ Pow string
CaptchaImg string
CaptchaAnswerImg string
CaptchaID string
@@ -81,7 +82,9 @@ type signupData struct {
Frames []string
HasSolvedCaptcha bool
ErrCaptcha string
+ ErrPow string
Errors database.UserErrors
+ PowEnabled bool
}
type byteRoadChallengeData struct {
@@ -886,3 +889,7 @@ type chessData struct {
Error string
Username database.Username
}
+
+type powHelperData struct {
+ Difficulty int64
+}
diff --git a/pkg/web/handlers/handlers.go b/pkg/web/handlers/handlers.go
@@ -840,6 +840,7 @@ func signupHandler(c echo.Context) error {
var data signupData
config.SignupPageLoad.Inc()
+ data.PowEnabled = config.PowEnabled.Load()
data.CaptchaSec = 120
data.Frames = generateCssFrames(data.CaptchaSec, nil, true)
@@ -857,6 +858,7 @@ func signupHandler(c echo.Context) error {
data.Username = strings.TrimSpace(c.Request().PostFormValue("username"))
data.Password = c.Request().PostFormValue("password")
data.RePassword = c.Request().PostFormValue("repassword")
+ data.Pow = c.Request().PostFormValue("pow")
captchaID := c.Request().PostFormValue("captcha_id")
captchaInput := c.Request().PostFormValue("captcha")
captchaInputImg := c.Request().PostFormValue("captcha_img")
@@ -874,6 +876,17 @@ func signupHandler(c echo.Context) error {
data.HasSolvedCaptcha = signupInfo.hasSolvedCaptcha
signupCache.SetD(signupToken, signupInfo)
+ // verify POW
+ if config.PowEnabled.IsTrue() {
+ h := sha256.Sum256([]byte(data.Username + ":" + data.Pow))
+ hashed := hex.EncodeToString(h[:])
+ prefix := strings.Repeat("0", config.PowDifficulty)
+ if !strings.HasPrefix(hashed, prefix) {
+ data.ErrPow = "invalid proof of work"
+ return c.Render(http.StatusOK, "standalone.signup", data)
+ }
+ }
+
config.SignupSucceed.Inc()
// If SignupFakeEnabled is enabled, we always say the account was created, but we do not create it.
@@ -1943,6 +1956,12 @@ func TorchessHandler(c echo.Context) error {
return c.Render(http.StatusOK, "torchess", nil)
}
+func PowHelpHandler(c echo.Context) error {
+ var data powHelperData
+ data.Difficulty = config.PowDifficulty
+ return c.Render(http.StatusOK, "pow-help", data)
+}
+
func CaptchaHelpHandler(c echo.Context) error {
return c.Render(http.StatusOK, "captcha-help", nil)
}
diff --git a/pkg/web/public/views/pages/pow-help.gohtml b/pkg/web/public/views/pages/pow-help.gohtml
@@ -0,0 +1,57 @@
+{{ define "title" }}dkf - pow help{{ end }}
+
+{{ define "content" }}
+
+<div class="container">
+ <div>
+
+ <h3>Proof of work help</h3>
+
+ <p>
+ To calculate the POW, you have to compute a suffix such that
+ the sha256 of <code>username:suffix</code> starts with seven zeros.
+ </p>
+ <p>
+ For example, the sha256 of <code>my_username:69395289</code> is<br />
+ <code><strong>0000000</strong>688a8637a52bd0f295e505cd85156ea2c403aa633e200cf73b4915cf1</code><br />
+ which starts with seven zeros, so your proof of work would be <code>69395289</code>
+ </p>
+
+ <p>
+ You can use the following script to calculate the proof of work:
+ </p>
+
+ <pre style="border: 1px solid #5d5d5d; padding: 5px;"><code>package main
+
+import (
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+)
+
+// Build with `go build -o pow main.go`
+// Use `./pow my_username`
+func main() {
+ username := os.Args[1]
+ difficulty := {{ .Data.Difficulty }}
+ prefix := strings.Repeat("0", difficulty)
+ var nonce int
+ for {
+ h := sha256.Sum256([]byte(username + ":" + strconv.Itoa(nonce)))
+ hashed := hex.EncodeToString(h[:])
+ if strings.HasPrefix(hashed, prefix) {
+ fmt.Printf("%s:%d -> %s\n", username, nonce, hashed)
+ return
+ }
+ nonce++
+ }
+}
+</code></pre>
+
+ </div>
+</div>
+
+{{ end }}
+\ No newline at end of file
diff --git a/pkg/web/public/views/pages/standalone/signup.gohtml b/pkg/web/public/views/pages/standalone/signup.gohtml
@@ -37,6 +37,13 @@
<div class="form-group">
<input class="transparent-input form-control" placeholder="{{ t "Confirm password" . }}" name="repassword" type="password" value="{{ .Data.RePassword }}" />
</div>
+ {{ if .Data.PowEnabled }}
+ <div class="form-group">
+ <div class="text-center mb-2 bg-label"><a href="/pow-help" target="_blank" rel="noopener noreferrer">Proof of work help</a></div>
+ <input class="transparent-input form-control{{ if .Data.ErrPow }} is-invalid{{ end }}" placeholder="{{ t "Proof of work" . }}" name="pow" type="text" value="{{ .Data.Pow }}" />
+ {{ if .Data.ErrPow }}<div class="invalid-feedback d-block">{{ .Data.ErrPow }}</div>{{ end }}
+ </div>
+ {{ end }}
{{ if not .Data.HasSolvedCaptcha }}
<div class="form-group">
<div class="text-center mb-2 bg-label">Captcha expires in <span id="timer_countdown"></span> seconds (<a href="/captcha-help" target="_blank" rel="noopener noreferrer">help</a>)</div>
diff --git a/pkg/web/web.go b/pkg/web/web.go
@@ -58,6 +58,7 @@ func getMainServer(db *database.DkfDB, i18nBundle *i18n.Bundle, renderer *tmp.Te
e.GET("/bhcli", handlers.BhcliHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
e.GET("/torchess", handlers.TorchessHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
e.GET("/captcha-help", handlers.CaptchaHelpHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
+ e.GET("/pow-help", handlers.PowHelpHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
e.GET("/werewolf", handlers.WerewolfHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
e.GET("/gists/:gistUUID", handlers.GistHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 5, false))
e.POST("/gists/:gistUUID", handlers.GistHandler, middlewares.CircuitRateLimitMiddleware(1*time.Second, 3, false))