package sqlite import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "fmt" "io" ) func normalizeSecret(secret []byte) []byte { if len(secret) == 0 { // Deterministic fallback for local/dev; production should set TASK_MASTER_SECRET. sum := sha256.Sum256([]byte("task-master-dev-secret")) return sum[:] } sum := sha256.Sum256(secret) return sum[:] } // EncryptToken seals plaintext with AES-GCM. func (db *DB) EncryptToken(plain string) ([]byte, error) { block, err := aes.NewCipher(db.Secret) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } nonce := make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, err } return gcm.Seal(nonce, nonce, []byte(plain), nil), nil } // DecryptToken opens an AES-GCM blob. func (db *DB) DecryptToken(blob []byte) (string, error) { block, err := aes.NewCipher(db.Secret) if err != nil { return "", err } gcm, err := cipher.NewGCM(block) if err != nil { return "", err } if len(blob) < gcm.NonceSize() { return "", fmt.Errorf("token ciphertext too short") } nonce, ct := blob[:gcm.NonceSize()], blob[gcm.NonceSize():] plain, err := gcm.Open(nil, nonce, ct, nil) if err != nil { return "", err } return string(plain), nil }