feat: Phase 2 Security Enhancement

- pkg/crypto: AES-256-GCM encryption with PBKDF2 key derivation
  - 100k iterations, 16-byte salt, SHA-256
  - Encrypt/Decrypt/IsEncrypted/HashPassword
- Storage layer encryption:
  - JSONStorage.SetPassword() enables transparent encrypt/decrypt
  - readJSON auto-decrypts, replace* auto-encrypts
- pkg/knownhosts: TOFU host key verification
  - Verify/Add/Remove host keys
  - HostKeyCallback for SSH config
- SSH client security:
  - SetHostKeyCallback() replaces InsecureIgnoreHostKey()
  - SetPassphraseCallback() for encrypted private keys
  - getKeySigner() tries passphrase on encrypted keys
- Models: AppConfig gains EncryptionEnabled, PasswordHash, KnownHostsFile
This commit is contained in:
swanadiva
2026-06-25 13:28:46 +07:00
parent a1cd3d5dc0
commit 611b794fc7
7 changed files with 445 additions and 8 deletions
+11 -1
View File
@@ -79,10 +79,20 @@ func (c *Client) getKeySigner() (cryptossh.Signer, error) {
return nil, fmt.Errorf("failed to read key file %s: %w", keyPath, err)
}
// Parse the key (support passphrase-protected keys in Phase 2)
// Parse the key (support passphrase-protected keys)
var signer cryptossh.Signer
if c.host.Auth.Password != "" {
signer, err = cryptossh.ParsePrivateKeyWithPassphrase(keyData, []byte(c.host.Auth.Password))
} else if c.passphraseCallback != nil {
// Try without passphrase first
signer, err = cryptossh.ParsePrivateKey(keyData)
if err != nil && strings.Contains(err.Error(), "encrypted") {
// Key is encrypted, prompt for passphrase
passphrase := c.passphraseCallback()
if passphrase != "" {
signer, err = cryptossh.ParsePrivateKeyWithPassphrase(keyData, []byte(passphrase))
}
}
} else {
signer, err = cryptossh.ParsePrivateKey(keyData)
}