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

Overview

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.

Issues
  • Tests and calibration fails on Linux/i386

    Tests and calibration fails on Linux/i386

    Hi,

    First: thanks for writing simple-scrypt! I'm using it for restic, a secure and fast backup program.

    A few days ago a user reported (https://github.com/restic/restic/issues/676) that restic fails with a mysterious error message:

    create key in backend at /tmp/foo failed: Calibrate: scrypt.Calibrate: scrypt: the parameters provided are invalid
    

    Building a 32 bit binary for restic and running it indeed produces invalid parameters, e.g. on my machine (rather recent Intel i5 CPU on a Thinkpad t460) Calibrate() returns the following values:

    {N:1073741824 R:8 P:1 SaltLen:16 DKLen:32}
    

    These parameters are (obviously) considered invalid.

    In addition I've discovered that the tests fail on 386: `` $ GOARCH=386 go test -v

    github.com/elithrar/simple-scrypt

    ./scrypt_test.go:26: constant 2147483648 overflows int ./scrypt_test.go:30: constant 2147483648 overflows int FAIL github.com/elithrar/simple-scrypt [build failed]

    Thanks!
    bug 
    opened by fd0 10
  • TestCalibrate fails sometimes

    TestCalibrate fails sometimes

    TestCalibrate seems to fail with "0. GenerateFromPassword was too fast" sometimes:

    https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=851723 https://people.debian.org/~sanvila/build-logs/golang-github-elithrar-simple-scrypt/

    help-wanted 
    opened by legrostdg 7
  • Calibrate with fixed r

    Calibrate with fixed r

    [edit by @elithrar]: Addresses https://github.com/elithrar/simple-scrypt/issues/12

    bug enhancement needs-review 
    opened by LouisChrist 5
  • Do not calibrate r

    Do not calibrate r

    r is not a memory parameter, it's a cache parameter. Scaling r scales both memory and CPU, just like N.

    So halving r has the exact same effect as halving N, except it makes scrypt less cache efficient on general purpose processors, artificially increasing is CPU requirements. ASICs would probably not pay that tax, though.

    I recommend just fixing r to 8, as cache line size is the same now as it was when the scrypt paper came out. If you want to increase CPU time without increasing memory, use p, as the Go implementation acts sequentially.

    opened by FiloSottile 4
  • Travis: Build and run tests for current Go versions

    Travis: Build and run tests for current Go versions

    The build error seen in PR #4 is caused by the race detector, which simply uses too much RAM. On my machine with 12GiB I was unable to complete the test TestGenerateFromPassword with race detector enabled, the OOM killer took care of the process.

    The commit in here updates the Go version used for Travis up to Go 1.7.

    Closes #4

    opened by fd0 3
  • Add GenerateFromPasswordWithLimit and Calibrate.

    Add GenerateFromPasswordWithLimit and Calibrate.

    Time and memory limit the derivation, and allow calibration - no cgo needed.

    opened by tgulacsi 2
  • Please tag a new release

    Please tag a new release

    Hi,

    we've recently switched to using dep and I've discovered that the last released version ("v1.1") is really old already and does not include my fix for i386 (#6).

    Can you please tag a new release? Would you mind switching to semantic versioning for that? I.e. tag the next release as 1.2.0?

    Thanks!

    build documentation 
    opened by fd0 2
  • Migrate to CircleCI

    Migrate to CircleCI

    • [x] Remove TravisCI
    • [x] Use Go Modules
    • [x] Add CircleCI config
    • [x] Update README

    This also removes testing for Go versions prior to 1.7.

    build 
    opened by elithrar 2
  • [ci] Update .travis.yml to build Go 1.6

    [ci] Update .travis.yml to build Go 1.6

    enhancement 
    opened by elithrar 1
  • [build] use golang/dep

    [build] use golang/dep

    build 
    opened by elithrar 1
  • deps: define as a Go Module

    deps: define as a Go Module

    build 
    opened by elithrar 0
  • build: add GitHub Actions

    build: add GitHub Actions

    Test GitHub Actions per https://github.com/mvdan/github-actions-golang

    • [ ] Lint on latest
    • [ ] gofmt on latest
    • [ ] Run on tip
    build 
    opened by elithrar 0
  • CircleCI: Create config.yml

    CircleCI: Create config.yml

    build 
    opened by elithrar 1
  • Go test fails on s390x arch

    Go test fails on s390x arch

    Any ideas?

    • go test -buildmode pie -compiler gc -ldflags '-extldflags '''-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '''' --- FAIL: TestCalibrate (22.60s) scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:32768, R:8, P:1, SaltLen:16, DKLen:32} took 390.533325ms () scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:65536, R:8, P:1, SaltLen:16, DKLen:32} took 566.445332ms () scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:32768, R:8, P:1, SaltLen:16, DKLen:32} took 224.123283ms () scrypt_test.go:145: 2. GenerateFromPassword was too fast (expected between 250ms and 750ms, got 224.123283ms) with scrypt.Params{N:32768, R:8, P:1, SaltLen:16, DKLen:32}. scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:32768, R:8, P:2, SaltLen:16, DKLen:32} took 501.654331ms () scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:16384, R:8, P:3, SaltLen:16, DKLen:32} took 437.232135ms () scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:8192, R:8, P:8, SaltLen:16, DKLen:32} took 484.281555ms () scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:4096, R:8, P:16, SaltLen:16, DKLen:32} took 424.747882ms () scrypt_test.go:140: GenerateFromPassword with scrypt.Params{N:512, R:8, P:146, SaltLen:16, DKLen:32} took 532.689005ms () FAIL exit status 1
    build 
    opened by copart 1
Releases(v1.3.0)
  • v1.3.0(Apr 2, 2018)

    A bug in Calibrate was fixed. This bug was based on a misinterpretation of the role of the "r" parameter in scrypt, which is tied to CPU cache lines as opposed to general memory. r is now fixed at a value of 8.

    CHANGELOG

    d150773 [bugfix] Calibrate with fixed r (#13)

    Source code(tar.gz)
    Source code(zip)
Owner
Matt Silverlock
Maintainer of the @gorilla web toolkit for Go. I care a lot about securing the web, making things fast for users, and good documentation.
Matt Silverlock
go seof: Simple Encrypted os.File

Encrypted implementation of golang' os.File. It handles WriteAt, Seek, Truncate, etc. Can deal with huge files, random access, etc.

Ed Riccardi 44 May 11, 2021
:key: Idiotproof golang password validation library inspired by Python's passlib

passlib for go 100% modules-free. Python's passlib is quite an amazing library. I'm not sure there's a password library in existence with more thought

Hugo Landau 245 Jul 15, 2021
Split and distribute your private keys securely amongst untrusted network

cocert An experimental tool for splitting and distributing your private keys safely* cocert, generates ECDSA - P521 key and uses a technique known as

Furkan Türkal 132 Jul 15, 2021
Accelerate aggregated MD5 hashing performance up to 8x for AVX512 and 4x for AVX2.

Accelerate aggregated MD5 hashing performance up to 8x for AVX512 and 4x for AVX2. Useful for server applications that need to compute many MD5 sums in parallel.

High Performance, Kubernetes Native Object Storage 78 May 17, 2021
sops is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, age, and PGP

sops is an editor of encrypted files that supports YAML, JSON, ENV, INI and BINARY formats and encrypts with AWS KMS, GCP KMS, Azure Key Vault, age, and PGP. (demo)

Mozilla 7.9k Jul 17, 2021
Secret - Encrypt anything with a password

Secret - Encrypt anything with a password Ever wanted to hide a file? Now you can do it really easily! Usage secret {-e/--encrypt | -d/--decrypt} <sou

Ishan Goel 30 Jul 16, 2021
Finds common flaws in passwords. Like cracklib, but written in Go.

crunchy Finds common flaws in passwords. Like cracklib, but written in Go. Detects: ErrEmpty: Empty passwords ErrTooShort: Too short passwords ErrNoDi

Christian Muehlhaeuser 368 Jul 18, 2021
The bare metal Go smart card

Authors Andrea Barisani [email protected] | [email protected] Introduction The GoKey application implements a USB smartcard in pure Go

F-Secure Foundry 106 Jul 13, 2021
SwissWallet is a deterministic cryptocurrency wallet generator heavily based on MindWallet and MemWallet

SwissWallet SwissWallet is a deterministic cryptocurrency wallet generator heavily based on MindWallet and MemWallet but using argon2 and scrypt by de

vortex.live 6 May 13, 2021
TLS/SSL Tunnel - A modern STunnel replacement written in golang

go-tunnel - Robust Quic/TLS Tunnel (Stunnel replacement) What is it? A supercharged Stunnel replacement written in golang. is in a sense a proxy enabl

Sudhi Herle 124 Jul 15, 2021
Simple Crypto API for Go

cryptogo Simple cryptography API in Go Hashes: hash.MD5Bytes, hash.MD5Base64, hash.MD5Hex calculates an MD5 chechsum of the input byte array as a byte

Basil Gorin 12 Jul 9, 2019
Merchant API reference implementation

mAPI More details available in the BRFC Spec for Merchant API. The old golang (v1.1) implementation is no longer being maintained and has been moved t

Bitcoin SV 11 Jun 24, 2021
Get any cryptocurrencies ticker and trade data in real time from multiple exchanges and then save it in multiple storage systems.

Cryptogalaxy is an app which will get any cryptocurrencies ticker and trade data in real time from multiple exchanges and then saves it in multiple storage systems.

Pavan Shetty 38 Jul 26, 2021
Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

Cossack Labs 1.3k Jul 23, 2021