Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to connect OATH Cmdlet #120

Merged
merged 6 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,5 @@ MigrationBackup/

# Fody - auto-generated XML schema
FodyWeavers.xsd
/.vscode/launch.json
/powershellYK.psd1
17 changes: 15 additions & 2 deletions Module/Cmdlets/FIDO2/ResetYubikeyFIDO2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
using powershellYK.Exceptions;
using System.Diagnostics;


namespace powershellYK.Cmdlets.Fido
{
[Cmdlet(VerbsCommon.Reset, "YubiKeyFIDO2", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
public class ResetYubikeyFIDO2Cmdlet : PSCmdlet
{
[Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Force reset of the FIDO2 applet")]
public SwitchParameter Force { get; set; } = false;
private bool _yubiKeyRemoved = false;
private bool _yubiKeyArrived = false;

Expand All @@ -38,7 +41,17 @@ protected override void BeginProcessing()

protected override void ProcessRecord()
{
if (ShouldProcess("This will delete all FIDO credentials, including FIDO U2F credentials, and restore factory settings. Proceed?", "This will delete all FIDO credentials, including FIDO U2F credentials, and restore factory settings. Proceed?", "WARNING!"))
// Add Bio MPE check
// TODO: @virot can/should we use our helper instead here?
var formFactor = YubiKeyModule._yubikey!.FormFactor;
var capabilities = YubiKeyModule._yubikey.AvailableUsbCapabilities;

if ((formFactor == FormFactor.UsbABiometricKeychain || formFactor == FormFactor.UsbCBiometricKeychain) && capabilities.HasFlag(YubiKeyCapabilities.Piv))
{
throw new Exception("YubiKey Bio Multi-Protocol Edition (MPE) detected. Reset using 'Reset-YubiKey' instead!");
}

if (Force || ShouldProcess("This will delete all FIDO credentials, including FIDO U2F credentials, and restore factory settings. Proceed?", "This will delete all FIDO credentials, including FIDO U2F credentials, and restore factory settings. Proceed?", "WARNING!"))
{
Console.WriteLine("Remove and re-insert the YubiKey to perform the reset...");

Expand Down Expand Up @@ -106,7 +119,7 @@ protected override void ProcessRecord()
}

YubiKeyModule._fido2PIN = null;
WriteInformation("YubiKey FIDO applet successfully reset.", new string[] { "FIDO2", "Info" });
WriteInformation("YubiKey FIDO applet successfully reset.", new string[] { "FIDO2", "Reset" });
}
}
}
Expand Down
122 changes: 106 additions & 16 deletions Module/Cmdlets/OATH/ConnectYubikeyOATH.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,90 @@
using System.Management.Automation; // Windows PowerShell namespace.
// Summary:
// Connects to the OATH application on a YubiKey, handling password authentication if required.
// If no YubiKey is selected, it will automatically call Connect-Yubikey first.
//
// Examples:
// # Basic connection (will prompt for password if needed)
// Connect-YubiKeyOATH
//
// # Connect with password
// $securePassword = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
// Connect-YubiKeyOATH -Password $securePassword
//

using System.Management.Automation; // Windows PowerShell namespace.
using Yubico.YubiKey;
using Yubico.YubiKey.Oath;
using powershellYK.support;
using System.Data.Common;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using powershellYK.support.validators;
using System.Security;
using System.Collections.ObjectModel;

namespace powershellYK.Cmdlets.OATH
{
[Cmdlet(VerbsCommunications.Connect, "YubiKeyOATH", DefaultParameterSetName = "Password")]

public class ConnectYubikeyOATHCommand : Cmdlet
public class ConnectYubikeyOATHCommand : PSCmdlet, IDynamicParameters
{
[ValidateYubikeyPassword(0, 255)]
[Parameter(Mandatory = true, ValueFromPipeline = false, HelpMessage = "Password", ParameterSetName = "Password")]
public SecureString? Password;
public object GetDynamicParameters()
{
Collection<Attribute> passwordAttributes;
if (YubiKeyModule._yubikey is not null)
{
using (var oathSession = new OathSession((YubiKeyDevice)YubiKeyModule._yubikey!))
{
if (oathSession.IsPasswordProtected)
{
passwordAttributes = new Collection<Attribute>() {
new ParameterAttribute() { Mandatory = true, HelpMessage = "Password provided as a SecureString.", ParameterSetName = "Password"},
new ValidateYubikeyPassword(0, 255)
};
}
else
{
passwordAttributes = new Collection<Attribute>() {
new ParameterAttribute() { Mandatory = false, HelpMessage = "Password provided as a SecureString.", ParameterSetName = "Password"},
new ValidateYubikeyPassword(0, 255)
};
}
}
}
else
{
// Try to conect to any YubiKey that is inserted.
try
{
var yubiKey = YubiKeyDevice.FindAll().First();
using (var oathSession = new OathSession(yubiKey))
{
if (oathSession.IsPasswordProtected)
{
passwordAttributes = new Collection<Attribute>() {
new ParameterAttribute() { Mandatory = true, HelpMessage = "Password provided as a SecureString.", ParameterSetName = "Password"},
new ValidateYubikeyPassword(0, 255)
};
}
else
{
passwordAttributes = new Collection<Attribute>() {
new ParameterAttribute() { Mandatory = false, HelpMessage = "Password provided as a SecureString.", ParameterSetName = "Password"},
new ValidateYubikeyPassword(0, 255)
};
}
}
}
catch
{
passwordAttributes = new Collection<Attribute>()
{
new ParameterAttribute() { Mandatory = true, HelpMessage = "Password provided as a SecureString.", ParameterSetName = "Password"},
new ValidateYubikeyPassword(0, 255)
};
}
}
var runtimeDefinedParameterDictionary = new RuntimeDefinedParameterDictionary();
var runtimeDefinedPassword = new RuntimeDefinedParameter("Password", typeof(SecureString), passwordAttributes);
runtimeDefinedParameterDictionary.Add("Password", runtimeDefinedPassword);
return runtimeDefinedParameterDictionary;
}

protected override void BeginProcessing()
{
if (YubiKeyModule._yubikey is null)
Expand All @@ -27,18 +95,40 @@ protected override void BeginProcessing()
WriteDebug($"Successfully connected.");
}
}

protected override void ProcessRecord()
{
using (var oathSession = new OathSession((YubiKeyDevice)YubiKeyModule._yubikey!))
try
{
oathSession.KeyCollector = YubiKeyModule._KeyCollector.YKKeyCollectorDelegate;

if (oathSession.IsPasswordProtected && Password is not null && Password.Length >= 1)
using (var oathSession = new OathSession((YubiKeyDevice)YubiKeyModule._yubikey!))
{
YubiKeyModule._OATHPassword = Password;
oathSession.VerifyPassword();
oathSession.KeyCollector = YubiKeyModule._KeyCollector.YKKeyCollectorDelegate;

// Check if the OATH applet is password protected
if (oathSession.IsPasswordProtected)
{
try
{
YubiKeyModule._OATHPassword = (SecureString)this.MyInvocation.BoundParameters["Password"];
oathSession.VerifyPassword();
WriteDebug("Successfully authenticated and connected to OATH applet.");
}
catch (Exception ex)
{
YubiKeyModule._OATHPassword = null;
throw new SecurityException("Failed to authenticate with OATH applet. Please check the password and try again.", ex);
}
}
else
{
WriteDebug("Successfully connected to OATH applet.");
}
}
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "OATHSessionError", ErrorCategory.OperationStopped, null));
}
}
}
}
50 changes: 0 additions & 50 deletions Module/Cmdlets/OATH/ProtectYubikeyOATH.cs

This file was deleted.

20 changes: 14 additions & 6 deletions Module/Cmdlets/OATH/ResetYubikeyOATH.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
namespace powershellYK.Cmdlets.OATH
{
[Cmdlet(VerbsCommon.Reset, "YubiKeyOATH", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]

public class ResetYubikeyOATH2Command : Cmdlet
public class ResetYubikeyOATHCommand : Cmdlet
{
protected override void BeginProcessing()
{
Expand All @@ -27,14 +26,23 @@ protected override void BeginProcessing()

protected override void ProcessRecord()
{
if (ShouldProcess($"Yubikey OATH", "Reset"))
if (ShouldProcess("This will delete all OATH credentials, and restore factory settings. Proceed?", "This will delete all OATH credentials, and restore factory settings. Proceed?", "WARNING!"))
{
using (var oathSession = new OathSession((YubiKeyDevice)YubiKeyModule._yubikey!))
try
{
using (var oathSession = new OathSession((YubiKeyDevice)YubiKeyModule._yubikey!))
{
oathSession.KeyCollector = YubiKeyModule._KeyCollector.YKKeyCollectorDelegate;
oathSession.ResetApplication();
WriteInformation("YubiKey OATH applet successfully reset.", new string[] { "OATH", "Reset" });
}
}
catch (Exception ex)
{
oathSession.KeyCollector = YubiKeyModule._KeyCollector.YKKeyCollectorDelegate;
oathSession.ResetApplication();
WriteError(new ErrorRecord(ex, "OATHResetError", ErrorCategory.OperationStopped, null));
}
}
}
}
}

Loading
Loading