From c59b0c83408aee3c376747351c94083acd6076f5 Mon Sep 17 00:00:00 2001 From: mStar Date: Mon, 31 Mar 2025 23:06:27 +0200 Subject: [PATCH] Move hashing and encryption to auth/helpers --- auth-new/helpers.go | 110 +++++++++++++++++++++++++++++++++++++++++ auth-new/password.go | 30 ----------- storage-new/helpers.go | 65 ------------------------ 3 files changed, 110 insertions(+), 95 deletions(-) create mode 100644 auth-new/helpers.go delete mode 100644 storage-new/helpers.go diff --git a/auth-new/helpers.go b/auth-new/helpers.go new file mode 100644 index 0000000..8907cdb --- /dev/null +++ b/auth-new/helpers.go @@ -0,0 +1,110 @@ +package auth + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + + "golang.org/x/crypto/argon2" +) + +// Len of salt for passwords in bytes +const saltLen = 32 + +func generateSalt(length int) ([]byte, error) { + salt := make([]byte, length) + if _, err := rand.Read(salt); err != nil { + return nil, err + } + return salt, nil +} + +func hashPassword(password string) ([]byte, error) { + salt, err := generateSalt(saltLen) + if err != nil { + return nil, err + } + hash := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32) + hash = append(hash, salt...) + // return bcrypt.GenerateFromPassword([]byte(password), 14) + return hash, nil +} + +// Compare a raw password against a hash + salt +func comparePassword(password string, hash []byte) bool { + // Hash is actually hash(password)+salt + salt := hash[len(hash)-saltLen:] + + return bytes.Equal(argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32), hash) +} + +// Copied and adjusted from: https://bruinsslot.jp/post/golang-crypto/ + +func Encrypt(key, data []byte) ([]byte, error) { + key, salt, err := deriveKey(key, nil) + if err != nil { + return nil, err + } + + blockCipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(blockCipher) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = rand.Read(nonce); err != nil { + return nil, err + } + + ciphertext := gcm.Seal(nonce, nonce, data, nil) + + ciphertext = append(ciphertext, salt...) + + return ciphertext, nil +} + +func Decrypt(key, data []byte) ([]byte, error) { + salt, data := data[len(data)-32:], data[:len(data)-32] + + key, _, err := deriveKey(key, salt) + if err != nil { + return nil, err + } + + blockCipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(blockCipher) + if err != nil { + return nil, err + } + + nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():] + + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil +} + +func deriveKey(password, salt []byte) ([]byte, []byte, error) { + if salt == nil { + salt = make([]byte, 32) + if _, err := rand.Read(salt); err != nil { + return nil, nil, err + } + } + + key := argon2.IDKey(password, salt, 1, 64*1024, 4, 32) + return key, salt, nil +} diff --git a/auth-new/password.go b/auth-new/password.go index ad22503..077eecb 100644 --- a/auth-new/password.go +++ b/auth-new/password.go @@ -1,14 +1,11 @@ package auth import ( - "bytes" - "crypto/rand" "time" "git.mstar.dev/mstar/goutils/other" "git.mstar.dev/mstar/goutils/sliceutils" "github.com/google/uuid" - "golang.org/x/crypto/argon2" "gorm.io/gorm" "gorm.io/gorm/clause" @@ -16,33 +13,6 @@ import ( "git.mstar.dev/mstar/linstrom/storage-new/models" ) -const saltLen = 32 - -func generateSalt(length int) ([]byte, error) { - salt := make([]byte, length) - if _, err := rand.Read(salt); err != nil { - return nil, err - } - return salt, nil -} - -func hashPassword(password string) ([]byte, error) { - salt, err := generateSalt(saltLen) - if err != nil { - return nil, err - } - hash := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32) - hash = append(hash, salt...) - // return bcrypt.GenerateFromPassword([]byte(password), 14) - return hash, nil -} - -func comparePassword(password string, hash []byte) bool { - salt := hash[len(hash)-saltLen:] - - return bytes.Equal(argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32), hash) -} - // Start a login process with a username (NOT account ID) and password // Returns the next state, a token corresponding to that state and error // Token will be empty on failure, error describes the reason for the diff --git a/storage-new/helpers.go b/storage-new/helpers.go deleted file mode 100644 index d212d92..0000000 --- a/storage-new/helpers.go +++ /dev/null @@ -1,65 +0,0 @@ -package storage - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" -) - -// Copied and adjusted from: https://bruinsslot.jp/post/golang-crypto/ - -func Encrypt(key, data []byte) ([]byte, error) { - // key, salt, err := DeriveKey(key, nil) - // if err != nil { - // return nil, err - // } - // - blockCipher, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(blockCipher) - if err != nil { - return nil, err - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err = rand.Read(nonce); err != nil { - return nil, err - } - - ciphertext := gcm.Seal(nonce, nonce, data, nil) - - // ciphertext = append(ciphertext, salt...) - - return ciphertext, nil -} - -func Decrypt(key, data []byte) ([]byte, error) { - // salt, data := data[len(data)-32:], data[:len(data)-32] - - // key, _, err := DeriveKey(key, salt) - // if err != nil { - // return nil, err - // } - // - blockCipher, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(blockCipher) - if err != nil { - return nil, err - } - - nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():] - - plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil -}