aboutsummaryrefslogtreecommitdiffstats
path: root/pwhash.go
blob: 2d7a70f0dcc3c29e4262f50d7d4c68ab3a1821b0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
	"crypto/pbkdf2"
	"crypto/sha256"
	"fmt"
	"os"

	"golang.org/x/term"
)

const defaultPBKDF2Iters = 35_000_000

//go:generate go tool stringer -type=pwHash -linecomment
type pwHash int8

const (
	pwHashInvalid            pwHash = iota
	pwHashPBKDF2_HMAC_SHA256        // PBKDF2-HMAC-SHA256
)

type hashMetadata struct {
	PasswordHashType pwHash
	Iterations       int32
	SaltSize         int8
}

func (h *hashMetadata) validate() error {
	if h.PasswordHashType != pwHashPBKDF2_HMAC_SHA256 {
		return fmt.Errorf("invalid hash type %q", h.PasswordHashType)
	}
	if h.Iterations <= 0 || h.Iterations > defaultPBKDF2Iters {
		return fmt.Errorf("too many iterations")
	}
	if h.SaltSize <= 0 || h.SaltSize > defaultSaltSize {
		return fmt.Errorf("salt size too long")
	}
	return nil
}

func (h *hashMetadata) hashPassword(password string, salt []byte) ([]byte, error) {
	return pbkdf2.Key(sha256.New, password, salt, int(h.Iterations), 32)
}

func termReadPassword() (string, error) {
	pw, err := term.ReadPassword(int(os.Stdin.Fd()))
	if err != nil {
		return "", err
	}
	return string(pw), nil
}