c9e15513c7
- app.md - backend/dist/assets/index-CFDWXN9Z.js - backend/dist/index.html - backend/handlers.go - backend/main.go - backend/users.go - e2e/smoke_live.sh - frontend/src/App.tsx - frontend/src/api.ts - frontend/src/components/CardChatPanel.tsx - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
1.7 KiB
Go
69 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
const moduleKeyEnv = "KANBAN_MODULE_KEY"
|
|
|
|
// moduleKey derives a 32-byte AES key from the KANBAN_MODULE_KEY env var.
|
|
// Returns (key, true) when present; (zero, false) when missing — callers
|
|
// must treat that as "module dispatcher disabled".
|
|
func moduleKey() ([32]byte, bool) {
|
|
v := os.Getenv(moduleKeyEnv)
|
|
if v == "" {
|
|
return [32]byte{}, false
|
|
}
|
|
return sha256.Sum256([]byte(v)), true
|
|
}
|
|
|
|
// encryptConfig encrypts a JSON config blob with AES-GCM. Returns the
|
|
// ciphertext and the 12-byte nonce. Caller persists both columns.
|
|
func encryptConfig(plain []byte) (cipherOut, nonce []byte, err error) {
|
|
key, ok := moduleKey()
|
|
if !ok {
|
|
return nil, nil, fmt.Errorf("%s not set; cannot encrypt module config", moduleKeyEnv)
|
|
}
|
|
block, err := aes.NewCipher(key[:])
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
nonce = make([]byte, gcm.NonceSize())
|
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
cipherOut = gcm.Seal(nil, nonce, plain, nil)
|
|
return cipherOut, nonce, nil
|
|
}
|
|
|
|
// decryptConfig is the inverse of encryptConfig.
|
|
func decryptConfig(cipherIn, nonce []byte) ([]byte, error) {
|
|
key, ok := moduleKey()
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s not set; cannot decrypt module config", moduleKeyEnv)
|
|
}
|
|
if len(nonce) == 0 {
|
|
return nil, errors.New("nonce empty")
|
|
}
|
|
block, err := aes.NewCipher(key[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return gcm.Open(nil, nonce, cipherIn, nil)
|
|
}
|