From dc9ffceb3e280fa7c150490a96b71753202e05db Mon Sep 17 00:00:00 2001 From: Nathan Coad Date: Tue, 9 Jan 2024 21:22:16 +1100 Subject: [PATCH] verify unlock key against hash on disk --- controllers/unlock.go | 3 +- models/key.go | 73 +++++++++++++++++++++++++++++++++++++++++-- models/user.go | 8 ----- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/controllers/unlock.go b/controllers/unlock.go index 5f55561..7437638 100644 --- a/controllers/unlock.go +++ b/controllers/unlock.go @@ -12,8 +12,7 @@ type UnlockInput struct { SecretKey string `json:"secretKey"` } -// receive secret key and store it using the Key model - +// Unlock receives secret key and store it in memory func Unlock(c *gin.Context) { var input UnlockInput diff --git a/models/key.go b/models/key.go index c2f0b79..9c6f994 100644 --- a/models/key.go +++ b/models/key.go @@ -1,11 +1,63 @@ package models -import "errors" +import ( + "errors" + "fmt" + "log" + "os" + "path/filepath" + + "golang.org/x/crypto/bcrypt" +) + +const hashFileName = "verify.hash" // TODO: Look at using shamir's secret sharing to distribute components of the secret key var secretKey []byte var secretReceived bool +func getHashFilePath() (string, error) { + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) + if err != nil { + return "", err + } + + filePath := filepath.Join(dir, hashFileName) + return filePath, nil +} + +func storeKeyHash(plaintext string, filePath string) error { + hash, err := bcrypt.GenerateFromPassword([]byte(plaintext), bcrypt.DefaultCost) + if err != nil { + return err + } + + err = os.WriteFile(filePath, hash, 0600) + if err != nil { + return err + } + + log.Println("Bcrypt hash stored in file:", filePath) + return nil +} + +func compareHashWithPlaintext(plaintext string, filePath string) (bool, error) { + hashBytes, err := os.ReadFile(filePath) + if err != nil { + return false, err + } + + err = bcrypt.CompareHashAndPassword(hashBytes, []byte(plaintext)) + if err != nil { + if err == bcrypt.ErrMismatchedHashAndPassword { + return false, nil // Passwords don't match + } + return false, err // Other error occurred + } + + return true, nil // Passwords match +} + func ReceiveKey(key string) error { // confirm that the key is 32 bytes long exactly @@ -13,9 +65,26 @@ func ReceiveKey(key string) error { return errors.New("secret key provided is not exactly 32 bytes long") } - // Store the secret key so that we can access it when encrypting/decrypting + // TODO hash the secret key and store it on disk so we can verify if correct secret key is received + filePath, _ := getHashFilePath() + + if filePath != "" { + // File already exists, compare received key with hash in file + compare, err := compareHashWithPlaintext(key, filePath) + if err != nil { + return fmt.Errorf("unable to verify secret key: '%s'", err.Error()) + } + if !compare { + return errors.New("secret key is not correct") + } + } else { + storeKeyHash(key, filePath) + } + + // Store the secret key in memory so that we can access it when encrypting/decrypting secretKey = []byte(key) secretReceived = true + return nil } diff --git a/models/user.go b/models/user.go index 49aa108..4444c5e 100644 --- a/models/user.go +++ b/models/user.go @@ -424,11 +424,3 @@ func UserCheckIfAdmin(userId int) bool { return u.Admin } - -func UserGetSafe() { - -} - -// need a way of checking what safe a user has access to -// if they only have access to one then that is easy -// if they are an admin then they have access to everything