• Stars
    star
    195
  • Rank 199,374 (Top 4 %)
  • Language
    Go
  • License
    MIT License
  • Created over 9 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A convenience library for generating, comparing and inspecting password hashes using the scrypt KDF in Go ๐Ÿ”‘

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.

More Repositories

1

admission-control

A helpful micro-framework for writing Kubernetes Admission Controllers ๐Ÿ”Ž๐ŸŽŸ
Go
173
star
2

workers-hono-rate-limit

Hono-compatible middleware for rate limiting requests with Cloudflare Workers.
TypeScript
65
star
3

dotfiles

dotfiles for my macOS & Linux environments โŒจ๏ธ
Shell
47
star
4

vgo-docker-example

An example of how to use vgo (https://research.swtch.com/vgo-tour) + Docker together.
Go
45
star
5

http-api-d1-example

An example HTTP API for Cloudflare's D1 database: https://developers.cloudflare.com/d1/
TypeScript
26
star
6

centiment

๐Ÿค– Crypto-currency sentiment analysis via Google Natural Language & Twitter.
Go
26
star
7

svelte-demo-d1

Cloudflare Pages + SvelteKit + Cloudflare D1 - https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site/#deploy-with-cloudflare-pages
JavaScript
13
star
8

elithrar.github.io

GitHub Pages repo for my technical blog.
CSS
6
star
9

node-typescript-starter

A simple starting point for writing TypeScript on Node.js.
TypeScript
6
star
10

station

station provides useful static file serving and HTTP caching handlers & middleware for Go.
Go
5
star
11

pinecone-workers-demo

Use Cloudflare Workers (https://workers.dev) to query a Pinecone vector database (https://www.pinecone.io/)
TypeScript
5
star
12

pubsub-to-r2

An example Worker for copying Cloudflare Pub/Sub messages to R2 storage
TypeScript
4
star
13

goji-logger

NOTE: This is a test release. Expect it to change to `goji/logger` soon! Package goji/logger provides request logging and request ID generation for Goji (https://goji.io/), a HTTP multiplexer written in Go.
Go
4
star
14

supervisord

A supervisord Docker image that logs to stdout, imports configs from child images, and can be inspected via supervisorctl.
3
star
15

pubsub-example-worker

An example Worker that acts as an on-publish hook for Pub/Sub (https://developers.cloudflare.com/pub-sub/).
TypeScript
2
star
16

remix-plus-d1

An example app for combining Cloudflare's D1 database with Remix loader functions.
TypeScript
1
star
17

qdrant-workers-demo

Query vectors in Qdrant Cloud (https://qdrant.tech/) from Cloudflare Workers (https://workers.dev/)
TypeScript
1
star
18

crowbot

A Discord bot that returns the current & upcoming Crowfall playtest dates ๐Ÿค–
TypeScript
1
star
19

finding-bugs-with-bigquery

A talk on using BigQuery, the GitHub Public Data & some elbow grease to find bugs in OSS projects.
1
star
20

llm-cloudflare

An LLM CLI plugin for Cloudflare Workers AI models.
Python
1
star