Skip to content

Instantly share code, notes, and snippets.

@skeeto

skeeto/go.mod

Last active Oct 5, 2020
Embed
What would you like to do?
Password hashing Go service
module hasher
go 1.15
require golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
package main
import (
"flag"
"log"
"net/http"
"runtime"
"golang.org/x/crypto/bcrypt"
)
type handler chan struct{}
func newHandler() handler {
return make(chan struct{}, runtime.GOMAXPROCS(0))
}
// ServeHTTP serves one entrypoint (/). All requests must include form data
// with a one-element "password" field. If the form includes a one-element
// "hash" field, then the password is validated against the hash, returnign
// either "true" and HTTP 200 or "false" and HTTP 401.
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "invalid form data", 400)
return
}
passwords, ok := r.Form["password"]
if !ok || len(passwords) != 1 {
http.Error(w, "missing password", 400)
return
}
hashes, ok := r.Form["hash"]
if ok && len(hashes) != 1 {
http.Error(w, "wrong number of hashes", 400)
return
}
if !ok {
// Hash the password
select {
case h <- struct{}{}:
defer func() { <-h }()
cost := bcrypt.DefaultCost
password := []byte(passwords[0])
hash, _ := bcrypt.GenerateFromPassword(password, cost)
w.Write(hash)
case <-r.Context().Done():
log.Println("hash request cancelled")
return
}
} else {
// Validate the password to the hash
select {
case h <- struct{}{}:
defer func() { <-h }()
hash := []byte(hashes[0])
password := []byte(passwords[0])
// TODO: to avoid DoS, reject hashes with high cost?
if bcrypt.CompareHashAndPassword(hash, password) == nil {
w.WriteHeader(200)
w.Write([]byte("true"))
} else {
w.WriteHeader(401)
w.Write([]byte("false"))
}
case <-r.Context().Done():
log.Println("validate request cancelled")
return
}
}
}
func main() {
addr := flag.String("addr", ":8080", "Server's host address")
flag.Parse()
s := &http.Server{
Addr: *addr,
Handler: newHandler(),
}
log.Printf("listening at %s", *addr)
log.Fatal(s.ListenAndServe())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment