diff --git a/Docs/Commands/Remove-YubikeyOTP.md b/Docs/Commands/Remove-YubikeyOTP.md index 06a6b1a..7650f17 100644 --- a/Docs/Commands/Remove-YubikeyOTP.md +++ b/Docs/Commands/Remove-YubikeyOTP.md @@ -13,7 +13,7 @@ Remove YubiKey OTP slot. ## SYNTAX ``` -Remove-YubikeyOTP -Slot [-WhatIf] [-Confirm] [] +Remove-YubikeyOTP -Slot [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -42,9 +42,10 @@ Removes the OTP configuration from slot 1 (Short press) Yubikey OTP Slot ```yaml -Type: PSObject +Type: Slot Parameter Sets: (All) Aliases: +Accepted values: None, ShortPress, LongPress Required: True Position: Named diff --git a/Docs/Commands/Request-YubikeyOTPChallange.md b/Docs/Commands/Request-YubikeyOTPChallange.md index 91cfcdf..9cd8c75 100644 --- a/Docs/Commands/Request-YubikeyOTPChallange.md +++ b/Docs/Commands/Request-YubikeyOTPChallange.md @@ -1,4 +1,4 @@ ---- +--- external help file: powershellYK.dll-Help.xml Module Name: powershellYK online version: @@ -13,7 +13,7 @@ Send Challaenge to YubiKey. ## SYNTAX ``` -Request-YubikeyOTPChallange -Slot -Phrase [-YubikeyOTP ] [] +Request-YubikeyOTPChallange -Slot -Phrase [-YubikeyOTP ] [] ``` ## DESCRIPTION @@ -59,9 +59,10 @@ Accept wildcard characters: False Yubikey OTP Slot ```yaml -Type: PSObject +Type: Slot Parameter Sets: (All) Aliases: +Accepted values: None, ShortPress, LongPress Required: True Position: Named diff --git a/Docs/Commands/Set-YubikeyOTP.md b/Docs/Commands/Set-YubikeyOTP.md index bfc4149..2d88420 100644 --- a/Docs/Commands/Set-YubikeyOTP.md +++ b/Docs/Commands/Set-YubikeyOTP.md @@ -14,25 +14,25 @@ Configure OTP slots ### Yubico OTP ``` -Set-YubikeyOTP -Slot [-YubicoOTP] [-PublicID ] [-PrivateID ] [-SecretKey ] +Set-YubikeyOTP -Slot [-YubicoOTP] [-PublicID ] [-PrivateID ] [-SecretKey ] [-Upload] [-WhatIf] [-Confirm] [] ``` ### Static Password ``` -Set-YubikeyOTP -Slot [-StaticPassword] -Password [-KeyboardLayout ] +Set-YubikeyOTP -Slot [-StaticPassword] -Password [-KeyboardLayout ] [-AppendCarriageReturn] [-WhatIf] [-Confirm] [] ``` ### Static Generated Password ``` -Set-YubikeyOTP -Slot [-StaticGeneratedPassword] -PasswordLength +Set-YubikeyOTP -Slot [-StaticGeneratedPassword] -PasswordLength [-KeyboardLayout ] [-AppendCarriageReturn] [-WhatIf] [-Confirm] [] ``` ### ChallengeResponse ``` -Set-YubikeyOTP -Slot [-ChallengeResponse] [-SecretKey ] +Set-YubikeyOTP -Slot [-ChallengeResponse] [-SecretKey ] [-Algorithm ] [-RequireTouch] [-WhatIf] [-Confirm] [] ``` @@ -240,9 +240,10 @@ Accept wildcard characters: False Yubikey OTP Slot. ```yaml -Type: PSObject +Type: Slot Parameter Sets: (All) Aliases: +Accepted values: None, ShortPress, LongPress Required: True Position: Named diff --git a/Module/Cmdlets/OTP/RemoveYubikeyOTP.cs b/Module/Cmdlets/OTP/RemoveYubikeyOTP.cs index 61b76d8..9db3fa5 100644 --- a/Module/Cmdlets/OTP/RemoveYubikeyOTP.cs +++ b/Module/Cmdlets/OTP/RemoveYubikeyOTP.cs @@ -11,12 +11,9 @@ namespace powershellYK.Cmdlets.OTP [Cmdlet(VerbsCommon.Remove, "YubikeyOTP", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] public class RemoveYubikeyOTPCommand : Cmdlet { - [TransformOTPSlot()] - [ValidateOTPSlot()] - [ArgumentCompletions("ShortPress", "LongPress")] + //[ValidateOTPSlot()] [Parameter(Mandatory = true, ValueFromPipeline = false, HelpMessage = "YubiOTP Slot", ParameterSetName = "Remove")] - public PSObject? Slot { get; set; } - private Slot _slot { get; set; } + public Slot Slot { get; set; } protected override void BeginProcessing() { @@ -37,16 +34,18 @@ protected override void BeginProcessing() } protected override void ProcessRecord() { - // Set an internal Slot variable to work with. - if (Slot!.BaseObject is Slot) - { - _slot = (Slot)Slot.BaseObject; - } - if (ShouldProcess($"Yubikey OTP {_slot}", "Set")) + if (ShouldProcess($"This will remove the OTP configuration in slot {Slot.ToString("d")} ({Slot}). Proceed?", $"This will remove the OTP configuration in slot {Slot.ToString("d")} ({Slot}). Proceed?", "Warning")) { using (var otpSession = new OtpSession((YubiKeyDevice)YubiKeyModule._yubikey!)) { - otpSession.DeleteSlot(_slot); + // Check if the slot is configured, if not, Write Warning and continue + if ((Slot == Slot.ShortPress && !otpSession.IsShortPressConfigured) || (Slot == Slot.LongPress && !otpSession.IsLongPressConfigured)) + { + WriteWarning($"Slot {Slot.ToString("d")} ({Slot}) is not configured."); + return; + } + var deleteSlot = otpSession.DeleteSlotConfiguration(Slot); + deleteSlot.Execute(); } } } diff --git a/Module/Cmdlets/OTP/RequestYubikeyOTPChallange.cs b/Module/Cmdlets/OTP/RequestYubikeyOTPChallange.cs index 3f80893..3aac856 100644 --- a/Module/Cmdlets/OTP/RequestYubikeyOTPChallange.cs +++ b/Module/Cmdlets/OTP/RequestYubikeyOTPChallange.cs @@ -17,11 +17,8 @@ namespace powershellYK.Cmdlets.OTP [Cmdlet(VerbsLifecycle.Request, "YubikeyOTPChallange")] public class RequestYubikeyOTPChallangeCommand : Cmdlet { - [TransformOTPSlot()] - [ValidateOTPSlot()] - [ArgumentCompletions("ShortPress", "LongPress")] [Parameter(Mandatory = true, ValueFromPipeline = false, HelpMessage = "YubiOTP Slot")] - public PSObject? Slot; + public Slot Slot; [TransformHexInput()] [Parameter(Mandatory = true, ValueFromPipeline = false, HelpMessage = "Phrase")] public PSObject? Phrase; @@ -50,7 +47,7 @@ protected override void ProcessRecord() { using (var otpSession = new OtpSession((YubiKeyDevice)YubiKeyModule._yubikey!)) { - CalculateChallengeResponse challange = otpSession.CalculateChallengeResponse((Slot)Slot!.BaseObject); + CalculateChallengeResponse challange = otpSession.CalculateChallengeResponse(Slot); challange = challange.UseChallenge((byte[])Phrase!.BaseObject); challange.UseYubiOtp(YubikeyOTP); WriteObject(HexConverter.ByteArrayToString(challange.GetDataBytes().ToArray())); diff --git a/Module/Cmdlets/OTP/SetYubikeyOTP.cs b/Module/Cmdlets/OTP/SetYubikeyOTP.cs index f2194d7..5cc656f 100644 --- a/Module/Cmdlets/OTP/SetYubikeyOTP.cs +++ b/Module/Cmdlets/OTP/SetYubikeyOTP.cs @@ -15,11 +15,8 @@ namespace powershellYK.Cmdlets.OTP [Cmdlet(VerbsCommon.Set, "YubikeyOTP", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] public class SetYubikeyOTPCommand : PSCmdlet { - [TransformOTPSlot()] - [ValidateOTPSlot()] - [ArgumentCompletions("ShortPress", "LongPress")] [Parameter(Mandatory = true, ValueFromPipeline = false, HelpMessage = "Yubikey OTP Slot")] - public PSObject? Slot { get; set; } + public Slot Slot { get; set; } [Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Allows configuration with all defaults", ParameterSetName = "Yubico OTP")] public SwitchParameter YubicoOTP { get; set; } [Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Allows configuration with all defaults", ParameterSetName = "Static Password")] @@ -56,11 +53,6 @@ public class SetYubikeyOTPCommand : PSCmdlet [Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Require Touch", ParameterSetName = "ChallengeResponse")] public SwitchParameter RequireTouch { get; set; } - - private Slot _slot { get; set; } - - - protected override void BeginProcessing() { if (YubiKeyModule._yubikey is null) @@ -82,12 +74,8 @@ protected override void ProcessRecord() { using (var otpSession = new OtpSession((YubiKeyDevice)YubiKeyModule._yubikey!)) { - if (Slot!.BaseObject is Slot) - { - _slot = (Slot)Slot.BaseObject; - } WriteDebug($"Working with {ParameterSetName}"); - if ((_slot == Yubico.YubiKey.Otp.Slot.ShortPress && !otpSession.IsShortPressConfigured) || (_slot == Yubico.YubiKey.Otp.Slot.LongPress && !otpSession.IsLongPressConfigured) || ShouldProcess($"Yubikey OTP {_slot}", "Set")) + if ((Slot == Yubico.YubiKey.Otp.Slot.ShortPress && !otpSession.IsShortPressConfigured) || (Slot == Yubico.YubiKey.Otp.Slot.LongPress && !otpSession.IsLongPressConfigured) || ShouldProcess($"Yubikey OTP {Slot}", "Set")) { switch (ParameterSetName) { @@ -95,7 +83,7 @@ protected override void ProcessRecord() Memory _publicID = new Memory(new byte[6]); Memory _privateID = new Memory(new byte[6]); Memory _secretKey = new Memory(new byte[16]); - ConfigureYubicoOtp configureyubicoOtp = otpSession.ConfigureYubicoOtp(_slot); + ConfigureYubicoOtp configureyubicoOtp = otpSession.ConfigureYubicoOtp(Slot); int? serial = YubiKeyModule._yubikey!.SerialNumber; if (PublicID is null) { @@ -139,7 +127,7 @@ protected override void ProcessRecord() break; case "Static Password": - ConfigureStaticPassword staticpassword = otpSession.ConfigureStaticPassword(_slot); + ConfigureStaticPassword staticpassword = otpSession.ConfigureStaticPassword(Slot); staticpassword = staticpassword.WithKeyboard(KeyboardLayout); staticpassword = staticpassword.SetPassword((Marshal.PtrToStringUni(Marshal.SecureStringToGlobalAllocUnicode(Password!))!).AsMemory()); if (AppendCarriageReturn.IsPresent) @@ -149,7 +137,7 @@ protected override void ProcessRecord() staticpassword.Execute(); break; case "Static Generated Password": - ConfigureStaticPassword staticgenpassword = otpSession.ConfigureStaticPassword(_slot); + ConfigureStaticPassword staticgenpassword = otpSession.ConfigureStaticPassword(Slot); Memory generatedPassword = new Memory(new char[PasswordLength]); staticgenpassword = staticgenpassword.WithKeyboard(KeyboardLayout); staticgenpassword = staticgenpassword.GeneratePassword(generatedPassword); @@ -161,7 +149,7 @@ protected override void ProcessRecord() break; case "ChallengeResponse": Memory _CRsecretKey = new Memory(new byte[20]); - ConfigureChallengeResponse configureCR = otpSession.ConfigureChallengeResponse(_slot); + ConfigureChallengeResponse configureCR = otpSession.ConfigureChallengeResponse(Slot); if (SecretKey is null) { configureCR = configureCR.GenerateKey(_CRsecretKey); diff --git a/Module/support/transform/TransformOTPSlot.cs b/Module/support/transform/TransformOTPSlot.cs deleted file mode 100644 index 87575b1..0000000 --- a/Module/support/transform/TransformOTPSlot.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Management.Automation; -using Yubico.YubiKey.Otp; - - -namespace powershellYK.support.transform -{ - class TransformOTPSlot : System.Management.Automation.ArgumentTransformationAttribute - { - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) - { - if (inputData is string) - { - if ((string)inputData == "1" || String.Equals((string)inputData, "ShortPress", StringComparison.OrdinalIgnoreCase) || String.Equals((string)inputData, "Short", StringComparison.OrdinalIgnoreCase) || (string)inputData == "[Yubico.YubiKey.Otp.Slot]::ShortPress") - { - return Yubico.YubiKey.Otp.Slot.ShortPress; - } - else if ((string)inputData == "2" || String.Equals((string)inputData, "LongPress", StringComparison.OrdinalIgnoreCase) || String.Equals((string)inputData, "Long", StringComparison.OrdinalIgnoreCase) || (string)inputData == "[Yubico.YubiKey.Otp.Slot]::LongPress") - { - return Yubico.YubiKey.Otp.Slot.LongPress; - } - } - else if (inputData is int) - { - if ((int)inputData == 1) - { - return Yubico.YubiKey.Otp.Slot.ShortPress; - } - else if ((int)inputData == 2) - { - return Yubico.YubiKey.Otp.Slot.LongPress; - } - } - return inputData; - } - } -} diff --git a/Module/support/transform/TransformPivSlot.cs b/Module/support/transform/TransformPivSlot.cs deleted file mode 100644 index 9a785ff..0000000 --- a/Module/support/transform/TransformPivSlot.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Management.Automation; -using Yubico.YubiKey.Piv; - - -namespace powershellYK.support.transform -{ - class TransformPivSlot : System.Management.Automation.ArgumentTransformationAttribute - { - private static byte[] _pivSlots = new byte[] { 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x9A, 0x9C, 0x9D, 0x9E, 0x9F }; - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) - { - if (inputData is string) - { - if (String.Equals((string)inputData, "PIV Authentication", StringComparison.OrdinalIgnoreCase) || String.Equals((string)inputData, "Authentication", StringComparison.OrdinalIgnoreCase)) - { - return (byte)0x9a; - } - else if (String.Equals((string)inputData, "Digital Signature", StringComparison.OrdinalIgnoreCase) || String.Equals((string)inputData, "Signature", StringComparison.OrdinalIgnoreCase)) - { - return (byte)0x9c; - } - else if (String.Equals((string)inputData, "Key Management", StringComparison.OrdinalIgnoreCase)) - { - return (byte)0x9d; - } - else if (String.Equals((string)inputData, "Card Authentication", StringComparison.OrdinalIgnoreCase) || String.Equals((string)inputData, "Card", StringComparison.OrdinalIgnoreCase)) - { - return (byte)0x9e; - } - else - { - foreach (byte slot in _pivSlots) - { - if (String.Equals((string)inputData, slot.ToString(), StringComparison.OrdinalIgnoreCase) || String.Equals((string)inputData, $"0x{slot:X2}", StringComparison.OrdinalIgnoreCase)) - { - return slot; - } - } - } - } - else if (inputData is int) - { - if ((int)inputData == 0x9a || ((int)inputData == 0x9c && (int)inputData <= 0x9f) || ((int)inputData >= 0x82 && (int)inputData <= 0x95)) - { - return (byte)(int)inputData; - } - } - else if (inputData is byte) - { - if ((byte)inputData == 0x9a || ((byte)inputData <= 0x9c && (byte)inputData <= 0x9f) || ((byte)inputData >= 0x82 && (byte)inputData <= 0x95)) - { - return (byte)inputData; - } - } - else if (inputData is PSObject) - { - if (((PSObject)inputData).BaseObject is Int32) - { - - foreach (byte slot in _pivSlots) - { - if ((Int32)(((PSObject)inputData).BaseObject) == slot) - { - return slot; - } - } - } - } - throw new ArgumentException($"Unable to parse slot for input data"); - } - } -} diff --git a/Module/support/validators/ValidateOTPSlot.cs b/Module/support/validators/ValidateOTPSlot.cs deleted file mode 100644 index ca2b654..0000000 --- a/Module/support/validators/ValidateOTPSlot.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Management.Automation; -using System.Security; -using Yubico.YubiKey.Otp; - -namespace powershellYK.support.validators -{ - class ValidateOTPSlot : ValidateArgumentsAttribute - { - protected override void Validate(object arguments, EngineIntrinsics engineIntrinsics) - { - - if (((PSObject)arguments).BaseObject is Slot) - { - return; - } - throw new ArgumentException("Invalid formatted slot"); - } - } -} diff --git a/Module/types/PIVSlot.cs b/Module/types/PIVSlot.cs index f502b40..2ba970d 100644 --- a/Module/types/PIVSlot.cs +++ b/Module/types/PIVSlot.cs @@ -1,7 +1,4 @@ -using powershellYK.support; -using System.Management.Automation; -using System.Security.Cryptography.X509Certificates; -using Yubico.YubiKey.Piv; +using Yubico.YubiKey.Piv; namespace powershellYK.PIV { diff --git a/Pester/030-Types-PIVSlot.tests.ps1 b/Pester/030-Types-PIVSlot.tests.ps1 new file mode 100644 index 0000000..59611a3 --- /dev/null +++ b/Pester/030-Types-PIVSlot.tests.ps1 @@ -0,0 +1,51 @@ +Describe "030 Test PIVSlot" -Tag "Without-YubiKey" { + AfterEach -Scriptblock { + Remove-Variable -Name Test -ErrorAction SilentlyContinue + } + It "Test create new slot without information" { + {[powershellYK.PIV.PIVSlot]::new()} | Should -Throw # PIVSlot cannot be null + } + It "Test create new slot with incorrect information" { + {[powershellYK.PIV.PIVSlot]::new(0x01)} | Should -Throw # PIVSlot must be a YubiKey PIV slot + {[powershellYK.PIV.PIVSlot]::new(0xff)} | Should -Throw # PIVSlot must be a YubiKey PIV slot + {[powershellYK.PIV.PIVSlot]::new(0x79)} | Should -Throw # PIVSlot must be a YubiKey PIV slot + } + It "Verify output type" { + [powershellYK.PIV.PIVSlot]::new(0x9a) | Should -BeOfType 'powershellYK.PIV.PIVSlot' + } + It "Test create slot with [Integer]" { + {[powershellYK.PIV.PIVSlot]::new([int]0x9a)} | Should -Not -Throw + {[powershellYK.PIV.PIVSlot]::new([int]0xffff)} | Should -Throw + } + It "Test create slot with [Byte]" { + {[powershellYK.PIV.PIVSlot]::new([Byte]0x9a)} | Should -Not -Throw + } + It "Test create slot with [String]" { + {[powershellYK.PIV.PIVSlot]::new([string]"0x9a")} | Should -Not -Throw + {[powershellYK.PIV.PIVSlot]::new([string]"0x9A")} | Should -Not -Throw + {[powershellYK.PIV.PIVSlot]::new([string]"PIV Authentication")} | Should -Not -Throw + } + It "Test create slot with [Yubico.YubiKey.Piv.PivSlot]" { + {[powershellYK.PIV.PIVSlot]::new([string]"0x9a")} | Should -Not -Throw + {[powershellYK.PIV.PIVSlot]::new([string]"0x9A")} | Should -Not -Throw + {[powershellYK.PIV.PIVSlot]::new([string]"PIV Authentication")} | Should -Not -Throw + } + It "Test ToString()" { + {[powershellYK.PIV.PIVSlot]::new(0x9a).ToString()} | Should -Not -Throw + [powershellYK.PIV.PIVSlot]::new(0x9a).ToString() | Should -Be "0x9A" + } + It "Test ToNamedSlot()" { + [powershellYK.PIV.PIVSlot]::new(0x9a).ToNamedSlot() | Should -Be "PIV Authentication" + } + It "Test ToByte()" { + [powershellYK.PIV.PIVSlot]::new(0x9a).ToByte() | Should -BeOfType 'Byte' + } + +#Verify Operators + It "Test Explicit Cast to [Byte]" { + {[byte][powershellYK.PIV.PIVSlot]::new(0x9a)} | Should -Not -Throw + } + It "Test Explicit Cast to [Int]" { + {[int][powershellYK.PIV.PIVSlot]::new(0x9a)} | Should -Not -Throw + } +}