All checks were successful
continuous-integration/drone/push Build is passing
111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
package models
|
|
|
|
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 {
|
|
log.Printf("storeKeyHash error generating hash : '%s'\n", err)
|
|
return err
|
|
}
|
|
|
|
err = os.WriteFile(filePath, hash, 0600)
|
|
if err != nil {
|
|
log.Printf("storeKeyHash error writing file : '%s'\n", err)
|
|
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 {
|
|
log.Printf("compareHashWithPlaintext error reading hashfile : '%s'\n", err)
|
|
return false, err
|
|
}
|
|
|
|
err = bcrypt.CompareHashAndPassword(hashBytes, []byte(plaintext))
|
|
if err != nil {
|
|
if err == bcrypt.ErrMismatchedHashAndPassword {
|
|
log.Printf("compareHashWithPlaintext provided key is incorrect")
|
|
return false, nil // Passwords don't match
|
|
}
|
|
log.Printf("compareHashWithPlaintext error comparing provided key : '%s'\n", err)
|
|
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
|
|
if len(key) != 32 {
|
|
return errors.New("secret key provided is not exactly 32 bytes long")
|
|
}
|
|
|
|
// TODO hash the secret key and store it on disk so we can verify if correct secret key is received
|
|
filePath, _ := getHashFilePath()
|
|
|
|
if filePath != "" {
|
|
log.Printf("ReceiveKey detected hash file at '%s'\n", 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 {
|
|
log.Printf("ReceiveKey storing key into file '%s'\n", filePath)
|
|
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
|
|
}
|
|
|
|
func ProvideKey() ([]byte, error) {
|
|
|
|
// Provide the key when needed to decrypt/encrypt stored secrets
|
|
if secretReceived {
|
|
return secretKey, nil
|
|
} else {
|
|
return nil, errors.New("secret key has not been received")
|
|
}
|
|
}
|
|
|
|
func CheckKeyProvided() bool {
|
|
return secretReceived
|
|
}
|