Merge pull request #63 from str4d/skippable-yubikeys
Enable users to skip YubiKeys at plugging-in time
This commit is contained in:
Generated
+77
-22
@@ -20,27 +20,27 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "age-core"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70afa630ef12a4fc666277713efbe6da2bc87bb3f3af0f1149415b701362c615"
|
||||
source = "git+https://github.com/str4d/rage.git?rev=fdb518c6d802a3618b47d959d56af6e60c668627#fdb518c6d802a3618b47d959d56af6e60c668627"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chacha20poly1305",
|
||||
"cookie-factory",
|
||||
"hkdf",
|
||||
"io_tee",
|
||||
"nom",
|
||||
"rand",
|
||||
"secrecy",
|
||||
"sha2",
|
||||
"sha2 0.10.2",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "age-plugin"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74049e94d591e0b96128880bb9dcbc5f27432b3089725524d020616e1dc36e2b"
|
||||
source = "git+https://github.com/str4d/rage.git?rev=fdb518c6d802a3618b47d959d56af6e60c668627#fdb518c6d802a3618b47d959d56af6e60c668627"
|
||||
dependencies = [
|
||||
"age-core",
|
||||
"base64",
|
||||
"bech32",
|
||||
"chrono",
|
||||
]
|
||||
@@ -68,7 +68,7 @@ dependencies = [
|
||||
"pcsc",
|
||||
"rand",
|
||||
"rust-embed",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"which",
|
||||
"x509",
|
||||
"x509-parser",
|
||||
@@ -149,6 +149,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -265,6 +274,16 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.11.1"
|
||||
@@ -357,6 +376,17 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.2",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.12.4"
|
||||
@@ -365,7 +395,7 @@ checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372"
|
||||
dependencies = [
|
||||
"der",
|
||||
"elliptic-curve",
|
||||
"hmac",
|
||||
"hmac 0.11.0",
|
||||
"signature",
|
||||
]
|
||||
|
||||
@@ -563,12 +593,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.11.0"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
|
||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -578,7 +607,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
"digest",
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -685,6 +723,12 @@ dependencies = [
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io_tee"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -917,7 +961,7 @@ checksum = "d053368e1bae4c8a672953397bd1bd7183dde1c72b0b7612a15719173148d186"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1156,7 +1200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e05c2603e2823634ab331437001b411b9ed11660fbc4066f3908c84a9439260d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
"digest 0.9.0",
|
||||
"lazy_static",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
@@ -1199,7 +1243,7 @@ version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -1274,10 +1318,10 @@ version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
@@ -1287,20 +1331,31 @@ version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2807892cfa58e081aa1f1111391c7a0649d4fa127a4ffbe34bcbfb35a1171a4"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.9.0",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
@@ -1658,7 +1713,7 @@ dependencies = [
|
||||
"der-parser",
|
||||
"des",
|
||||
"elliptic-curve",
|
||||
"hmac",
|
||||
"hmac 0.11.0",
|
||||
"log",
|
||||
"nom",
|
||||
"num-bigint-dig",
|
||||
@@ -1672,7 +1727,7 @@ dependencies = [
|
||||
"rsa",
|
||||
"secrecy",
|
||||
"sha-1",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"subtle",
|
||||
"subtle-encoding",
|
||||
"uuid",
|
||||
|
||||
@@ -50,3 +50,7 @@ rust-embed = "6"
|
||||
[dev-dependencies]
|
||||
flate2 = "1"
|
||||
man = "0.3"
|
||||
|
||||
[patch.crates-io]
|
||||
age-core = { git = "https://github.com/str4d/rage.git", rev = "fdb518c6d802a3618b47d959d56af6e60c668627" }
|
||||
age-plugin = { git = "https://github.com/str4d/rage.git", rev = "fdb518c6d802a3618b47d959d56af6e60c668627" }
|
||||
|
||||
@@ -151,6 +151,9 @@ plugin-err-invalid-stanza = Invalid {-yubikey} stanza
|
||||
plugin-err-decryption-failed = Failed to decrypt {-yubikey} stanza
|
||||
|
||||
plugin-insert-yk = Please insert {-yubikey} with serial {$yubikey_serial}
|
||||
plugin-yk-is-plugged-in = {-yubikey} is plugged in
|
||||
plugin-skip-this-yk = Skip this {-yubikey}
|
||||
plugin-insert-yk-retry = Could not open {-yubikey}. Please insert {-yubikey} with serial {$yubikey_serial}
|
||||
plugin-err-yk-not-found = Could not find {-yubikey} with serial {$yubikey_serial}
|
||||
plugin-err-yk-opening = Could not open {-yubikey} with serial {$yubikey_serial}
|
||||
plugin-err-yk-timed-out = Timed out while waiting for {-yubikey} with serial {$yubikey_serial} to be inserted
|
||||
|
||||
+98
-40
@@ -199,7 +199,7 @@ pub struct Stub {
|
||||
pub(crate) serial: Serial,
|
||||
pub(crate) slot: RetiredSlotId,
|
||||
pub(crate) tag: [u8; TAG_BYTES],
|
||||
identity_index: usize,
|
||||
pub(crate) identity_index: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for Stub {
|
||||
@@ -260,38 +260,53 @@ impl Stub {
|
||||
self.tag == line.tag
|
||||
}
|
||||
|
||||
/// Returns:
|
||||
/// - `Ok(Ok(Some(connection)))` if we successfully connected to this YubiKey.
|
||||
/// - `Ok(Ok(None))` if the user told us to skip this YubiKey.
|
||||
/// - `Ok(Err(_))` if we encountered an error while trying to connect to the YubiKey.
|
||||
/// - `Err(_)` on communication errors with the age client.
|
||||
pub(crate) fn connect<E>(
|
||||
&self,
|
||||
callbacks: &mut dyn Callbacks<E>,
|
||||
) -> io::Result<Result<Connection, identity::Error>> {
|
||||
) -> io::Result<Result<Option<Connection>, identity::Error>> {
|
||||
let mut yubikey = match YubiKey::open_by_serial(self.serial) {
|
||||
Ok(yk) => yk,
|
||||
Err(yubikey::Error::NotFound) => {
|
||||
if callbacks
|
||||
.message(&i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-insert-yk",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
))?
|
||||
.is_err()
|
||||
{
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-not-found",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}));
|
||||
}
|
||||
let mut message = i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-insert-yk",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
);
|
||||
|
||||
// Start a 15-second timer waiting for the YubiKey to be inserted
|
||||
let start = SystemTime::now();
|
||||
loop {
|
||||
match YubiKey::open_by_serial(self.serial) {
|
||||
Ok(yubikey) => break yubikey,
|
||||
Err(yubikey::Error::NotFound) => (),
|
||||
Err(_) => {
|
||||
// If the `confirm` command is available, we loop until either the YubiKey
|
||||
// we want is inserted, or the used explicitly skips.
|
||||
let yubikey = loop {
|
||||
match callbacks.confirm(
|
||||
&message,
|
||||
&fl!("plugin-yk-is-plugged-in"),
|
||||
Some(&fl!("plugin-skip-this-yk")),
|
||||
)? {
|
||||
// `confirm` command is not available.
|
||||
Err(age_core::plugin::Error::Unsupported) => break None,
|
||||
// User told us to skip this key.
|
||||
Ok(false) => return Ok(Ok(None)),
|
||||
// User said they plugged it in; try it.
|
||||
Ok(true) => match YubiKey::open_by_serial(self.serial) {
|
||||
Ok(yubikey) => break Some(yubikey),
|
||||
Err(yubikey::Error::NotFound) => (),
|
||||
Err(_) => {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-opening",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}));
|
||||
}
|
||||
},
|
||||
// We can't communicate with the user.
|
||||
Err(age_core::plugin::Error::Fail) => {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
@@ -299,22 +314,65 @@ impl Stub {
|
||||
"plugin-err-yk-opening",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}));
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
match SystemTime::now().duration_since(start) {
|
||||
Ok(end) if end >= FIFTEEN_SECONDS => {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-timed-out",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}))
|
||||
// We're going to loop around, meaning that the first attempt failed.
|
||||
// Change the message to indicate this to the user.
|
||||
message = i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-insert-yk-retry",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
);
|
||||
};
|
||||
|
||||
if let Some(yk) = yubikey {
|
||||
yk
|
||||
} else {
|
||||
// `confirm` is not available; fall back to `message` with a timeout.
|
||||
if callbacks.message(&message)?.is_err() {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-not-found",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
// Start a 15-second timer waiting for the YubiKey to be inserted
|
||||
let start = SystemTime::now();
|
||||
loop {
|
||||
match YubiKey::open_by_serial(self.serial) {
|
||||
Ok(yubikey) => break yubikey,
|
||||
Err(yubikey::Error::NotFound) => (),
|
||||
Err(_) => {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-opening",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
match SystemTime::now().duration_since(start) {
|
||||
Ok(end) if end >= FIFTEEN_SECONDS => {
|
||||
return Ok(Err(identity::Error::Identity {
|
||||
index: self.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-timed-out",
|
||||
yubikey_serial = self.serial.to_string(),
|
||||
),
|
||||
}))
|
||||
}
|
||||
_ => sleep(ONE_SECOND),
|
||||
}
|
||||
_ => sleep(ONE_SECOND),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,7 +406,7 @@ impl Stub {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Ok(Connection {
|
||||
Ok(Ok(Some(Connection {
|
||||
yubikey,
|
||||
cert,
|
||||
pk,
|
||||
@@ -357,7 +415,7 @@ impl Stub {
|
||||
identity_index: self.identity_index,
|
||||
cached_metadata: None,
|
||||
last_touch: None,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-2
@@ -68,7 +68,15 @@ impl RecipientPluginV1 for RecipientPlugin {
|
||||
let mut yk_errors = vec![];
|
||||
for stub in &self.yubikeys {
|
||||
match stub.connect(&mut callbacks)? {
|
||||
Ok(conn) => yk_recipients.push(conn.recipient().clone()),
|
||||
Ok(Some(conn)) => yk_recipients.push(conn.recipient().clone()),
|
||||
Ok(None) => yk_errors.push(recipient::Error::Identity {
|
||||
index: stub.identity_index,
|
||||
message: i18n_embed_fl::fl!(
|
||||
crate::LANGUAGE_LOADER,
|
||||
"plugin-err-yk-opening",
|
||||
yubikey_serial = stub.serial.to_string(),
|
||||
),
|
||||
}),
|
||||
Err(e) => yk_errors.push(match e {
|
||||
identity::Error::Identity { index, message } => {
|
||||
recipient::Error::Identity { index, message }
|
||||
@@ -203,7 +211,11 @@ impl IdentityPluginV1 for IdentityPlugin {
|
||||
|
||||
for (stub, files) in candidate_stanzas.iter() {
|
||||
let mut conn = match stub.connect(&mut callbacks)? {
|
||||
Ok(conn) => conn,
|
||||
// The user skipped this YubiKey.
|
||||
Ok(None) => continue,
|
||||
// We connected to this YubiKey.
|
||||
Ok(Some(conn)) => conn,
|
||||
// We failed to connect to this YubiKey.
|
||||
Err(e) => {
|
||||
callbacks.error(e)?.unwrap();
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user