dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

commit de01f1a197763aa731d07b1e768ef3f70e277f89
parent 9a4f6777e843dc847cffe4f4764b21fea1bbab22
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Sat, 27 May 2023 20:45:45 -0700

optional pow for registration

Diffstat:
Mcmd/pow/main.go | 11+++++------
Mpkg/config/config.go | 3+++
Mpkg/web/handlers/data.go | 7+++++++
Mpkg/web/handlers/handlers.go | 19+++++++++++++++++++
Apkg/web/public/views/pages/pow-help.gohtml | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/web/public/views/pages/standalone/signup.gohtml | 7+++++++
Mpkg/web/web.go | 1+
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))