simple-scrypt
simple-scrypt provides a convenience wrapper around Go's existing
scrypt package that makes it easier to
securely derive strong keys ("hash user passwords"). This library allows you to:
- Generate a scrypt derived key with a crytographically secure salt and sane
default parameters for N, r and p.
- Upgrade the parameters used to generate keys as hardware improves by storing
them with the derived key (the scrypt spec. doesn't allow for this by
default).
- Provide your own parameters (if you wish to).
The API closely mirrors Go's bcrypt
library in an effort to make it easy to migrate—and because it's an easy to grok
API.
Installation
With a working Go toolchain:
go get -u github.com/elithrar/simple-scrypt
Example
simple-scrypt doesn't try to re-invent the wheel or do anything "special". It
wraps the scrypt.Key
function as thinly as possible, generates a
crytographically secure salt for you using Go's crypto/rand
package, and
returns the derived key with the parameters prepended:
package main
import(
"fmt"
"log"
"github.com/elithrar/simple-scrypt"
)
func main() {
passwordFromForm := "prew8fid9hick6c"
hash, err := scrypt.GenerateFromPassword([]byte(passwordFromForm), scrypt.DefaultParams)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", hash)
err := scrypt.CompareHashAndPassword(hash, []byte(passwordFromForm))
if err != nil {
log.Fatal(err)
}
}
Upgrading Parameters
Upgrading derived keys from a set of parameters to a "stronger" set of parameters
as hardware improves, or as you scale (and move your auth process to separate
hardware), can be pretty useful. Here's how to do it with simple-scrypt:
func main() {
current, err := scrypt.Cost(hash)
if err != nil {
log.Fatal(err)
}
slower := scrypt.Params{
N: 32768,
R: 8,
P: 2,
SaltLen: 16,
DKLen: 32,
}
if !reflect.DeepEqual(current, slower) {
}
}
Automatically Determining Parameters
Thanks to the work by tgulacsi, you can have simple-scrypt
automatically determine the optimal parameters for you (time vs. memory). You should run this once
on program startup, as calibrating parameters can be an expensive operation.
var params scrypt.Params
func main() {
var err error
params, err = scrypt.Calibrate(500*time.Millisecond, 64, Params{})
if err != nil {
return nil, err
}
...
}
func RegisterUserHandler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
email := r.PostFormValue("email")
pass := r.PostFormValue("password")
hash, err := scrypt.GenerateFromPassword([]byte(pass), params)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
Be aware that increasing these, whilst making it harder to brute-force the resulting hash, also
increases the risk of a denial-of-service attack against your server. A surge in authenticate
attempts (even if legitimate!) could consume all available resources.
License
MIT Licensed. See LICENSE file for details.