From 188f7fcd4eb111f0111d4d8cca7e9615ee871c90 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 25 Jan 2024 04:51:26 +0000 Subject: [PATCH] Add specific error message for OpenSSH hardware authenticators --- age/i18n/en-US/age.ftl | 19 +++++++++++++++++++ age/i18n/es-AR/age.ftl | 3 +++ age/i18n/it/age.ftl | 3 +++ age/i18n/zh-CN/age.ftl | 3 +++ age/i18n/zh-TW/age.ftl | 3 +++ age/src/cli_common/recipients.rs | 2 +- age/src/ssh.rs | 3 ++- age/src/ssh/identity.rs | 24 ++++++++++++++++++++---- age/src/ssh/recipient.rs | 6 +++--- 9 files changed, 57 insertions(+), 9 deletions(-) diff --git a/age/i18n/en-US/age.ftl b/age/i18n/en-US/age.ftl index f89d8ae0..b7f414ee 100644 --- a/age/i18n/en-US/age.ftl +++ b/age/i18n/en-US/age.ftl @@ -17,6 +17,9 @@ -ssh-keygen = ssh-keygen -ssh-rsa = ssh-rsa -ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV ## CLI helpers @@ -148,3 +151,19 @@ ssh-unsupported-key-type = subset of these for backwards compatibility, specifically the '{-ssh-rsa}' and '{-ssh-ed25519}' key types. This SSH key uses the unsupported key type '{$key_type}'. + +ssh-unsupported-security-key = + Unsupported SSH Hardware Authenticator + -------------------------------------- + {-openssh} version 8.2p1 added support for {-fido-u2f} hardware authenticators, + including hardware security keys such as {-yubikeys}. {-rage} does not work with + these SSH key types, because their protocol does not support encryption. + This SSH key uses the incompatible type '{$key_type}'. + + If you have a compatible hardware security key, you should use this plugin: + + {$age_plugin_yubikey_url} + + A hardware security key used with both {-openssh} and this plugin will have a + separate SSH public key and {-age} encryption recipient, because the plugin + implements the {-piv} protocol. diff --git a/age/i18n/es-AR/age.ftl b/age/i18n/es-AR/age.ftl index 18022ffe..657760a3 100644 --- a/age/i18n/es-AR/age.ftl +++ b/age/i18n/es-AR/age.ftl @@ -17,6 +17,9 @@ -ssh-keygen = ssh-keygen -ssh-rsa = ssh-rsa -ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV ## CLI helpers diff --git a/age/i18n/it/age.ftl b/age/i18n/it/age.ftl index 762caafa..c643aa0e 100644 --- a/age/i18n/it/age.ftl +++ b/age/i18n/it/age.ftl @@ -17,6 +17,9 @@ -ssh-keygen = ssh-keygen -ssh-rsa = ssh-rsa -ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV ## CLI helpers diff --git a/age/i18n/zh-CN/age.ftl b/age/i18n/zh-CN/age.ftl index 098ea312..f38cf1de 100644 --- a/age/i18n/zh-CN/age.ftl +++ b/age/i18n/zh-CN/age.ftl @@ -17,6 +17,9 @@ -ssh-keygen = ssh-keygen -ssh-rsa = ssh-rsa -ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV ## CLI helpers diff --git a/age/i18n/zh-TW/age.ftl b/age/i18n/zh-TW/age.ftl index aa6d398f..871ba939 100644 --- a/age/i18n/zh-TW/age.ftl +++ b/age/i18n/zh-TW/age.ftl @@ -17,6 +17,9 @@ -ssh-keygen = ssh-keygen -ssh-rsa = ssh-rsa -ssh-ed25519 = ssh-ed25519 +-fido-u2f = FIDO/U2F +-yubikeys = YubiKeys +-piv = PIV ## CLI helpers diff --git a/age/src/cli_common/recipients.rs b/age/src/cli_common/recipients.rs index 6792b9e4..9e4022f6 100644 --- a/age/src/cli_common/recipients.rs +++ b/age/src/cli_common/recipients.rs @@ -35,7 +35,7 @@ where ParseRecipientKeyError::RsaModulusTooLarge => Err(ReadError::RsaModulusTooLarge), ParseRecipientKeyError::Unsupported(key_type) => Err(ReadError::UnsupportedKey( filename.to_string(), - UnsupportedKey::Type(key_type), + UnsupportedKey::from_key_type(key_type), )), }, } diff --git a/age/src/ssh.rs b/age/src/ssh.rs index e20c175b..bcbb045d 100644 --- a/age/src/ssh.rs +++ b/age/src/ssh.rs @@ -415,7 +415,8 @@ mod read_ssh { UnencryptedKey::SshEd25519(ssh_key_ed25519.clone(), privkey).into() }), map(string, |key_type| { - UnsupportedKey::Type(String::from_utf8_lossy(key_type).to_string()).into() + UnsupportedKey::from_key_type(String::from_utf8_lossy(key_type).to_string()) + .into() }), )), ) diff --git a/age/src/ssh/identity.rs b/age/src/ssh/identity.rs index a12b54bf..83cd0847 100644 --- a/age/src/ssh/identity.rs +++ b/age/src/ssh/identity.rs @@ -28,7 +28,7 @@ use crate::{ error::DecryptError, fl, util::read::{base64_arg, wrapped_str_while_encoded}, - wlnfl, Callbacks, + wfl, wlnfl, Callbacks, }; /// An SSH private key for decrypting an age file. @@ -136,11 +136,21 @@ pub enum UnsupportedKey { EncryptedPem, /// An encrypted SSH key using a specific cipher. EncryptedSsh(String), + /// An SSH key type we believe to be stored on a hardware security key. + Hardware(String), /// An SSH key type that we do not support. Type(String), } impl UnsupportedKey { + pub(crate) fn from_key_type(key_type: String) -> Self { + if key_type.starts_with("sk-ssh-") { + Self::Hardware(key_type) + } else { + Self::Type(key_type) + } + } + /// Prints details about this unsupported key. pub fn display(&self, f: &mut fmt::Formatter, filename: Option<&str>) -> fmt::Result { if let Some(name) = filename { @@ -148,7 +158,7 @@ impl UnsupportedKey { writeln!(f)?; } match self { - UnsupportedKey::EncryptedPem => wlnfl!( + UnsupportedKey::EncryptedPem => wfl!( f, "ssh-insecure-key-format", change_passphrase = "ssh-keygen -o -p", @@ -159,15 +169,21 @@ impl UnsupportedKey { "https://github.com/str4d/rage/issues/new?title=Support%20OpenSSH%20key%20encryption%20cipher%20{}", cipher, ); - wlnfl!( + wfl!( f, "ssh-unsupported-cipher", cipher = cipher.as_str(), new_issue = new_issue.as_str(), )?; } + UnsupportedKey::Hardware(key_type) => wfl!( + f, + "ssh-unsupported-security-key", + key_type = key_type.as_str(), + age_plugin_yubikey_url = "https://str4d.xyz/age-plugin-yubikey", + )?, UnsupportedKey::Type(key_type) => { - wlnfl!(f, "ssh-unsupported-key-type", key_type = key_type.as_str())? + wfl!(f, "ssh-unsupported-key-type", key_type = key_type.as_str())? } } Ok(()) diff --git a/age/src/ssh/recipient.rs b/age/src/ssh/recipient.rs index 0f796441..959caa6b 100644 --- a/age/src/ssh/recipient.rs +++ b/age/src/ssh/recipient.rs @@ -129,9 +129,9 @@ impl TryFrom for Recipient { )) } } - Identity::Unsupported(UnsupportedKey::Type(key_type)) => { - Err(ParseRecipientKeyError::Unsupported(key_type)) - } + Identity::Unsupported( + UnsupportedKey::Hardware(key_type) | UnsupportedKey::Type(key_type), + ) => Err(ParseRecipientKeyError::Unsupported(key_type)), Identity::Unsupported(_) => Err(ParseRecipientKeyError::Ignore), } }