simple-scrypt

A convenience library for generating, comparing and inspecting password hashes using the scrypt KDF in Go.

Github星跟踪图

simple-scrypt

GoDoc Build Status

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() {
    // e.g. r.PostFormValue("password")
    passwordFromForm := "prew8fid9hick6c"

    // Generates a derived key of the form "N$r$p$salt$dk" where N, r and p are defined as per
    // Colin Percival's scrypt paper: http://www.tarsnap.com/scrypt/scrypt.pdf
    // scrypt.Defaults (N=16384, r=8, p=1) makes it easy to provide these parameters, and
    // (should you wish) provide your own values via the scrypt.Params type.
    hash, err := scrypt.GenerateFromPassword([]byte(passwordFromForm), scrypt.DefaultParams)
    if err != nil {
        log.Fatal(err)
    }

    // Print the derived key with its parameters prepended.
    fmt.Printf("%s\n", hash)

    // Uses the parameters from the existing derived key. Return an error if they don't match.
    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() {
    // SCENE: We've successfully authenticated a user, compared their submitted
    // (cleartext) password against the derived key stored in our database, and
    // now want to upgrade the parameters (more rounds, more parallelism) to
    // reflect some shiny new hardware we just purchased. As the user is logging
    // in, we can retrieve the parameters used to generate their key, and if
    // they don't match our "new" parameters, we can re-generate the key while
    // we still have the cleartext password in memory
    // (e.g. before the HTTP request ends).
    current, err := scrypt.Cost(hash)
    if err != nil {
        log.Fatal(err)
    }

    // Now to check them against our own Params struct (e.g. using reflect.DeepEquals)
    // and determine whether we want to generate a new key with our "upgraded" parameters.
    slower := scrypt.Params{
        N: 32768,
        R: 8,
        P: 2,
        SaltLen: 16,
        DKLen: 32,
    }

    if !reflect.DeepEqual(current, slower) {
        // Re-generate the key with the slower parameters
        // here using scrypt.GenerateFromPassword
    }
}

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
    // 500ms, 64MB of RAM per hash.
    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
    }

    // Make sure you validate: not empty, not too long, etc.
    email := r.PostFormValue("email")
    pass := r.PostFormValue("password")

    // Use our calibrated parameters
    hash, err := scrypt.GenerateFromPassword([]byte(pass), params)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Save to DB, etc.
}

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.

主要指标

概览
名称与所有者elithrar/simple-scrypt
主编程语言Go
编程语言Go (语言数: 1)
平台
许可证MIT License
所有者活动
创建于2015-04-14 06:52:21
推送于2021-04-12 20:33:15
最后一次提交2018-06-06 07:22:12
发布数5
最新版本名称v1.3.0 (发布于 )
第一版名称v0.1 (发布于 2015-04-14 15:04:01)
用户参与
星数197
关注者数7
派生数27
提交数24
已启用问题?
问题数6
打开的问题数1
拉请求数7
打开的拉请求数2
关闭的拉请求数5
项目设置
已启用Wiki?
已存档?
是复刻?
已锁定?
是镜像?
是私有?