diff --git a/src/mgm.rs b/src/mgm.rs index 44fd3cc..853a15b 100644 --- a/src/mgm.rs +++ b/src/mgm.rs @@ -183,19 +183,84 @@ impl MgmKey { MgmKey::from_bytes(item) } - /// Set the management key (MGM) + /// Resets the management key for the given YubiKey to the default value. + /// + /// This will wipe any metadata related to derived and PIN-protected management keys. #[cfg(feature = "untested")] - pub fn set(&self, yubikey: &mut YubiKey, touch: Option) -> Result<(), Error> { - let txn = yubikey.begin_transaction()?; - txn.set_mgm_key(&self, touch) + pub fn set_default(yubikey: &mut YubiKey) -> Result<(), Error> { + MgmKey::default().set_manual(yubikey, false) } - /// Set protected management key (MGM) + /// Configures the given YubiKey to use this management key. + /// + /// The management key must be stored by the user, and provided when performing key + /// management operations. + /// + /// This will wipe any metadata related to derived and PIN-protected management keys. + #[cfg(feature = "untested")] + pub fn set_manual(&self, yubikey: &mut YubiKey, require_touch: bool) -> Result<(), Error> { + let txn = yubikey.begin_transaction()?; + + txn.set_mgm_key(&self, require_touch).map_err(|e| { + // Log a warning, since the device mgm key is corrupt or we're in a state + // where we can't set the mgm key. + error!("could not set new derived mgm key, err = {}", e); + e + })?; + + // After this point, we've set the mgm key, so the function should succeed, + // regardless of being able to set the metadata. + + if let Ok(mut admin_data) = AdminData::read(&txn) { + // Clear the protected mgm key bit. + if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) { + let mut flags_1 = [0u8; 1]; + if item.len() == flags_1.len() { + flags_1.copy_from_slice(item); + flags_1[0] &= !ADMIN_FLAGS_1_PROTECTED_MGM; + + if let Err(e) = admin_data.set_item(TAG_ADMIN_FLAGS_1, &flags_1) { + error!("could not set admin flags item, err = {}", e); + } + } else { + error!( + "admin data flags are an incorrect size: {} (expected {})", + item.len(), + flags_1.len() + ); + } + } + + // Remove any existing salt for a derived mgm key. + if let Err(e) = admin_data.set_item(TAG_ADMIN_SALT, &[]) { + error!("could not unset derived mgm salt (err = {})", e) + } + + if let Err(e) = admin_data.write(&txn) { + error!("could not write admin data, err = {}", e); + } + } + + // Clear any prior mgm key from protected data. + if let Ok(mut protected_data) = ProtectedData::read(&txn) { + if let Err(e) = protected_data.set_item(TAG_PROTECTED_MGM, &[]) { + error!("could not clear protected mgm item, err = {:?}", e); + } else if let Err(e) = protected_data.write(&txn) { + error!("could not write protected data, err = {:?}", e); + } + } + + Ok(()) + } + + /// Configures the given YubiKey to use this as a PIN-protected management key. + /// + /// This enables key management operations to be performed with access to the PIN. #[cfg(feature = "untested")] pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<(), Error> { let txn = yubikey.begin_transaction()?; - txn.set_mgm_key(self, None).map_err(|e| { + txn.set_mgm_key(self, false).map_err(|e| { // log a warning, since the device mgm key is corrupt or we're in // a state where we can't set the mgm key error!("could not set new derived mgm key, err = {}", e); diff --git a/src/transaction.rs b/src/transaction.rs index 0509b0d..2708688 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -229,14 +229,8 @@ impl<'tx> Transaction<'tx> { /// Set the management key (MGM). #[cfg(feature = "untested")] - pub fn set_mgm_key(&self, new_key: &MgmKey, touch: Option) -> Result<(), Error> { - let p2 = match touch.unwrap_or_default() { - 0 => 0xff, - 1 => 0xfe, - _ => { - return Err(Error::GenericError); - } - }; + pub fn set_mgm_key(&self, new_key: &MgmKey, require_touch: bool) -> Result<(), Error> { + let p2 = if require_touch { 0xfe } else { 0xff }; let mut data = [0u8; DES_LEN_3DES + 3]; data[0] = ALGO_3DES; diff --git a/tests/integration.rs b/tests/integration.rs index b6c1223..2fd4aff 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -114,10 +114,11 @@ fn test_verify_pin() { #[cfg(feature = "untested")] #[test] #[ignore] -fn test_protected_mgmkey() { +fn test_set_mgmkey() { let mut yubikey = YUBIKEY.lock().unwrap(); assert!(yubikey.verify_pin(b"123456").is_ok()); + assert!(MgmKey::get_protected(&mut yubikey).is_err()); assert!(yubikey.authenticate(MgmKey::default()).is_ok()); // Set a protected management key. @@ -129,10 +130,19 @@ fn test_protected_mgmkey() { assert!(yubikey.authenticate(MgmKey::default()).is_err()); assert!(yubikey.authenticate(protected.clone()).is_ok()); + // Set a manual management key. + let manual = MgmKey::generate().unwrap(); + assert!(manual.set_manual(&mut yubikey, false).is_ok()); + assert!(MgmKey::get_protected(&mut yubikey).is_err()); + assert!(yubikey.authenticate(MgmKey::default()).is_err()); + assert!(yubikey.authenticate(protected.clone()).is_err()); + assert!(yubikey.authenticate(manual.clone()).is_ok()); + // Set back to the default management key. - // TODO: This does not clear the previous key from the protected metadata. - assert!(MgmKey::default().set(&mut yubikey, None).is_ok()); + assert!(MgmKey::set_default(&mut yubikey).is_ok()); + assert!(MgmKey::get_protected(&mut yubikey).is_err()); assert!(yubikey.authenticate(protected).is_err()); + assert!(yubikey.authenticate(manual).is_err()); assert!(yubikey.authenticate(MgmKey::default()).is_ok()); }