cli/cmd/crypt/cmd_encrypt.go
Claude 47e11e7861
refactor: flatten commands, extract php/ci variants to own repos
- Remove internal/cmd/php/ (now core/php repo)
- Remove internal/cmd/ci/ and internal/cmd/sdk/ (now core/ci repo)
- Remove internal/variants/ build tag system entirely
- Move all 30 remaining command packages from internal/cmd/ to cmd/
- Rewrite main.go with direct imports (no more variant selection)
- Update all cross-references from internal/cmd/ to cmd/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 14:44:28 +00:00

115 lines
2.8 KiB
Go

package crypt
import (
"fmt"
"os"
"strings"
"forge.lthn.ai/core/go/pkg/cli"
"forge.lthn.ai/core/go/pkg/crypt"
)
// Encrypt command flags
var (
encryptPassphrase string
encryptAES bool
)
func addEncryptCommand(parent *cli.Command) {
encryptCmd := cli.NewCommand("encrypt", "Encrypt a file", "", func(cmd *cli.Command, args []string) error {
return runEncrypt(args[0])
})
encryptCmd.Args = cli.ExactArgs(1)
cli.StringFlag(encryptCmd, &encryptPassphrase, "passphrase", "p", "", "Passphrase (prompted if not given)")
cli.BoolFlag(encryptCmd, &encryptAES, "aes", "", false, "Use AES-256-GCM instead of ChaCha20-Poly1305")
parent.AddCommand(encryptCmd)
decryptCmd := cli.NewCommand("decrypt", "Decrypt an encrypted file", "", func(cmd *cli.Command, args []string) error {
return runDecrypt(args[0])
})
decryptCmd.Args = cli.ExactArgs(1)
cli.StringFlag(decryptCmd, &encryptPassphrase, "passphrase", "p", "", "Passphrase (prompted if not given)")
cli.BoolFlag(decryptCmd, &encryptAES, "aes", "", false, "Use AES-256-GCM instead of ChaCha20-Poly1305")
parent.AddCommand(decryptCmd)
}
func getPassphrase() (string, error) {
if encryptPassphrase != "" {
return encryptPassphrase, nil
}
return cli.Prompt("Passphrase", "")
}
func runEncrypt(path string) error {
passphrase, err := getPassphrase()
if err != nil {
return cli.Wrap(err, "failed to read passphrase")
}
if passphrase == "" {
return cli.Err("passphrase cannot be empty")
}
data, err := os.ReadFile(path)
if err != nil {
return cli.Wrap(err, "failed to read file")
}
var encrypted []byte
if encryptAES {
encrypted, err = crypt.EncryptAES(data, []byte(passphrase))
} else {
encrypted, err = crypt.Encrypt(data, []byte(passphrase))
}
if err != nil {
return cli.Wrap(err, "failed to encrypt")
}
outPath := path + ".enc"
if err := os.WriteFile(outPath, encrypted, 0o600); err != nil {
return cli.Wrap(err, "failed to write encrypted file")
}
cli.Success(fmt.Sprintf("Encrypted %s -> %s", path, outPath))
return nil
}
func runDecrypt(path string) error {
passphrase, err := getPassphrase()
if err != nil {
return cli.Wrap(err, "failed to read passphrase")
}
if passphrase == "" {
return cli.Err("passphrase cannot be empty")
}
data, err := os.ReadFile(path)
if err != nil {
return cli.Wrap(err, "failed to read file")
}
var decrypted []byte
if encryptAES {
decrypted, err = crypt.DecryptAES(data, []byte(passphrase))
} else {
decrypted, err = crypt.Decrypt(data, []byte(passphrase))
}
if err != nil {
return cli.Wrap(err, "failed to decrypt")
}
outPath := strings.TrimSuffix(path, ".enc")
if outPath == path {
outPath = path + ".dec"
}
if err := os.WriteFile(outPath, decrypted, 0o600); err != nil {
return cli.Wrap(err, "failed to write decrypted file")
}
cli.Success(fmt.Sprintf("Decrypted %s -> %s", path, outPath))
return nil
}