hashy

package module
v0.0.0-...-7789ec0 Latest Latest
Warning

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

Go to latest
Published: May 11, 2022 License: NCSA Imports: 14 Imported by: 0

README

Hashy: Support for passlib (Python) and PHPass (PHP) password hashes.

...and several PHC-compatible hashes.

(It also makes life easier for argon2(id) users.)

Why?

I needed to support PHPass hashes for conversion to a Golang-based content platform, but I also had a need to import (and support) a variety of other hashes including some from passlib. Hashy was built from earlier efforts to support PHPass. It naturally evolved from this state into supporting more hashes.

Limitations

Hashy doesn't support all password hashes and it may not support all PHC-formatted hashes either. Commonly encountered hashes should work if they're widly supported (anything using bcrypt or some dervative thereof using SHA/SHA256) while others may or may not be functional. If something doesn't work, please file a ticket containing the plain text value and the associated hash. Additional information may be required such as the library that generated the hash and any additional hash options (round counts, for instance).

Hashy is intended to support a wide array of KDFs and other hashes but at present only bcrypt and argon2(id) have support for password hash generation. Regardless, you probably shouldn't export any other hash types if you can help it.

If Hashy exports a hashed password using identical options to those used to generate a passlib hash, it normalizes the output hash such that it's compatible with newer PHC formats. This may cause issues with cross-compatibility when using passlib. This needs testing.

It will almost certainly break when using older versions of passlib. Bear this in mind.

Usage

To use Hashy, simply:

go get git.destrealm.org/go/hashy

and include is similarly to:

package name

import (
    "git.destrealm.org"
)

Generating Hashes

Hashes can be generated easily via:


hasher := hashy.Hash("this is a password")

which will use Hashy's defaults to hash the password string. These defaults utilize argon2id with a time parameter of 2, memory usage of 100KiB, and $NUMCPU threads determined by the number of CPUs detected on your platform. Memory usage is much lower than recommended by the argon2 developers (64MiB is advised) in order to support memory- or performance-constrained environments such as web services with fairly substantial traffic.

Custom hashes and hash configurations can be passed in via the .New() package function:

// argon2i hash with 64MiB memory cost and a time cost of 1 (upstream defaults):
hasher := hashy.New(hashy.Argon2i, hashy.Options{
    TimeCost: 2,
    MemoryCost: 64 * 1024 * 1024,
})

Comparing Hashes

Hash comparison in Hashy is trivial and operates the same regardless of whether you're using Hashy-derived passwords or paswords derived from other sources (PHPass, passlib, et al):

phpassPassword := "$P$Bs66lOqqYxULxUV.N6DoqyDZA879yt/"

if hashy.Compare(phpassPassword, "this is a test") {
    // Matches; do something.
}

License

Hashy is licensed under the fairly liberal and highly permissive NCSA license. This license is preferred as it combines the best of the BSD and MIT licenses while providing coverage for associated documentation and other works that are not considered "original source code." Consequently, all Hashy documentation is likewise covered under the NCSA with which the source is distributed.

As with BSD-like and similar licenses attribute is, of course, required.

Copyright © 2019-2021 Benjamin A. Shelton.

See LICENSE.

Documentation

Index

Constants

View Source
const (
	DialectPHC dialect = iota + 1
	DialectCrypt
	DialectDjangoBCryptSHA256
	DialectPHPass
	DialectPHPassBB3
)

Variables

View Source
var AB64Encoding = base64.NewEncoding(AB64Table).WithPadding(base64.NoPadding)
View Source
var AB64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"
View Source
var B64Encoding = base64.NewEncoding(B64Table).WithPadding(base64.NoPadding)
View Source
var B64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
View Source
var BCryptEncoding = base64.NewEncoding(BCryptTable).WithPadding(base64.NoPadding)
View Source
var BCryptTable = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
View Source
var CryptEncoding = base64.NewEncoding(CryptTable).WithPadding(base64.NoPadding)
View Source
var CryptTable = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
View Source
var ErrConvertingParameterType = errors.NewError("unable to convert parameter type")
View Source
var ErrEmptyHash = errors.NewError("empty hash not allowed")
View Source
var ErrHashingPassword = errors.NewError("error hashing password")
View Source
var ErrInsufficientRandomBytes = errors.NewError("insufficient random bytes read")
View Source
var ErrParsingHash = errors.NewError("unable to parse hash")
View Source
var ErrPasswordEmpty = errors.NewError("empty passwords are not permitted")
View Source
var ErrPasswordSaltLength = errors.NewError("password salt length underrun")
View Source
var ErrReadingRandomBytes = errors.NewError("unable to read random bytes")
View Source
var Version string = "0.1.0"

Functions

func AB64Encode

func AB64Encode(src []byte) []byte

AB64Encode is a passlib- (Python) compatible base64 implementation using the alternative AB64Table alphabet with "+" replaced by ".".

func B64Encode

func B64Encode(src []byte) []byte

B64Encode is a passlib- (Python) compatible base64 implementation that does *not* write padding values to the end of the output.

func Compare

func Compare(phash, password string) bool

Compare the given password to the provided password hash string. This will automatically deduce the type of hash and perform a comparison accordingly.

func Hash

func Hash(password string) string

Hash the given password using library defaults. Out-of-the-box, this will use argon2id with a time parameter of 2, memory usage of 100KiB (64MiB is recommended), and NUMCPU threads. The lower memory value is selected for constrained environments.

If you require customization of the hashing algorthim(s), or its properties, please refer to New.

func Hash64Encode

func Hash64Encode(src []byte) []byte

Hash64Encode encodes incoming bytes using the base64 crypt()-style encoding variant found in PHPass.

This differs somewhat from a direct one-to-one port of PHPass in that a) a length argument is not accepted and b) allocations are handled internally before being returned (rather than concatenated or appended).

func IsWeak

func IsWeak(hash string) bool

IsWeak returns true if the specified hash is weak. Weak hashes are those that do not implement modern algorithms. Note that this does not (yet) examine parameters to determine if the hash is "weak."

This function can be used in conjunction with Compare() and Hash() when a weak hash is discovered. e.g., when migrating users from platforms that use weak hashes, once one is found, the password can be transparently re-hashed to a stronger algorithm.

func NewHashString

func NewHashString(hash, salt string) *phcString

NewHashString returns a new PHC-formatted hash generator using the provided hash and salt.

Use this function to generate a new PHC object, attach values to it (e.g. via Add()), and export it as a string via the String() method.

func Parse

func Parse(phash string) (*phcString, error)

Parse the PHC-formatted hash.

Types

type Algorithm

type Algorithm int
const (
	// Argon2id is the default algorithm used by hashy.
	Argon2id Algorithm = iota + 1
	Argon2i
	BCrypt
	BCryptCompat
	// BCryptSHA256 provides Python passlib compatibility. Supports v1
	// (SHA2-256) and v2 (HMAC-SHA2-256).
	BCryptSHA256
	// PHPass compatibility, such as that found in common use via WordPress.
	// phpBB3 uses a non-standard (of a non-standard) hash ID that deviates from
	// PHPass ("H" rather than "P"). We treat both the same.
	PHPass
	SCrypt

	DjangoBCryptSHA256
)

func (Algorithm) String

func (a Algorithm) String() string

type Argon2Hash

type Argon2Hash struct {
	// contains filtered or unexported fields
}

func NewArgon2IDHash

func NewArgon2IDHash(options Options) *Argon2Hash

func NewArgon2IHash

func NewArgon2IHash(options Options) *Argon2Hash

func (*Argon2Hash) Compare

func (h *Argon2Hash) Compare(phash, password string) (bool, error)

func (*Argon2Hash) Error

func (h *Argon2Hash) Error() error

func (*Argon2Hash) Hash

func (h *Argon2Hash) Hash(password string) (*phcString, error)

type BCryptHash

type BCryptHash struct {
	// contains filtered or unexported fields
}

func NewBCryptHash

func NewBCryptHash(options Options) *BCryptHash

func (*BCryptHash) Compare

func (h *BCryptHash) Compare(phash, password string) (bool, error)

func (*BCryptHash) Error

func (h *BCryptHash) Error() error

func (*BCryptHash) Hash

func (h *BCryptHash) Hash(password string) (*phcString, error)

type BCryptSHA256Hash

type BCryptSHA256Hash struct {
	// contains filtered or unexported fields
}

func NewBCryptSHA256Hash

func NewBCryptSHA256Hash(options Options) *BCryptSHA256Hash

func (*BCryptSHA256Hash) Compare

func (h *BCryptSHA256Hash) Compare(phash, password string) (bool, error)

func (*BCryptSHA256Hash) Error

func (h *BCryptSHA256Hash) Error() error

func (*BCryptSHA256Hash) Hash

func (h *BCryptSHA256Hash) Hash(password string) (*phcString, error)

type Decoder

type Decoder func([]byte) []byte

func B64Decoder

func B64Decoder(alphabet string) Decoder

B64Decoder returns a custom Decoder utilizing the specified alphabet. The Decoder returned will accept a hash string as a byte array and return its decoded byte-level representation.

type Dialect

type Dialect interface {
	Dialect() dialect
	String() string
}

type Encoder

type Encoder func([]byte) []byte

func B64Encoder

func B64Encoder(alphabet string) Encoder

B64Encoder returns a custom Encoder utilizing the specified alphabet. The Encoder returned will accept a byte array and return its hash as a byte array.

func Hash64Encoder

func Hash64Encoder(alphabet string) Encoder

type Hasher

type Hasher interface {
	// Compares the hash with the plain text password returning true if they
	// match.
	Compare(hash, password string) (bool, error)

	// Error returns the current error state, if any, of the hasher.
	Error() error
	// Hash the specified password returning an appropriately-encoded hash. If
	// an error occurs during the hashing process, this should return the string
	// "*" and client code should examine the output of Error().
	Hash(string) (*phcString, error)
}

type Hashy

type Hashy struct {
	Hasher Hasher
	// contains filtered or unexported fields
}

Hashy is the primary data type around which Hashy's public API is constructed. When configuring a new hash, with options, this is the type that is returned.

func New

func New(algo Algorithm, options Options) *Hashy

New returns a new Hashy instance configured with a Hasher using the specified Options.

func (*Hashy) Compare

func (h *Hashy) Compare(hash, password string) bool

Compare the given hash and password.

func (*Hashy) Hash

func (h *Hashy) Hash(password string) string

Hash the provided password and return the generated hash as a string.

func (*Hashy) IsWeak

func (h *Hashy) IsWeak(hash string) bool

IsWeak returns true if the specified hash is weak. Weak hashes are those that do not implement modern algorithms. Note that this does not (yet) examine parameters to determine if the hash is "weak."

This function can be used in conjunction with Compare() and Hash() when a weak hash is discovered. e.g., when migrating users from platforms that use weak hashes, once one is found, the password can be transparently re-hashed to a stronger algorithm.

type Options

type Options struct {
	// SaltLength for hashes that support variable-length salts. Default values
	// are algorithm-dependent.
	SaltLength int

	// TimeCost for argon2. If unset this will default to 2. Recommended default
	// from upstream: 1 (when using 64MiB for MemoryCost).
	TimeCost int

	// MemoryCost for argon2. If unset this will default to 100*1024 (100KiB).
	// Recommended default from upstream: 64*1024*1024 (64MiB).
	MemoryCost int

	// Threads (CPU count) for argon2. If set to 0, this will use
	// runtime.NumCPU.
	Threads int

	// KeyLength for argon2. This controls the length of the generated hash. If
	// set to 0 this will default to 64.
	KeyLength int

	// CostFactor for bcrypt. If unset this will default to 12.
	CostFactor int

	// RoundCount for PHPass hashes. Defaults to 512 if unset.
	RoundCount int
}

type PHPassHash

type PHPassHash struct {
	// contains filtered or unexported fields
}

func NewPHPassHash

func NewPHPassHash(options Options) *PHPassHash

func (*PHPassHash) Compare

func (h *PHPassHash) Compare(phash, password string) (bool, error)

func (*PHPassHash) Error

func (h *PHPassHash) Error() error

func (*PHPassHash) Hash

func (h *PHPassHash) Hash(password string) (*phcString, error)

Directories

Path Synopsis
Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing algorithm.
Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing algorithm.

Jump to

Keyboard shortcuts

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