forked from sneak/secret
Add blank lines before return statements in all files to satisfy the nlreturn linter. This improves code readability by providing visual separation before return statements. Changes made across 24 files: - internal/cli/*.go - internal/secret/*.go - internal/vault/*.go - pkg/agehd/agehd.go - pkg/bip85/bip85.go All 143 nlreturn issues have been resolved.
117 lines
3.6 KiB
Go
117 lines
3.6 KiB
Go
package secret
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
|
|
"filippo.io/age"
|
|
"golang.org/x/term"
|
|
)
|
|
|
|
// EncryptToRecipient encrypts data to a recipient using age
|
|
func EncryptToRecipient(data []byte, recipient age.Recipient) ([]byte, error) {
|
|
Debug("EncryptToRecipient starting", "data_length", len(data))
|
|
|
|
var buf bytes.Buffer
|
|
Debug("Creating age encryptor")
|
|
w, err := age.Encrypt(&buf, recipient)
|
|
if err != nil {
|
|
Debug("Failed to create encryptor", "error", err)
|
|
|
|
return nil, fmt.Errorf("failed to create encryptor: %w", err)
|
|
}
|
|
Debug("Created age encryptor successfully")
|
|
|
|
Debug("Writing data to encryptor")
|
|
if _, err := w.Write(data); err != nil {
|
|
Debug("Failed to write data to encryptor", "error", err)
|
|
|
|
return nil, fmt.Errorf("failed to write data: %w", err)
|
|
}
|
|
Debug("Wrote data to encryptor successfully")
|
|
|
|
Debug("Closing encryptor")
|
|
if err := w.Close(); err != nil {
|
|
Debug("Failed to close encryptor", "error", err)
|
|
|
|
return nil, fmt.Errorf("failed to close encryptor: %w", err)
|
|
}
|
|
Debug("Closed encryptor successfully")
|
|
|
|
result := buf.Bytes()
|
|
Debug("EncryptToRecipient completed successfully", "result_length", len(result))
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// DecryptWithIdentity decrypts data with an identity using age
|
|
func DecryptWithIdentity(data []byte, identity age.Identity) ([]byte, error) {
|
|
r, err := age.Decrypt(bytes.NewReader(data), identity)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create decryptor: %w", err)
|
|
}
|
|
|
|
result, err := io.ReadAll(r)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read decrypted data: %w", err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// EncryptWithPassphrase encrypts data using a passphrase with age's scrypt-based encryption
|
|
func EncryptWithPassphrase(data []byte, passphrase string) ([]byte, error) {
|
|
recipient, err := age.NewScryptRecipient(passphrase)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create scrypt recipient: %w", err)
|
|
}
|
|
|
|
return EncryptToRecipient(data, recipient)
|
|
}
|
|
|
|
// DecryptWithPassphrase decrypts data using a passphrase with age's scrypt-based decryption
|
|
func DecryptWithPassphrase(encryptedData []byte, passphrase string) ([]byte, error) {
|
|
identity, err := age.NewScryptIdentity(passphrase)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create scrypt identity: %w", err)
|
|
}
|
|
|
|
return DecryptWithIdentity(encryptedData, identity)
|
|
}
|
|
|
|
// ReadPassphrase reads a passphrase securely from the terminal without echoing
|
|
// This version is for unlocking and doesn't require confirmation
|
|
func ReadPassphrase(prompt string) (string, error) {
|
|
// Check if stdin is a terminal
|
|
if !term.IsTerminal(syscall.Stdin) {
|
|
// Not a terminal - never read passphrases from piped input for security reasons
|
|
return "", fmt.Errorf("cannot read passphrase from non-terminal stdin " +
|
|
"(piped input or script). Please set the SB_UNLOCK_PASSPHRASE " +
|
|
"environment variable or run interactively")
|
|
}
|
|
|
|
// stdin is a terminal, check if stderr is also a terminal for interactive prompting
|
|
if !term.IsTerminal(syscall.Stderr) {
|
|
return "", fmt.Errorf("cannot prompt for passphrase: stderr is not a terminal " +
|
|
"(running in non-interactive mode). Please set the SB_UNLOCK_PASSPHRASE " +
|
|
"environment variable")
|
|
}
|
|
|
|
// Both stdin and stderr are terminals - use secure password reading
|
|
fmt.Fprint(os.Stderr, prompt) // Write prompt to stderr, not stdout
|
|
passphrase, err := term.ReadPassword(syscall.Stdin)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to read passphrase: %w", err)
|
|
}
|
|
fmt.Fprintln(os.Stderr) // Print newline to stderr since ReadPassword doesn't echo
|
|
|
|
if len(passphrase) == 0 {
|
|
return "", fmt.Errorf("passphrase cannot be empty")
|
|
}
|
|
|
|
return string(passphrase), nil
|
|
}
|