mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
first test
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GenerateSecureSecret generates a cryptographically secure random secret
|
||||
func GenerateSecureSecret(byteLength int) (string, error) {
|
||||
bytes := make([]byte, byteLength)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
// GenerateSecureKey generates a hex-encoded encryption key
|
||||
func GenerateSecureKey(bitLength int) (string, error) {
|
||||
byteLength := bitLength / 8
|
||||
bytes := make([]byte, byteLength)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
// GetOrCreateJWTSecret retrieves JWT secret from file or generates a new one
|
||||
func GetOrCreateJWTSecret() (string, error) {
|
||||
secretFile := "jwt_secret.key"
|
||||
|
||||
// Try to read existing secret
|
||||
if secret, err := readSecretFromFile(secretFile); err == nil {
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// Generate new secret
|
||||
secret, err := GenerateSecureSecret(32) // 32 bytes = 256 bits
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate JWT secret: %w", err)
|
||||
}
|
||||
|
||||
// Save to file
|
||||
if err := saveSecretToFile(secretFile, secret); err != nil {
|
||||
return "", fmt.Errorf("failed to save JWT secret: %w", err)
|
||||
}
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// GetOrCreateEncryptionKey retrieves encryption key from file or generates a new one
|
||||
func GetOrCreateEncryptionKey() (string, error) {
|
||||
keyFile := "encryption.key"
|
||||
|
||||
// Try to read existing key
|
||||
if key, err := readSecretFromFile(keyFile); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Generate new key
|
||||
key, err := GenerateSecureKey(256) // 256 bits
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate encryption key: %w", err)
|
||||
}
|
||||
|
||||
// Save to file
|
||||
if err := saveSecretToFile(keyFile, key); err != nil {
|
||||
return "", fmt.Errorf("failed to save encryption key: %w", err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// readSecretFromFile reads a secret from a file
|
||||
func readSecretFromFile(filename string) (string, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
secret := string(data)
|
||||
if secret == "" {
|
||||
return "", fmt.Errorf("empty secret file")
|
||||
}
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// saveSecretToFile saves a secret to a file with secure permissions
|
||||
func saveSecretToFile(filename, secret string) error {
|
||||
// Create the file with restricted permissions (only readable by owner)
|
||||
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(secret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSecretStrength checks if a secret meets minimum security requirements
|
||||
func ValidateSecretStrength(secret string, minLength int) error {
|
||||
if len(secret) < minLength {
|
||||
return fmt.Errorf("secret too short: minimum %d characters required", minLength)
|
||||
}
|
||||
|
||||
// Check entropy (basic check)
|
||||
entropy := calculateEntropy(secret)
|
||||
if entropy < 3.0 { // Minimum entropy threshold
|
||||
return fmt.Errorf("secret has low entropy: %.2f (minimum 3.0)", entropy)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateEntropy calculates the Shannon entropy of a string
|
||||
func calculateEntropy(s string) float64 {
|
||||
if len(s) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Count character frequencies
|
||||
freq := make(map[rune]int)
|
||||
for _, char := range s {
|
||||
freq[char]++
|
||||
}
|
||||
|
||||
// Calculate entropy
|
||||
entropy := 0.0
|
||||
length := float64(len(s))
|
||||
|
||||
for _, count := range freq {
|
||||
if count > 0 {
|
||||
p := float64(count) / length
|
||||
entropy -= p * log2(p)
|
||||
}
|
||||
}
|
||||
|
||||
return entropy
|
||||
}
|
||||
|
||||
// log2 calculates base-2 logarithm
|
||||
func log2(x float64) float64 {
|
||||
const ln2 = 0.6931471805599453 // ln(2)
|
||||
return 1.0 / ln2 * logNatural(x)
|
||||
}
|
||||
|
||||
// logNatural calculates natural logarithm using approximation
|
||||
func logNatural(x float64) float64 {
|
||||
if x <= 0 {
|
||||
return 0
|
||||
}
|
||||
if x == 1 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Simple approximation for ln(x)
|
||||
// For production, use math.Log from the standard library
|
||||
n := 0.0
|
||||
for x > 1.0 {
|
||||
x /= 2.718281828459045 // e
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// RotateSecret generates a new secret and updates the file
|
||||
func RotateSecret(filename string) (string, error) {
|
||||
// Generate new secret
|
||||
var newSecret string
|
||||
var err error
|
||||
|
||||
if filename == "jwt_secret.key" {
|
||||
newSecret, err = GenerateSecureSecret(32)
|
||||
} else if filename == "encryption.key" {
|
||||
newSecret, err = GenerateSecureKey(256)
|
||||
} else {
|
||||
return "", fmt.Errorf("unknown secret file type: %s", filename)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate new secret: %w", err)
|
||||
}
|
||||
|
||||
// Backup old secret if it exists
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
backupFile := fmt.Sprintf("%s.backup.%d", filename, time.Now().Unix())
|
||||
if err := os.Rename(filename, backupFile); err != nil {
|
||||
return "", fmt.Errorf("failed to backup old secret: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Save new secret
|
||||
if err := saveSecretToFile(filename, newSecret); err != nil {
|
||||
return "", fmt.Errorf("failed to save new secret: %w", err)
|
||||
}
|
||||
|
||||
return newSecret, nil
|
||||
}
|
||||
|
||||
// GetSecretFilePath returns the full path to a secret file
|
||||
func GetSecretFilePath(filename string) string {
|
||||
// Store secrets in a secure directory
|
||||
secretDir := os.Getenv("SECRET_DIR")
|
||||
if secretDir == "" {
|
||||
secretDir = "./secrets"
|
||||
}
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if err := os.MkdirAll(secretDir, 0700); err != nil {
|
||||
// Fallback to current directory
|
||||
return filename
|
||||
}
|
||||
|
||||
return filepath.Join(secretDir, filename)
|
||||
}
|
||||
Reference in New Issue
Block a user