test(groupware): add testcontainers based jmap test

* adds pkg/jmap/jmap_integration_test.go

 * uses ghcr.io/stalwartlabs/stalwart:v0.13.2-alpine

 * can be disabled by setting one of the following environment
   variables, in the same fashion as ca0493b28
   - CI=woodpecker
   - CI_SYSTEM_NAME=woodpecker
   - USE_TESTCONTAINERS=false

 * dependencies:
   - bump github.com/go-test/deep from 1.1.0 to 1.1.1
   - add github.com/cention-sany/utf7
   - add github.com/dustinkirkland/golang-petname
   - add github.com/emersion/go-imap/v2
   - add github.com/emersion/go-message
   - add github.com/emersion/go-sasl
   - add github.com/go-crypt/crypt
   - add github.com/go-crypt/x
   - add github.com/gogs/chardet
   - add github.com/inbucket/html2text
   - add github.com/jhilleryerd/enmime/v2
   - add github.com/ssor/bom
   - add gopkg.in/loremipsum.v1
This commit is contained in:
Pascal Bleser
2025-09-04 22:48:05 +02:00
parent 3813d14cae
commit b171609376
204 changed files with 24589 additions and 5 deletions

26
vendor/github.com/go-crypt/crypt/algorithm/const.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package algorithm
const (
// DigestSHA1 is te name for SHA1 digests.
DigestSHA1 = "sha1"
// DigestSHA224 is te name for SHA224 digests.
DigestSHA224 = "sha224"
// DigestSHA256 is te name for SHA256 digests.
DigestSHA256 = "sha256"
// DigestSHA384 is te name for SHA384 digests.
DigestSHA384 = "sha384"
// DigestSHA512 is te name for SHA512 digests.
DigestSHA512 = "sha512"
)
const (
// SaltLengthDefault is the default salt size for most implementations.
SaltLengthDefault = 16
// KeyLengthDefault is the default key size for most implementations.
KeyLengthDefault = 32
)

3
vendor/github.com/go-crypt/crypt/algorithm/doc.go generated vendored Normal file
View File

@@ -0,0 +1,3 @@
// Package algorithm is a package which contains the individual algorithms and interfaces related to their
// implementation.
package algorithm

66
vendor/github.com/go-crypt/crypt/algorithm/errors.go generated vendored Normal file
View File

@@ -0,0 +1,66 @@
package algorithm
import (
"errors"
)
var (
// ErrEncodedHashInvalidFormat is an error returned when an encoded hash has an invalid format.
ErrEncodedHashInvalidFormat = errors.New("provided encoded hash has an invalid format")
// ErrEncodedHashInvalidIdentifier is an error returned when an encoded hash has an invalid identifier for the
// given digest.
ErrEncodedHashInvalidIdentifier = errors.New("provided encoded hash has an invalid identifier")
// ErrEncodedHashInvalidVersion is an error returned when an encoded hash has an unsupported or otherwise invalid
// version.
ErrEncodedHashInvalidVersion = errors.New("provided encoded hash has an invalid version")
// ErrEncodedHashInvalidOption is an error returned when an encoded hash has an unsupported or otherwise invalid
// option in the option field.
ErrEncodedHashInvalidOption = errors.New("provided encoded hash has an invalid option")
// ErrEncodedHashInvalidOptionKey is an error returned when an encoded hash has an unknown or otherwise invalid
// option key in the option field.
ErrEncodedHashInvalidOptionKey = errors.New("provided encoded hash has an invalid option key")
// ErrEncodedHashInvalidOptionValue is an error returned when an encoded hash has an unknown or otherwise invalid
// option value in the option field.
ErrEncodedHashInvalidOptionValue = errors.New("provided encoded hash has an invalid option value")
// ErrEncodedHashKeyEncoding is an error returned when an encoded hash has a salt with an invalid or unsupported
// encoding.
ErrEncodedHashKeyEncoding = errors.New("provided encoded hash has a key value that can't be decoded")
// ErrEncodedHashSaltEncoding is an error returned when an encoded hash has a salt with an invalid or unsupported
// encoding.
ErrEncodedHashSaltEncoding = errors.New("provided encoded hash has a salt value that can't be decoded")
// ErrKeyDerivation is returned when a Key function returns an error.
ErrKeyDerivation = errors.New("failed to derive the key with the provided parameters")
// ErrSaltEncoding is an error returned when a salt has an invalid or unsupported encoding.
ErrSaltEncoding = errors.New("provided salt has a value that can't be decoded")
// ErrPasswordInvalid is an error returned when a password has an invalid or unsupported properties. It is NOT
// returned on password mismatches.
ErrPasswordInvalid = errors.New("password is invalid")
// ErrSaltInvalid is an error returned when a salt has an invalid or unsupported properties.
ErrSaltInvalid = errors.New("salt is invalid")
// ErrSaltReadRandomBytes is an error returned when generating the random bytes for salt resulted in an error.
ErrSaltReadRandomBytes = errors.New("could not read random bytes for salt")
// ErrParameterInvalid is an error returned when a parameter has an invalid value.
ErrParameterInvalid = errors.New("parameter is invalid")
)
// Error format strings.
const (
ErrFmtInvalidIntParameter = "%w: parameter '%s' must be between %d%s and %d but is set to '%d'"
ErrFmtDigestDecode = "%s decode error: %w"
ErrFmtDigestMatch = "%s match error: %w"
ErrFmtHasherHash = "%s hashing error: %w"
ErrFmtHasherValidation = "%s validation error: %w"
)

View File

@@ -0,0 +1,46 @@
package shacrypt
const (
// EncodingFmt is the encoding format for this algorithm.
EncodingFmt = "$%s$rounds=%d$%s$%s"
// EncodingFmtRoundsOmitted is the encoding format for this algorithm when the rounds can be omitted.
EncodingFmtRoundsOmitted = "$%s$%s$%s"
// AlgName is the name for this algorithm.
AlgName = "shacrypt"
// AlgIdentifierSHA256 is the identifier used in encoded SHA256 variants of this algorithm.
AlgIdentifierSHA256 = "5"
// AlgIdentifierSHA512 is the identifier used in encoded SHA512 variants of this algorithm.
AlgIdentifierSHA512 = "6"
// IterationsMin is the minimum number of iterations accepted.
IterationsMin = 1000
// IterationsMax is the maximum number of iterations accepted.
IterationsMax = 999999999
// IterationsDefaultSHA256 is the default number of iterations for SHA256.
IterationsDefaultSHA256 = 1000000
// IterationsDefaultSHA512 is the default number of iterations for SHA512.
IterationsDefaultSHA512 = 500000
// IterationsDefaultOmitted is the default number of iterations when the rounds are omitted.
IterationsDefaultOmitted = 5000
// SaltLengthMin is the minimum salt length.
SaltLengthMin = 1
// SaltLengthMax is the maximum salt length.
SaltLengthMax = 16
// SaltCharSet are the valid characters for the salt.
SaltCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"
)
const (
variantDefault = VariantSHA512
)

View File

@@ -0,0 +1,136 @@
package shacrypt
import (
"fmt"
"strconv"
"github.com/go-crypt/crypt/algorithm"
"github.com/go-crypt/crypt/internal/encoding"
)
// RegisterDecoder the decoder with the algorithm.DecoderRegister.
func RegisterDecoder(r algorithm.DecoderRegister) (err error) {
if err = RegisterDecoderSHA256(r); err != nil {
return err
}
if err = RegisterDecoderSHA512(r); err != nil {
return err
}
return nil
}
// RegisterDecoderSHA256 registers specifically the sha256 decoder variant with the algorithm.DecoderRegister.
func RegisterDecoderSHA256(r algorithm.DecoderRegister) (err error) {
if err = r.RegisterDecodeFunc(VariantSHA256.Prefix(), DecodeVariant(VariantSHA256)); err != nil {
return err
}
return nil
}
// RegisterDecoderSHA512 registers specifically the sha512 decoder variant with the algorithm.DecoderRegister.
func RegisterDecoderSHA512(r algorithm.DecoderRegister) (err error) {
if err = r.RegisterDecodeFunc(VariantSHA512.Prefix(), DecodeVariant(VariantSHA512)); err != nil {
return err
}
return nil
}
// Decode the encoded digest into a algorithm.Digest.
func Decode(encodedDigest string) (digest algorithm.Digest, err error) {
return DecodeVariant(VariantNone)(encodedDigest)
}
// DecodeVariant the encoded digest into a algorithm.Digest provided it matches the provided Variant. If VariantNone is
// used all variants can be decoded.
func DecodeVariant(v Variant) func(encodedDigest string) (digest algorithm.Digest, err error) {
return func(encodedDigest string) (digest algorithm.Digest, err error) {
var (
parts []string
variant Variant
)
if variant, parts, err = decoderParts(encodedDigest); err != nil {
return nil, fmt.Errorf(algorithm.ErrFmtDigestDecode, AlgName, err)
}
if v != VariantNone && v != variant {
return nil, fmt.Errorf(algorithm.ErrFmtDigestDecode, AlgName, fmt.Errorf("the '%s' variant cannot be decoded only the '%s' variant can be", variant.String(), v.String()))
}
if digest, err = decode(variant, parts); err != nil {
return nil, fmt.Errorf(algorithm.ErrFmtDigestDecode, AlgName, err)
}
return digest, nil
}
}
func decoderParts(encodedDigest string) (variant Variant, parts []string, err error) {
parts = encoding.Split(encodedDigest, -1)
if n := len(parts); n != 4 && n != 5 {
return VariantNone, nil, algorithm.ErrEncodedHashInvalidFormat
}
variant = NewVariant(parts[1])
if variant == VariantNone {
return variant, nil, fmt.Errorf("%w: identifier '%s' is not an encoded %s digest", algorithm.ErrEncodedHashInvalidIdentifier, parts[1], AlgName)
}
return variant, parts[2:], nil
}
func decode(variant Variant, parts []string) (digest algorithm.Digest, err error) {
decoded := &Digest{
variant: variant,
}
var (
ip, is, ik int
)
switch len(parts) {
case 2:
ip, is, ik = -1, 0, 1
case 3:
ip, is, ik = 0, 1, 2
}
if len(parts[ik]) == 0 {
return nil, fmt.Errorf("%w: key has 0 bytes", algorithm.ErrEncodedHashKeyEncoding)
}
decoded.iterations = IterationsDefaultOmitted
var params []encoding.Parameter
if ip >= 0 {
if params, err = encoding.DecodeParameterStr(parts[ip]); err != nil {
return nil, err
}
}
for _, param := range params {
switch param.Key {
case "rounds":
var rounds uint64
if rounds, err = strconv.ParseUint(param.Value, 10, 32); err != nil {
return nil, fmt.Errorf("%w: option '%s' has invalid value '%s': %v", algorithm.ErrEncodedHashInvalidOptionValue, param.Key, param.Value, err)
}
decoded.iterations = int(rounds)
default:
return nil, fmt.Errorf("%w: option '%s' with value '%s' is unknown", algorithm.ErrEncodedHashInvalidOptionKey, param.Key, param.Value)
}
}
decoded.salt, decoded.key = []byte(parts[is]), []byte(parts[ik])
return decoded, nil
}

View File

@@ -0,0 +1,83 @@
package shacrypt
import (
"crypto/subtle"
"fmt"
"strings"
xcrypt "github.com/go-crypt/x/crypt"
"github.com/go-crypt/crypt/algorithm"
)
// Digest is a digest which handles SHA-crypt hashes like SHA256 or SHA512.
type Digest struct {
variant Variant
iterations int
salt, key []byte
}
// Match returns true if the string password matches the current shacrypt.Digest.
func (d *Digest) Match(password string) (match bool) {
return d.MatchBytes([]byte(password))
}
// MatchBytes returns true if the []byte passwordBytes matches the current shacrypt.Digest.
func (d *Digest) MatchBytes(passwordBytes []byte) (match bool) {
match, _ = d.MatchBytesAdvanced(passwordBytes)
return match
}
// MatchAdvanced is the same as Match except if there is an error it returns that as well.
func (d *Digest) MatchAdvanced(password string) (match bool, err error) {
if match, err = d.MatchBytesAdvanced([]byte(password)); err != nil {
return match, fmt.Errorf(algorithm.ErrFmtDigestMatch, AlgName, err)
}
return match, nil
}
// MatchBytesAdvanced is the same as MatchBytes except if there is an error it returns that as well.
func (d *Digest) MatchBytesAdvanced(passwordBytes []byte) (match bool, err error) {
if len(d.key) == 0 {
return false, fmt.Errorf("%w: key has 0 bytes", algorithm.ErrPasswordInvalid)
}
return subtle.ConstantTimeCompare(d.key, xcrypt.KeySHACrypt(d.variant.HashFunc(), passwordBytes, d.salt, d.iterations)) == 1, nil
}
// Encode this Digest as a string for storage.
func (d *Digest) Encode() (hash string) {
switch d.iterations {
case IterationsDefaultOmitted:
return strings.ReplaceAll(fmt.Sprintf(EncodingFmtRoundsOmitted,
d.variant.Prefix(),
d.salt, d.key,
), "\n", "")
default:
return strings.ReplaceAll(fmt.Sprintf(EncodingFmt,
d.variant.Prefix(), d.iterations,
d.salt, d.key,
), "\n", "")
}
}
// String returns the storable format of the shacrypt.Digest hash utilizing fmt.Sprintf and shacrypt.EncodingFmt.
func (d *Digest) String() string {
return d.Encode()
}
func (d *Digest) defaults() {
switch d.variant {
case VariantSHA256, VariantSHA512:
break
default:
d.variant = variantDefault
}
if d.iterations == 0 {
d.iterations = d.variant.DefaultIterations()
}
}

View File

@@ -0,0 +1,7 @@
// Package shacrypt provides helpful abstractions for an implementation of SHA-crypt and implements
// github.com/go-crypt/crypt interfaces.
//
// See https://www.akkadia.org/drepper/SHA-crypt.html for specification details.
//
// This implementation is loaded by crypt.NewDefaultDecoder and crypt.NewDecoderAll.
package shacrypt

View File

@@ -0,0 +1,156 @@
package shacrypt
import (
"fmt"
xcrypt "github.com/go-crypt/x/crypt"
"github.com/go-crypt/crypt/algorithm"
"github.com/go-crypt/crypt/internal/random"
)
// New returns a *Hasher without any settings configured. This d to a SHA512 hash.Hash
// with 1000000 iterations. These settings can be overridden with the methods with the With prefix.
func New(opts ...Opt) (hasher *Hasher, err error) {
hasher = &Hasher{}
if err = hasher.WithOptions(opts...); err != nil {
return nil, err
}
if err = hasher.Validate(); err != nil {
return nil, err
}
return hasher, nil
}
// Hasher is a algorithm.Hash for SHA-crypt which can be initialized via shacrypt.New using a functional options pattern.
type Hasher struct {
variant Variant
iterations, bytesSalt int
d bool
}
// NewSHA256 returns a *Hasher with the SHA256 hash.Hash which d to 1000000 iterations. These
// settings can be overridden with the methods with the With prefix.
func NewSHA256() (hasher *Hasher, err error) {
return New(
WithVariant(VariantSHA256),
WithIterations(VariantSHA256.DefaultIterations()),
)
}
// NewSHA512 returns a *Hasher with the SHA512 hash.Hash which d to 1000000 iterations. These
// settings can be overridden with the methods with the With prefix.
func NewSHA512() (hasher *Hasher, err error) {
return New(
WithVariant(VariantSHA512),
WithIterations(VariantSHA512.DefaultIterations()),
)
}
// WithOptions defines the options for this scrypt.Hasher.
func (h *Hasher) WithOptions(opts ...Opt) (err error) {
for _, opt := range opts {
if err = opt(h); err != nil {
return err
}
}
return nil
}
// Hash performs the hashing operation and returns either a shacrypt.Digest as a algorithm.Digest or an error.
func (h *Hasher) Hash(password string) (digest algorithm.Digest, err error) {
h.defaults()
if digest, err = h.hash(password); err != nil {
return nil, fmt.Errorf(algorithm.ErrFmtHasherHash, AlgName, err)
}
return digest, nil
}
func (h *Hasher) hash(password string) (digest algorithm.Digest, err error) {
var salt []byte
if salt, err = random.CharSetBytes(h.bytesSalt, SaltCharSet); err != nil {
return nil, fmt.Errorf("%w: %v", algorithm.ErrSaltReadRandomBytes, err)
}
return h.hashWithSalt(password, salt)
}
// HashWithSalt overloads the Hash method allowing the user to provide a salt. It's recommended instead to configure the
// salt size and let this be a random value generated using crypto/rand.
func (h *Hasher) HashWithSalt(password string, salt []byte) (digest algorithm.Digest, err error) {
h.defaults()
if digest, err = h.hashWithSalt(password, salt); err != nil {
return nil, fmt.Errorf(algorithm.ErrFmtHasherHash, AlgName, err)
}
return digest, nil
}
func (h *Hasher) hashWithSalt(password string, salt []byte) (digest algorithm.Digest, err error) {
if s := len(salt); s > SaltLengthMax || s < SaltLengthMin {
return nil, fmt.Errorf("%w: salt bytes must have a length of between %d and %d but has a length of %d", algorithm.ErrSaltInvalid, SaltLengthMin, SaltLengthMax, len(salt))
}
d := &Digest{
variant: h.variant,
iterations: h.iterations,
salt: salt,
}
d.defaults()
d.key = xcrypt.KeySHACrypt(d.variant.HashFunc(), []byte(password), d.salt, d.iterations)
return d, nil
}
// MustHash overloads the Hash method and panics if the error is not nil. It's recommended if you use this option to
// utilize the Validate method first or handle the panic appropriately.
func (h *Hasher) MustHash(password string) (digest algorithm.Digest) {
var err error
if digest, err = h.Hash(password); err != nil {
panic(err)
}
return digest
}
// Validate checks the settings/parameters for this shacrypt.Hasher and returns an error.
func (h *Hasher) Validate() (err error) {
h.defaults()
if err = h.validate(); err != nil {
return fmt.Errorf(algorithm.ErrFmtHasherValidation, AlgName, err)
}
return nil
}
func (h *Hasher) validate() (err error) {
h.defaults()
return nil
}
func (h *Hasher) defaults() {
if h.d {
return
}
h.d = true
if h.bytesSalt < SaltLengthMin {
h.bytesSalt = algorithm.SaltLengthDefault
}
}

View File

@@ -0,0 +1,98 @@
package shacrypt
import (
"fmt"
"github.com/go-crypt/crypt/algorithm"
)
// Opt describes the functional option pattern for the shacrypt.Hasher.
type Opt func(h *Hasher) (err error)
// WithVariant configures the shacrypt.Variant of the resulting shacrypt.Digest.
// Default is shacrypt.VariantSHA512.
func WithVariant(variant Variant) Opt {
return func(h *Hasher) (err error) {
switch variant {
case VariantNone:
return nil
case VariantSHA256, VariantSHA512:
h.variant = variant
return nil
default:
return fmt.Errorf(algorithm.ErrFmtHasherValidation, AlgName, fmt.Errorf("%w: variant '%d' is invalid", algorithm.ErrParameterInvalid, variant))
}
}
}
// WithVariantName uses the variant name or identifier to configure the shacrypt.Variant of the resulting shacrypt.Digest.
// Default is shacrypt.VariantSHA512.
func WithVariantName(identifier string) Opt {
return func(h *Hasher) (err error) {
if identifier == "" {
return nil
}
variant := NewVariant(identifier)
if variant == VariantNone {
return fmt.Errorf(algorithm.ErrFmtHasherValidation, AlgName, fmt.Errorf("%w: variant identifier '%s' is invalid", algorithm.ErrParameterInvalid, identifier))
}
h.variant = variant
return nil
}
}
// WithSHA256 adjusts this Hasher to utilize the SHA256 hash.Hash.
func WithSHA256() Opt {
return func(h *Hasher) (err error) {
h.variant = VariantSHA256
return nil
}
}
// WithSHA512 adjusts this Hasher to utilize the SHA512 hash.Hash.
func WithSHA512() Opt {
return func(h *Hasher) (err error) {
h.variant = VariantSHA512
return nil
}
}
// WithIterations sets the iterations parameter of the resulting shacrypt.Digest.
// Minimum 1000, Maximum 999999999. Default is 1000000.
func WithIterations(iterations int) Opt {
return func(h *Hasher) (err error) {
if iterations < IterationsMin || iterations > IterationsMax {
return fmt.Errorf(algorithm.ErrFmtHasherValidation, AlgName, fmt.Errorf(algorithm.ErrFmtInvalidIntParameter, algorithm.ErrParameterInvalid, "iterations", IterationsMin, "", IterationsMax, iterations))
}
h.iterations = iterations
return nil
}
}
// WithRounds is an alias for shacrypt.WithIterations.
func WithRounds(rounds int) Opt {
return WithIterations(rounds)
}
// WithSaltLength adjusts the salt size (in bytes) of the resulting shacrypt.Digest.
// Minimum 1, Maximum 16. Default is 16.
func WithSaltLength(bytes int) Opt {
return func(h *Hasher) (err error) {
if bytes < SaltLengthMin || bytes > SaltLengthMax {
return fmt.Errorf(algorithm.ErrFmtHasherValidation, AlgName, fmt.Errorf(algorithm.ErrFmtInvalidIntParameter, algorithm.ErrParameterInvalid, "salt length", SaltLengthMin, "", SaltLengthMax, bytes))
}
h.bytesSalt = bytes
return nil
}
}

View File

@@ -0,0 +1,92 @@
package shacrypt
import (
"crypto/sha256"
"crypto/sha512"
"github.com/go-crypt/crypt/algorithm"
)
// NewVariant converts an identifier string to a shacrypt.Variant.
func NewVariant(identifier string) Variant {
switch identifier {
case AlgIdentifierSHA256, algorithm.DigestSHA256:
return VariantSHA256
case AlgIdentifierSHA512, algorithm.DigestSHA512:
return VariantSHA512
default:
return VariantSHA512
}
}
// Variant is a variant of the shacrypt.Digest.
type Variant int
const (
// VariantNone is a variant of the shacrypt.Digest which is unknown.
VariantNone Variant = iota
// VariantSHA256 is a variant of the shacrypt.Digest which uses SHA-256.
VariantSHA256
// VariantSHA512 is a variant of the shacrypt.Digest which uses SHA-512.
VariantSHA512
)
// String implements the fmt.Stringer returning a string representation of the shacrypt.Variant.
func (v Variant) String() (identifier string) {
switch v {
case VariantSHA256:
return algorithm.DigestSHA256
case VariantSHA512:
return algorithm.DigestSHA512
default:
return
}
}
// Prefix returns the shacrypt.Variant prefix identifier.
func (v Variant) Prefix() (prefix string) {
switch v {
case VariantSHA256:
return AlgIdentifierSHA256
case VariantSHA512:
return AlgIdentifierSHA512
default:
return AlgIdentifierSHA512
}
}
// Name returns the Variant name.
func (v Variant) Name() (s string) {
switch v {
case VariantSHA256:
return algorithm.DigestSHA256
case VariantSHA512:
return algorithm.DigestSHA512
default:
return algorithm.DigestSHA512
}
}
// HashFunc returns the internal HMAC HashFunc.
func (v Variant) HashFunc() algorithm.HashFunc {
switch v {
case VariantSHA256:
return sha256.New
case VariantSHA512:
return sha512.New
default:
return sha512.New
}
}
// DefaultIterations returns the default iterations for the particular variant.
func (v Variant) DefaultIterations() int {
switch v {
case VariantSHA512:
return IterationsDefaultSHA512
default:
return IterationsDefaultSHA256
}
}

62
vendor/github.com/go-crypt/crypt/algorithm/types.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package algorithm
import (
"fmt"
"hash"
)
// Hash is an interface which implements password hashing.
type Hash interface {
// Validate checks the hasher configuration to ensure it's valid. This should be used when the Hash is going to be
// reused and you should use it in conjunction with MustHash.
Validate() (err error)
// Hash performs the hashing operation on a password and resets any relevant parameters such as a manually set salt.
// It then returns a Digest and error.
Hash(password string) (hashed Digest, err error)
// HashWithSalt is an overload of Digest that also accepts a salt.
HashWithSalt(password string, salt []byte) (hashed Digest, err error)
// MustHash overloads the Hash method and panics if the error is not nil. It's recommended if you use this method to
// utilize the Validate method first or handle the panic appropriately.
MustHash(password string) (hashed Digest)
}
// Matcher is an interface used to match passwords.
type Matcher interface {
Match(password string) (match bool)
MatchBytes(passwordBytes []byte) (match bool)
MatchAdvanced(password string) (match bool, err error)
MatchBytesAdvanced(passwordBytes []byte) (match bool, err error)
}
// Digest represents a hashed password. It's implemented by all hashed password results so that when we pass a
// stored hash into its relevant type we can verify the password against the hash.
type Digest interface {
fmt.Stringer
Matcher
Encode() (hash string)
}
// DecodeFunc describes a function to decode an encoded digest into a algorithm.Digest.
type DecodeFunc func(encodedDigest string) (digest Digest, err error)
// DecoderRegister describes an implementation that allows registering DecodeFunc's.
type DecoderRegister interface {
RegisterDecodeFunc(prefix string, decoder DecodeFunc) (err error)
RegisterDecodePrefix(prefix, identifier string) (err error)
Decoder
}
// Decoder is a representation of a implementation that performs generic decoding. Currently this is just intended for
// use by implementers.
type Decoder interface {
Decode(encodedDigest string) (digest Digest, err error)
}
// HashFunc is a function which returns a hash.Hash.
type HashFunc func() hash.Hash