NOJSCAP/server/go-http/server.go

179 lines
4 KiB
Go

package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"html/template"
"log"
"net/http"
"strconv"
"sync"
"time"
)
const (
port = 3000
difficulty = 20
)
var (
challengeStore = make(map[string]time.Time)
storeMutex sync.Mutex
)
func generateChallenge() string {
b := make([]byte, 12)
_, err := rand.Read(b)
if err != nil {
panic(err)
}
return hex.EncodeToString(b)
}
func checkDifficulty(hash []byte, bits int) bool {
i := 0
for bits > 0 {
b := hash[i]
if bits >= 8 {
if b != 0 {
return false
}
bits -= 8
} else {
mask := byte(0xFF << (8 - bits))
if b&mask != 0 {
return false
}
bits = 0
}
i++
}
return true
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
challenge := generateChallenge()
storeMutex.Lock()
challengeStore[challenge] = time.Now()
storeMutex.Unlock()
tmpl.Execute(w, map[string]string{
"Challenge": challenge,
"Difficulty": strconv.Itoa(difficulty),
})
}
func postHandler(w http.ResponseWriter, r *http.Request) {
challenge := r.FormValue("challenge")
nonce := r.FormValue("nonce")
storeMutex.Lock()
_, ok := challengeStore[challenge]
if ok {
delete(challengeStore, challenge)
}
storeMutex.Unlock()
if !ok {
fmt.Fprint(w, "<p>Invalid challenge.</p><a href='/'>Try again</a>")
return
}
hash := sha256.Sum256([]byte(challenge + nonce))
if checkDifficulty(hash[:], difficulty) {
fmt.Fprint(w, "<div style='text-align:center;'><h1>Success! Valid nonce.</h1><a href='/'>Try again</a></div>")
} else {
fmt.Fprint(w, "<div style='text-align:center;'><p>Invalid nonce.</p><a href='/'>Try again</a></div>")
}
}
var tmpl = template.Must(template.New("page").Parse(`
<!doctype html>
<html lang="en">
<head>
<title>NOJSCAP demo</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
padding: 20px;
margin-bottom: 300px;
background: #cacaca;
color: black;
}
::selection {
background: #a60000;
color: white;
}
code {
white-space: break-spaces;
background: black;
color: white;
padding: 10px;
display: block;
max-width: 500px;
}
.h {
font-size: 21px;
font-weight: bolder;
width: 100%;
float: left;
}
button {
background: #006ed0;
color: white;
border: 1px solid #006ed0;
padding: 10px 20px;
margin-top: 10px;
}
input {
background: black;
color: white;
padding: 10px;
display: block;
max-width: 500px;
width: 100%;
border: 0px solid transparent;
font-family: monospace;
font-size: 12px;
}
</style>
</head>
<body>
<h1>NOJSCAP demo</h1>
<p><a href="https://git.libroot.org/libroot/NOJSCAP/" target="_blank">https://git.libroot.org/libroot/NOJSCAP/</a></p>
<p>If you don't already have the <b><em>NOJSCAP</em></b> client:</p>
<code>$ git clone https://git.libroot.org/libroot/NOJSCAP/$ cd NOJSCAP/client/</code>
<p><code>$ python3 nojscap.py {{.Challenge}} {{.Difficulty}}</code></p>
<p><strong>Go:</strong></p>
<p><code>$ go run nojscap.go {{.Challenge}} {{.Difficulty}}</code></p>
<p><strong>Node.js:</strong></p>
<p><code>$ node nojscap.js {{.Challenge}} {{.Difficulty}}</code></p>
<p><strong>Rust:</strong></p>
<p><code>$ rustc nojscap.rs -o nojscap_rs$ nojscap_rs {{.Challenge}} {{.Difficulty}}</code></p>
<p><strong>C:</strong></p>
<p><code>$ gcc -O2 -o nojscap nojscap.c -lssl -lcrypto$ ./nojscap {{.Challenge}} {{.Difficulty}}</code></p>
<form method="POST">
<input type="hidden" name="challenge" value="{{.Challenge}}">
<label class="h">Nonce: <input name="nonce"></label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
postHandler(w, r)
} else {
indexHandler(w, r)
}
})
fmt.Printf("Running on http://localhost:%d\n", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}