|
|
|
|
@@ -1,9 +1,9 @@
|
|
|
|
|
package cli
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"log"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
@@ -127,22 +127,27 @@ func newUnlockerAddCmd() *cobra.Command {
|
|
|
|
|
Use --keyid to specify a particular key, otherwise uses your default GPG key.`
|
|
|
|
|
|
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
|
supportedTypes = "passphrase, keychain, pgp"
|
|
|
|
|
supportedTypes = "passphrase, keychain, pgp, secure-enclave"
|
|
|
|
|
typeDescriptions = `Available unlocker types:
|
|
|
|
|
|
|
|
|
|
passphrase - Traditional password-based encryption
|
|
|
|
|
Prompts for a passphrase that will be used to encrypt/decrypt the vault's master key.
|
|
|
|
|
The passphrase is never stored in plaintext.
|
|
|
|
|
passphrase - Traditional password-based encryption
|
|
|
|
|
Prompts for a passphrase that will be used to encrypt/decrypt the vault's master key.
|
|
|
|
|
The passphrase is never stored in plaintext.
|
|
|
|
|
|
|
|
|
|
keychain - macOS Keychain integration (macOS only)
|
|
|
|
|
Stores the vault's master key in the macOS Keychain, protected by your login password.
|
|
|
|
|
Automatically unlocks when your Keychain is unlocked (e.g., after login).
|
|
|
|
|
Provides seamless integration with macOS security features like Touch ID.
|
|
|
|
|
keychain - macOS Keychain integration (macOS only)
|
|
|
|
|
Stores the vault's master key in the macOS Keychain, protected by your login password.
|
|
|
|
|
Automatically unlocks when your Keychain is unlocked (e.g., after login).
|
|
|
|
|
Provides seamless integration with macOS security features like Touch ID.
|
|
|
|
|
|
|
|
|
|
pgp - GNU Privacy Guard (GPG) key-based encryption
|
|
|
|
|
Uses your existing GPG key to encrypt/decrypt the vault's master key.
|
|
|
|
|
Requires gpg to be installed and configured with at least one secret key.
|
|
|
|
|
Use --keyid to specify a particular key, otherwise uses your default GPG key.`
|
|
|
|
|
pgp - GNU Privacy Guard (GPG) key-based encryption
|
|
|
|
|
Uses your existing GPG key to encrypt/decrypt the vault's master key.
|
|
|
|
|
Requires gpg to be installed and configured with at least one secret key.
|
|
|
|
|
Use --keyid to specify a particular key, otherwise uses your default GPG key.
|
|
|
|
|
|
|
|
|
|
secure-enclave - Apple Secure Enclave hardware protection (macOS only)
|
|
|
|
|
Stores the vault's master key encrypted by a non-exportable P-256 key
|
|
|
|
|
held in the Secure Enclave. The key never leaves the hardware.
|
|
|
|
|
Uses ECIES encryption; decryption is performed inside the SE.`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
|
@@ -319,6 +324,8 @@ func (cli *Instance) UnlockersList(jsonOutput bool) error {
|
|
|
|
|
unlocker = secret.NewKeychainUnlocker(cli.fs, unlockerDir, diskMetadata)
|
|
|
|
|
case "pgp":
|
|
|
|
|
unlocker = secret.NewPGPUnlocker(cli.fs, unlockerDir, diskMetadata)
|
|
|
|
|
case "secure-enclave":
|
|
|
|
|
unlocker = secret.NewSecureEnclaveUnlocker(cli.fs, unlockerDir, diskMetadata)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
@@ -410,7 +417,7 @@ func (cli *Instance) UnlockersAdd(unlockerType string, cmd *cobra.Command) error
|
|
|
|
|
// Build the supported types list based on platform
|
|
|
|
|
supportedTypes := "passphrase, pgp"
|
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
|
supportedTypes = "passphrase, keychain, pgp"
|
|
|
|
|
supportedTypes = "passphrase, keychain, pgp, secure-enclave"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch unlockerType {
|
|
|
|
|
@@ -481,6 +488,31 @@ func (cli *Instance) UnlockersAdd(unlockerType string, cmd *cobra.Command) error
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
case "secure-enclave":
|
|
|
|
|
if runtime.GOOS != "darwin" {
|
|
|
|
|
return fmt.Errorf("secure enclave unlockers are only supported on macOS")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seUnlocker, err := secret.CreateSecureEnclaveUnlocker(cli.fs, cli.stateDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to create Secure Enclave unlocker: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmd.Printf("Created Secure Enclave unlocker: %s\n", seUnlocker.GetID())
|
|
|
|
|
|
|
|
|
|
vlt, err := vault.GetCurrentVault(cli.fs, cli.stateDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to get current vault: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := vlt.SelectUnlocker(seUnlocker.GetID()); err != nil {
|
|
|
|
|
cmd.Printf("Warning: Failed to auto-select new unlocker: %v\n", err)
|
|
|
|
|
} else {
|
|
|
|
|
cmd.Printf("Automatically selected as current unlocker\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
|
|
case "pgp":
|
|
|
|
|
// Get GPG key ID from flag, environment, or default key
|
|
|
|
|
var gpgKeyID string
|
|
|
|
|
@@ -656,6 +688,8 @@ func (cli *Instance) checkUnlockerExists(vlt *vault.Vault, unlockerID string) er
|
|
|
|
|
unlocker = secret.NewKeychainUnlocker(cli.fs, unlockerDir, diskMetadata)
|
|
|
|
|
case "pgp":
|
|
|
|
|
unlocker = secret.NewPGPUnlocker(cli.fs, unlockerDir, diskMetadata)
|
|
|
|
|
case "secure-enclave":
|
|
|
|
|
unlocker = secret.NewSecureEnclaveUnlocker(cli.fs, unlockerDir, diskMetadata)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if unlocker != nil && unlocker.GetID() == unlockerID {
|
|
|
|
|
|