package models import ( "errors" "fmt" "log" "os" "path/filepath" "smt/utils" "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") } if os.Getenv("SECRETS_KEY") == "" { // Hash the secret key and store it on disk so we can verify if correct secret key is received filePath, _ := getHashFilePath() if filePath != "" && utils.FileExists(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 successfully verified supplied key\n") } } else if filePath != "" { log.Printf("ReceiveKey storing key into file '%s'\n", filePath) storeKeyHash(key, filePath) } else { return fmt.Errorf("unable to determine path to key hash file '%s'", hashFileName) } } else { log.Printf("ReceiveKey not storing hash on disk since we read key from environment variable") } // 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 }