diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-09-26 09:12:27 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-09-26 09:12:27 -0700 |
| commit | 768d3629b938196d132b80864204d7b6a9683a9e (patch) | |
| tree | a147fbd27c911b6a1ec38c6492c02fd5f7b93456 | |
| parent | 6cb1031bcbea0c13fb49f9ae445641aed53d2e8e (diff) | |
| download | roseh.moe-768d3629b938196d132b80864204d7b6a9683a9e.tar.zst | |
Turn up all the parameters
Hopefully this makes it secure 😳
| -rw-r--r-- | internal/cryptoutil/cryptoutil.go | 54 | ||||
| -rw-r--r-- | tools/finditers/finditers.go | 25 |
2 files changed, 42 insertions, 37 deletions
diff --git a/internal/cryptoutil/cryptoutil.go b/internal/cryptoutil/cryptoutil.go index b58282b..4ed600d 100644 --- a/internal/cryptoutil/cryptoutil.go +++ b/internal/cryptoutil/cryptoutil.go @@ -9,24 +9,27 @@ import ( "crypto/hmac" "crypto/pbkdf2" "crypto/rand" - "crypto/sha256" + "crypto/sha512" "crypto/subtle" "encoding/hex" "errors" ) const ( - defaultIterations = 3749890 // from cmd/finditers + defaultIterations = 4718580 // from cmd/finditers - saltSize = 2 * sha256.Size - hashSize = 32 - certSize = sha256.Size - aesKeySize = 32 - nonceSize = aes.BlockSize + oneSaltSize = 64 + hashSize = 64 + certSize = sha512.Size + aesKeySize = 32 + nonceSize = aes.BlockSize ) +// SaltSize is the expected salt length for HashIter. +const SaltSize = 2 * oneSaltSize + // A PasswordHash must be PasswordHashSize bytes. -const PasswordHashSize = saltSize + hashSize +const PasswordHashSize = SaltSize + hashSize // A PasswordHash is derived from the user's password and can be passed to // CheckPassword to verify if two passwords match. A PasswordHash must be @@ -42,14 +45,14 @@ type RawKey struct { } // An HMACKey must be HMACKeySize bytes. -const HMACKeySize = sha256.Size +const HMACKeySize = sha512.BlockSize // An HMAC key can be used to symmetrically sign and verify messages -// using HMAC-SHA256. An HMACKey must be HMACKeyLen bytes. +// using HMAC-SHA512. An HMACKey must be HMACKeyLen bytes. type HMACKey []byte // A SignedMessage is a message that has been cryptographically signed -// with HMAC-SHA256. +// with HMAC-SHA512. type SignedMessage []byte // An EncryptionKey must be EncryptionKeySize bytes. @@ -59,33 +62,34 @@ const EncryptionKeySize = aesKeySize + HMACKeySize // must be EncryptionKeySize bytes. type EncryptionKey []byte -// An EncryptedMessage is encrypted with AES-256-CTR-HMAC-SHA256. +// An EncryptedMessage is encrypted with AES-256-CTR-HMAC-SHA512. type EncryptedMessage []byte -// HashIter runs PBKDF2-SHA256 for iter iterations. Useful for benchmarking. +// HashIter runs PBKDF2-SHA512 for iter iterations. Useful for benchmarking. The +// salt must be SaltSize bytes. func HashIter(password string, salt []byte, iter int) ([]byte, error) { - return pbkdf2.Key(sha256.New, password, salt, iter, sha256.Size) + return pbkdf2.Key(sha512.New, password, salt, iter, sha512.Size) } func hashWithSalt(password string, salt []byte) (RawKey, []byte, error) { - hash, err := HashIter(password, salt[:sha256.Size], defaultIterations) + hash, err := HashIter(password, salt[:oneSaltSize], defaultIterations) if err != nil { return RawKey{}, nil, err } - key, err := hkdf.Extract(sha256.New, hash, salt[sha256.Size:]) + key, err := hkdf.Extract(sha512.New, hash, salt[oneSaltSize:]) if err != nil { return RawKey{}, nil, err } - pwHash, err := hkdf.Expand(sha256.New, key, "pwhash", hashSize) + pwHash, err := hkdf.Expand(sha512.New, key, "pwhash", hashSize) if err != nil { return RawKey{}, nil, err } return RawKey{key}, pwHash, nil } -// Hash hashes a user password using PBKDF2-SHA256. +// Hash hashes a user password using PBKDF2-SHA512. func Hash(password string) (PasswordHash, error) { - salt := make([]byte, saltSize, saltSize+hashSize) + salt := make([]byte, SaltSize, SaltSize+hashSize) rand.Read(salt) _, pwHash, err := hashWithSalt(password, salt) if err != nil { @@ -97,7 +101,7 @@ func Hash(password string) (PasswordHash, error) { // CheckPassword verifies password against a PasswordHash and returns an // EncryptionKey derived from the password if successful. func (h PasswordHash) CheckPassword(password string) (RawKey, error) { - salt, expectedHash := h[:saltSize], h[saltSize:] + salt, expectedHash := h[:SaltSize], h[SaltSize:] key, pwHash, err := hashWithSalt(password, salt) if err != nil { return RawKey{}, err @@ -108,9 +112,9 @@ func (h PasswordHash) CheckPassword(password string) (RawKey, error) { return key, nil } -// Sign generates an HMAC-SHA256 signature and appends it to msg. +// Sign generates an HMAC-SHA512 signature and appends it to msg. func (k HMACKey) Sign(msg []byte) SignedMessage { - mac := hmac.New(sha256.New, k) + mac := hmac.New(sha512.New, k) mac.Write(msg) return mac.Sum(msg) } @@ -122,7 +126,7 @@ func (k HMACKey) Verify(msg SignedMessage) ([]byte, bool) { return nil, false } msg, sig := msg[:len(msg)-certSize], msg[len(msg)-certSize:] - mac := hmac.New(sha256.New, k) + mac := hmac.New(sha512.New, k) mac.Write(msg) if !hmac.Equal(sig, mac.Sum(nil)) { return nil, false @@ -132,10 +136,10 @@ func (k HMACKey) Verify(msg SignedMessage) ([]byte, bool) { // EncryptionKey derives an EncryptionKey. func (k RawKey) EncryptionKey() (EncryptionKey, error) { - return hkdf.Expand(sha256.New, k.key, "encrypt", EncryptionKeySize) + return hkdf.Expand(sha512.New, k.key, "encrypt", EncryptionKeySize) } -// Encrypt encrypts a message with AES-256-CTR-HMAC-SHA256. +// Encrypt encrypts a message with AES-256-CTR-HMAC-SHA512. func (k EncryptionKey) Encrypt(msg []byte) (EncryptedMessage, error) { aesKey, hmacKey := k[:aesKeySize], HMACKey(k[aesKeySize:]) block, err := aes.NewCipher(aesKey) diff --git a/tools/finditers/finditers.go b/tools/finditers/finditers.go index cd144fa..746242b 100644 --- a/tools/finditers/finditers.go +++ b/tools/finditers/finditers.go @@ -1,8 +1,8 @@ package main import ( - "encoding/hex" "fmt" + "math/rand/v2" "os" "sort" "testing" @@ -11,18 +11,17 @@ import ( "gitlab.com/rhogenson/roseh.moe/internal/cryptoutil" ) -func mustHex(s string) []byte { - bytes, err := hex.DecodeString(s) - if err != nil { - panic(fmt.Sprintf("mustHex: bad hex %q: %s", s, err)) - } - return bytes -} - var ( iterations int - salt = mustHex("3fb84513fc3afcd6d3b230bf9ece91aaae2d2a99da17efbf7de83b21fafe3f08") + salt = func() []byte { + r := rand.New(new(rand.PCG)) + salt := make([]byte, cryptoutil.SaltSize) + for i := range salt { + salt[i] = byte(r.IntN(256)) + } + return salt + }() ) func BenchmarkHashIter(b *testing.B) { @@ -33,13 +32,15 @@ func BenchmarkHashIter(b *testing.B) { } func run() error { - for iterations = 8192; time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) < 500*time.Millisecond; iterations *= 2 { + const targetDuration = 2 * time.Second + + for iterations = 8192; time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) < targetDuration; iterations *= 2 { } lo := iterations / 2 hi := iterations fmt.Println(lo + sort.Search(hi-lo, func(i int) bool { iterations = lo + i - return time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) > 500*time.Millisecond + return time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) > targetDuration })) return nil } |
