password

package module
Version: v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 11, 2020 License: MIT Imports: 13 Imported by: 2

README

goSecretBoxPassword

Go Report Card GoDoc cover.run Build Status

This is a Golang library for securing passwords it is based on the Dropbox method for password storage. The both passphrases are first hashed with Blake2b-512 then a random 64-bit salt is generated and a secure hash is generated using Scrypt with the user specified parameters. The salt is appended to resulting 56 byte hash for a total of 64 bytes. The masterpassphrase Scrypt output, which Dropbox describes as a global pepper, is then hashed with Blake2b-256 and is used as a key along with a 192-bit random nonce value for the user passphrase Scrypt output along with Scrypt salt to be encrypted using NaCl Secretbox. NaCl Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate data.

All hashing and crypto is done by Go library packages. This is only a utility library to make the process described easier.

The primary changes from the Dropbox method are the use of Blake2b as both a password stretching and hashing method rather than SHA. This provides predictable and acceptable length input for both Scrypt and NaCl Secretbox rather than relying on users to provide sufficient length input. The other changes is the use of NaCl Secretbox using XSalsa20 for encryption and Poly1305 for authentication rather than AES-GCM.

The resulting string is not more than 225 characters in length and contains an identifier, version (used for master passphrase version), the ciphertext Scrypt base-64 value, masterkey Scrypt salt as base-64, and user passphrase scrypt parameters as integers, followed by master passphrase Scrypt parameters in the same format with sections separated by $.

You should not store the master passphrase with the password hashes. How you choose to store this value is up to you, but you should understand that losing the masterpassphrase will cause you to lose access to all passwords encrypted with this. According to the math, you should be able to use the same master passphrase for several quintillion passphrases without key exhaustion using XSalsa20 so you should feel free to use it for all users rather than attempting to rotate this. You can store the master passphrase and an environmental variable, CLI argument or come up with your own novel approach as long as you don't lose it.

Using this method, even if the user credential database were to be compromised the attacker would first need to break the Secretbox encryption before being able to then attempt to crack the Scrypt passphrase hashes which would still only reveal the Blake2b-512 hash of the passphrase. By tuning the Scrypt parameters you can make the password hash derivation more costly (more time and resource consuming). You will need to balance security and speed.

As of writing, secure recommendations for interactive logins are N=32768, r=8 and p=1. The parameters N, r, and p should be increased as memory latency and CPU parallelism increases; consider setting N to the highest power of 2 you can derive within an acceptable period of time. Because these parameters are combined to be stored with the output you should have no issue changing the settings without forcing users to update. There is also a version tag included to allow for future library updates without causing breaking changes for existing users.

The params are used for both the user passphrase and master passphrase hash, keep in mind that any settings is being done twice as thus Scrypt hashing params have twice the impact. I have considered creating an updated version where master or user passphrase hash is computed using a different algorithm or with different parameters to increase speed. This would not be a breaking change since there is a version included in all hash outputs.

Example Output:

secBoxv1$0$G0Ke6q1upuoC+fs+cPvK5swmF8WNNxc9cWHwyp5pZtL/Yc+KmjD1x/43mjy/ySWj4uAFc92LL5tKmvsTCedFMqyNJ8URdzJ1MgdqmCgMIkOXy87JacKdLnxjyIWjeeNnLVxiCWjXhrI=$8OPoOpUeIf0=$32768$16$1$16384$8$1

Usage

Latest from Github:

go get github.com/dwin/goSecretBoxPassword
import "github.com/dwin/goSecretBoxPassword"
Future Plans
  • Helper function for updating master passphrase without modifying user passphrase
  • Allow seamless change of master passphrase using version code
  • Allow disable of Scrypt, use of different parameters or algorithm for master passphrase hash to increase speed or security
Example
package main

import (
    "fmt"

    password "github.com/dwin/goSecretBoxPassword"
)

// This should be from config file or environemnt variables rather than your source
var mastPw = "masterpassword" 

func main() {
    userPw := "userpassword"
    // Hash and encrypt passphrase
    pwHash, err := password.Hash(userPw, mastPw, 0, password.ScryptParams{N: 32768, R: 16, P: 1}, password.DefaultParams)
    if err != nil {
        fmt.Println("Hash fail. ", err)
    }
   // Store pwHash to Database ->

    // -------- Verify -------------
    // Get pwHash from database <- and compare to user input using same
    // masterpassword stored hash was created with.
    // Verify will return nil unless password does not match or other error occurs
    err = password.Verify(userPw, mastPw, pwHash)
    if err != nil {
        fmt.Println("Verify fail. ", err)
    }
    fmt.Println("Success")
}

Documentation

Overview

Package password is a probably paranoid utility library for securly hashing and encrypting passwords based on the Dropbox method. This implementation uses Blake2b, Scrypt and XSalsa20-Poly1305 (via NaCl SecretBox) to create secure password hashes that are also encrypted using a master passphrase. If the master passphrase is lost you will lose access to all passwords encrypted with it so store is securely, my recommendation is that you store it as an environmental variable or in a config file to avoid storing it in source code.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrCiphertextVer indicates version sub-string mismatch normally; ex. "secBoxv1"
	ErrCiphertextVer = errors.New("Nonmatched ciphertext version")
	// ErrCiphertextFormat indicates input is not in expected format
	ErrCiphertextFormat = errors.New("Ciphertext input format not as expected")
	// ErrInvalidVersionUpdate indicates new version given not oldVersion + 1 or greater
	ErrInvalidVersionUpdate = errors.New("Invalid new version int, new master passphrase version must be greater than previous")
	// ErrPassphraseHashMismatch indicates invalid passphrase for supplied ciphertext
	ErrPassphraseHashMismatch = errors.New("Passphrase hash does not match supplied ciphertext")
	// ErrPassphraseLength indicates supplied passphrase is not at least MinLength
	ErrPassphraseLength = errors.New("Passphrase must be at least MinLength")
	// ErrSecretBoxDecryptFail indicates SecretBox decryption could not be completed
	ErrSecretBoxDecryptFail = errors.New("SecretBox decryption failed")
	// ErrScryptParamN indicates ScryptParams:N out of acceptable range
	ErrScryptParamN = errors.New("Given Scrypt (N) cost factor out of acceptable range")
	// ErrScryptParamR indicates ScryptParams:r out of acceptable range
	ErrScryptParamR = errors.New("Given Scrypt (r) cost factor out of acceptable range")
	// ErrScryptParamP indicates ScryptParams:p out of acceptable range
	ErrScryptParamP = errors.New("Given Scrypt (p) cost factor out of acceptable range")
)
View Source
var (
	// MinLength changes the minimum passphrase and master passphrase length accepted
	MinLength = 8
	// DefaultParams defines Scrypt Parameters
	DefaultParams = ScryptParams{N: 16384, R: 8, P: 1}
)

Functions

func Benchmark

func Benchmark(params ScryptParams) (seconds float64, err error)

Benchmark takes ScryptParams and returns the number of seconds elapsed as a float64 and error

func GetHashVersion

func GetHashVersion(ciphertext string) (version int, err error)

GetHashVersion takes ciphertext string and returns goSecretBoxPassword version as int and error.

func GetMasterVersion

func GetMasterVersion(ciphertext string) (version int, err error)

GetMasterVersion takes ciphertext string and returns master passphrase version as int and error.

func Hash

func Hash(userpass, masterpass string, version int, userparams, masterparams ScryptParams) (pwHashOut string, err error)

Hash takes passphrase ,masterpassphrase as strings, version indicator as int, and userparams and masterparams as ScryptParams and returns up to 225 char ciphertext string and error - ex. password.Hash("password1234", "masterpassphrase", 0, ScryptParams{N: 32768, R: 16, P: 1}, DefaultParams)

func UpdateMaster

func UpdateMaster(newMaster, oldMaster string, newVersion int, ciphertext string, masterparams ScryptParams) (pwHashOut string, err error)

UpdateMaster takes new master passphrase, old master passphrase as string, new version as int, cipertext as string, and new ScryptParams. It returns and updated hash output string and error.

func Verify

func Verify(userpass, masterpass, ciphertext string) error

Verify takes passphrase, masterpassphrase and ciphertext as strings and returns error if verification fails, else returns nil upon success

Types

type ScryptParams

type ScryptParams struct {
	N int
	R int
	P int
}

ScryptParams sets the Scrypt devivation parameters used for hashing

func GetParams

func GetParams(ciphertext string) (userParams, masterParams ScryptParams, err error)

GetParams takes ciphertext string, returns user and master parameters and error. This may be useful for upgrading.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL