Support encrypted native identity keys
ci / test (push) Has been cancelled
ci / remote-bench (push) Has been cancelled

This commit is contained in:
DuProcess
2026-06-13 15:26:31 -04:00
parent 55a0fc1ab4
commit f324a7627f
4 changed files with 287 additions and 20 deletions
+42 -5
View File
@@ -358,13 +358,24 @@ pub fn derive_native_session_key(
}
pub fn load_ed25519_identity(path: &Path) -> Result<SigningKey> {
load_ed25519_identity_with_passphrase(path, None)
}
pub fn load_ed25519_identity_with_passphrase(
path: &Path,
passphrase: Option<&str>,
) -> Result<SigningKey> {
let private_key = ssh_key::PrivateKey::read_openssh_file(path)
.with_context(|| format!("read OpenSSH identity {}", path.display()))?;
anyhow::ensure!(
!private_key.is_encrypted(),
"OpenSSH identity {} is encrypted; encrypted-key prompts are not wired yet",
path.display()
);
let private_key = if private_key.is_encrypted() {
let passphrase = passphrase
.ok_or_else(|| anyhow::anyhow!("OpenSSH identity {} is encrypted", path.display()))?;
private_key
.decrypt(passphrase)
.with_context(|| format!("decrypt OpenSSH identity {}", path.display()))?
} else {
private_key
};
let ed25519 = private_key
.key_data()
.ed25519()
@@ -929,6 +940,32 @@ mod tests {
);
}
#[test]
fn load_ed25519_identity_decrypts_encrypted_openssh_private_key() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("id_ed25519");
let signing = SigningKey::from_bytes(&[12u8; 32]);
let keypair = ssh_key::private::Ed25519Keypair::from(&signing);
let private =
ssh_key::PrivateKey::new(ssh_key::private::KeypairData::from(keypair), "").unwrap();
let encrypted = private
.encrypt(&mut rand::rngs::OsRng, "correct horse battery staple")
.unwrap();
encrypted
.write_openssh_file(&path, ssh_key::LineEnding::LF)
.unwrap();
assert!(load_ed25519_identity(&path).is_err());
assert!(load_ed25519_identity_with_passphrase(&path, Some("wrong")).is_err());
let loaded =
load_ed25519_identity_with_passphrase(&path, Some("correct horse battery staple"))
.unwrap();
assert_eq!(
VerifyingKey::from(&loaded).to_bytes(),
VerifyingKey::from(&signing).to_bytes()
);
}
#[test]
fn native_session_key_derivation_matches_and_binds_transcript() {
let host_signing = SigningKey::from_bytes(&[3u8; 32]);