argon2

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Apr 11, 2024 License: BSD-3-Clause Imports: 12 Imported by: 2

README

argon2 unofficial

CodeQL Build Linter Codecov Go Reference Go Report Card CodeFactor CodeBeat

Package argon2 is an improved version of the official Go argon2 hashing package.

Features missing from the official Go argon2 package:

  • NIST 800-63B recommends using a secret value of at least 112 bits. But as of now (golang.org/x/crypto v0.19.0), Key and IDkey functions pass nil value when calling deriveKey function.
  • Does not provide any straightforward way for password hashing and verification.

This package contains two additional functions KeyWithSecret and IDKeyWithSecret to provide an extra layer of security on top of the existing Key and IDKey functions by including an additional secret.

One of the standout features of this package is the seamless integration of a user-friendly wrapper (an improved version of github.com/alexedwards/argon2id). The wrapper encapsulates the complexities of the underlying argon2 package, offering a simplified and intuitive interface for developers across different projects.

Usage

package main

import (
	"fmt"

	"github.com/pilinux/argon2"
)

func main() {
	// Argon2i
	// create hash using argon2i and without any secret
	// $argon2i$v=19$m=65536,t=1,p=2$frbISZVHQ/ZgUpNA0SgdNQ$GuGB9vz9rTcJmDIebUFmVk0kyAX9xninyCp696PRdCA
	hash, err := argon2.CreateHash("pa$$word", "", argon2.DefaultParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(hash)

	// ComparePasswordAndHash performs a constant-time comparison between a
	// plain-text password and argon2id hash, using the parameters and salt
	// contained in the hash. It returns true if they match, otherwise it returns
	// false.
	match, err := argon2.ComparePasswordAndHash("pa$$word", "", hash)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Match: %v\n", match)

	// create hash using argon2i and with secret
	// $argon2i$v=19$m=65536,t=1,p=2$TfGuJ6SGvluWcQjsDdQWPQ$Seozoi/ZKJngavqpyZ5rs5lX5EKzJ2HSnMWJwVwJmVU
	hash, err = argon2.CreateHash("pa$$word", "12€45", argon2.DefaultParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(hash)

	match, err = argon2.ComparePasswordAndHash("pa$$word", "12€45", hash)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Match: %v\n", match)
	// --------------------------------------------------------------

	// Argon2id
	// create hash using argon2id and without any secret
	// $argon2id$v=19$m=65536,t=1,p=2$+Ni27r9ZIaEBs8mZn60Dlg$cxuw8quTcT5fIDqNIU27SinXyKiKQWFo/mfF4sogeKo
	idHash, err := argon2.IDCreateHash("pa$$word", "", argon2.DefaultParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(idHash)

	match, err = argon2.ComparePasswordAndHash("pa$$word", "", idHash)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Match: %v\n", match)

	// create hash using argon2id and with secret
	// $argon2id$v=19$m=65536,t=1,p=2$+haa0lKvImdlxrXCs0aJ/A$1h2lpPic8KQ7XckSdg+cE7LJX5kQ83BAZGNCBL6zmZI
	idHash, err = argon2.IDCreateHash("pa$$word", "12€45", argon2.DefaultParams)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(idHash)

	match, err = argon2.ComparePasswordAndHash("pa$$word", "12€45", idHash)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Match: %v\n", match)
}

Parameters

  • Memory — The amount of memory used by the Argon2 algorithm (in kibibytes).
  • Iterations — The number of iterations (or passes) over the memory.
  • Parallelism — The number of threads (or lanes) used by the algorithm.
  • Salt length — Length of the random salt. 16 bytes or more is recommended for password hashing.
  • Key length — Length of the generated key (or password hash). 16 bytes or more is recommended.
params := &argon2.Params{
		Memory:      128 * 1024,
		Iterations:  4,
		Parallelism: 4,
		SaltLength:  16,
		KeyLength:   32,
	}

hash, err := argon2.CreateHash("pa$$word", "", params)

For guidance, visit: rfc9106

Documentation

Overview

Package argon2 implements the key derivation function Argon2. Argon2 was selected as the winner of the Password Hashing Competition and can be used to derive cryptographic keys from passwords.

For a detailed specification of Argon2 see [1].

If you aren't sure which function you need, use Argon2id (IDKey) and the parameter recommendations for your scenario.

Argon2i

Argon2i (implemented by Key) is the side-channel resistant version of Argon2. It uses data-independent memory access, which is preferred for password hashing and password-based key derivation. Argon2i requires more passes over memory than Argon2id to protect from trade-off attacks. The recommended parameters (taken from [2]) for non-interactive operations are time=3 and to use the maximum available memory.

Argon2id

Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining Argon2i and Argon2d. It uses data-independent memory access for the first half of the first iteration over the memory and data-dependent memory access for the rest. Argon2id is side-channel resistant and provides better brute- force cost savings due to time-memory tradeoffs than Argon2i. The recommended parameters for non-interactive operations (taken from [2]) are time=1 and to use the maximum available memory.

[1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3

Index

Constants

View Source
const Version = 0x13

The Argon2 version implemented by this package.

Variables

View Source
var (
	// ErrInvalidHash is returned by ComparePasswordAndHash if the provided
	// hash isn't in the expected format.
	ErrInvalidHash = errors.New("argon2: hash is not in the correct format")

	// ErrIncompatibleVariant is returned by ComparePasswordAndHash if the
	// provided hash was created using a unsupported variant of Argon2.
	// Currently only argon2id is supported by this package.
	ErrIncompatibleVariant = errors.New("argon2: incompatible variant of argon2")

	// ErrIncompatibleVersion is returned by ComparePasswordAndHash if the
	// provided hash was created using a different version of Argon2.
	ErrIncompatibleVersion = errors.New("argon2: incompatible version of argon2")
)
View Source
var DefaultParams = &Params{
	Memory:      64 * 1024,
	Iterations:  1,
	Parallelism: 2,
	SaltLength:  16,
	KeyLength:   32,
}

DefaultParams provides some sane default parameters for hashing passwords.

Follows recommendations given by the Argon2 RFC: "The Argon2id variant with t=1 and maximum available memory is RECOMMENDED as a default setting for all environments. This setting is secure against side-channel attacks and maximizes adversarial costs on dedicated brute-force hardware."

The default parameters should generally be used for development/testing purposes only. Custom parameters should be set for production applications depending on available memory/CPU resources and business requirements.

Functions

func ComparePasswordAndHash

func ComparePasswordAndHash(password, secret, hash string) (match bool, err error)

ComparePasswordAndHash performs a constant-time comparison between a plain-text password and Argon2 hash, using the parameters, secret and salt contained in the hash. It returns true if they match, otherwise it returns false.

func CreateHash

func CreateHash(password, secret string, params *Params) (hash string, err error)

CreateHash generates an Argon2i password hash using the provided password, secret, and parameters. It returns the generated hash as a string and any error encountered.

The function takes the user's password, a secret for additional security, and a Params struct containing the parameters for key derivation. It generates a random salt and derives a key using the Argon2i key derivation function.

Parameters:

  • password: The user's password.
  • secret: An additional secret used for key derivation.
  • params: A Params struct containing key derivation parameters.

Returns:

  • hash: The generated Argon2i password hash.
  • err: Any error encountered during hash generation.

The returned hash follows the format used by the Argon2 reference C implementation and contains the base64-encoded Argon2i derived key prefixed by the salt and parameters. It looks like this:

$argon2i$v=19$m=65536,t=1,p=2$Ell6DALdx5M3PMaNxPsFyA$VTeuPaGQW621unpzV0zHKT8S4xRir8djGSY63vsYb7U

func IDCreateHash

func IDCreateHash(password, secret string, params *Params) (hash string, err error)

IDCreateHash generates an Argon2id password hash using the provided password, secret, and parameters. It returns the generated hash as a string and any error encountered.

The function takes the user's password, a secret for additional security, and a Params struct containing the parameters for key derivation. It generates a random salt and derives a key using the Argon2id key derivation function.

Parameters:

  • password: The user's password.
  • secret: An additional secret used for key derivation.
  • params: A Params struct containing key derivation parameters.

Returns:

  • hash: The generated Argon2id password hash.
  • err: Any error encountered during hash generation.

The returned hash follows the format used by the Argon2 reference C implementation and contains the base64-encoded Argon2id derived key prefixed by the salt and parameters. It looks like this:

$argon2id$v=19$m=65536,t=1,p=2$FmIYUI9SfLj+xHJJsM3JXw$DI8bBB2wHgOFwWVXXUSjmwRMeh/1pVVu5PDbsjoFtYE

func IDKey

func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte

IDKey derives a key from the password, salt, and cost parameters using Argon2id returning a byte slice of length keyLen that can be used as cryptographic key. The CPU cost and parallelism degree must be greater than zero.

For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:

key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)

The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number. If using that amount of memory (64 MB) is not possible in some contexts then the time parameter can be increased to compensate.

The time parameter specifies the number of passes over the memory and the memory parameter specifies the size of the memory in KiB. For example memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be adjusted to the numbers of available CPUs. The cost parameters should be increased as memory latency and CPU parallelism increases. Remember to get a good random salt.

func IDKeyWithSecret

func IDKeyWithSecret(password, salt, secret []byte, time, memory uint32, threads uint8, keyLen uint32) []byte

IDKeyWithSecret adds an extra layer of security on top of the existing IDKey function by including an additional secret.

func Key

func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte

Key derives a key from the password, salt, and cost parameters using Argon2i returning a byte slice of length keyLen that can be used as cryptographic key. The CPU cost and parallelism degree must be greater than zero.

For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:

key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)

The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number. If using that amount of memory (32 MB) is not possible in some contexts then the time parameter can be increased to compensate.

The time parameter specifies the number of passes over the memory and the memory parameter specifies the size of the memory in KiB. For example memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be adjusted to the number of available CPUs. The cost parameters should be increased as memory latency and CPU parallelism increases. Remember to get a good random salt.

func KeyWithSecret

func KeyWithSecret(password, salt, secret []byte, time, memory uint32, threads uint8, keyLen uint32) []byte

KeyWithSecret adds an extra layer of security on top of the existing Key function by including an additional secret.

Types

type Params

type Params struct {
	// The amount of memory used by the algorithm (in kibibytes).
	Memory uint32

	// The number of iterations over the memory.
	Iterations uint32

	// The number of threads (or lanes) used by the algorithm.
	// Recommended value is between 1 and runtime.NumCPU().
	Parallelism uint8

	// Length of the random salt. 16 bytes is recommended for password hashing.
	SaltLength uint32

	// Length of the generated key. 16 bytes or more is recommended.
	KeyLength uint32
}

Params describes the input parameters used by the Argon2id algorithm. The Memory and Iterations parameters control the computational cost of hashing the password. The higher these figures are, the greater the cost of generating the hash and the longer the runtime. It also follows that the greater the cost will be for any attacker trying to guess the password. If the code is running on a machine with multiple cores, then you can decrease the runtime without reducing the cost by increasing the Parallelism parameter. This controls the number of threads that the work is spread across. Important note: Changing the value of the Parallelism parameter changes the hash output.

For guidance and an outline process for choosing appropriate parameters see https://tools.ietf.org/html/draft-irtf-cfrg-argon2-04#section-4

func CheckHash

func CheckHash(password, secret, hash string) (match bool, params *Params, err error)

CheckHash is like ComparePasswordAndHash, except it also returns the params that the hash was created with. This can be useful if you want to update your hash params over time (which you should).

func DecodeHash

func DecodeHash(hash string) (argon2Variant int, params *Params, salt, key []byte, err error)

DecodeHash expects a hash created from this package, and parses it to return the params used to create it, as well as the variant of argon2, salt and key (password hash).

Jump to

Keyboard shortcuts

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