diff --git a/.gitignore b/.gitignore index 6325807..f4afde3 100644 --- a/.gitignore +++ b/.gitignore @@ -362,4 +362,5 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd /.vscode/launch.json -/powershellYK.psd1 \ No newline at end of file +/powershellYK.psd1 +/.cursorrules diff --git a/Module/Cmdlets/OTP/SetYubikeyOTP.cs b/Module/Cmdlets/OTP/SetYubikeyOTP.cs index 2f60815..188e4b0 100644 --- a/Module/Cmdlets/OTP/SetYubikeyOTP.cs +++ b/Module/Cmdlets/OTP/SetYubikeyOTP.cs @@ -46,6 +46,10 @@ /// Set-YubiKeyOTP -Slot ShortPress -HOTP -Base32Secret "QRFJ7DTIVASL3PNYXWFIQAQN5RKUJD4U" -AppendCarriageReturn /// /// .EXAMPLE +/// # Configure HOTP with hex encoded secret and carriage return +/// Set-YubiKeyOTP -Slot ShortPress -HOTP -HexSecret "0102030405060708090a0b0c0d0e0f1011121314" -AppendCarriageReturn +/// +/// .EXAMPLE /// # Configure HOTP with TAB before OTP code for easier form navigation /// Set-YubiKeyOTP -Slot ShortPress -HOTP -Base32Secret "QRFJ7DTIVASL3PNYXWFIQAQN5RKUJD4U" -SendTabFirst /// @@ -158,6 +162,10 @@ public class SetYubikeyOTPCommand : PSCmdlet [Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Base32 encoded secret key for HOTP", ParameterSetName = "HOTP")] public string? Base32Secret { get; set; } + // Hex encoded secret key for HOTP mode + [Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Hex encoded secret key for HOTP", ParameterSetName = "HOTP")] + public string? HexSecret { get; set; } + // Use 8 digits instead of 6 for HOTP mode [Parameter(Mandatory = false, ValueFromPipeline = false, HelpMessage = "Use 8 digits instead of 6 for HOTP", ParameterSetName = "HOTP")] public SwitchParameter Use8Digits { get; set; } @@ -326,12 +334,18 @@ protected override void ProcessRecord() Memory _HOTPsecretKey = new Memory(new byte[20]); ConfigureHotp configureHOTP = otpSession.ConfigureHotp(Slot); - // Handle Secret Key configuration + // Handle Secret Key configuration using Base32 if (Base32Secret != null) { _HOTPsecretKey = powershellYK.support.Base32.Decode(Base32Secret); configureHOTP = configureHOTP.UseKey(_HOTPsecretKey); } + // Handle Secret Key configuration using Hex + else if (HexSecret != null) + { + _HOTPsecretKey = powershellYK.support.Hex.Decode(HexSecret); + configureHOTP = configureHOTP.UseKey(_HOTPsecretKey); + } else if (SecretKey is null) { configureHOTP = configureHOTP.GenerateKey(_HOTPsecretKey); @@ -362,10 +376,10 @@ protected override void ProcessRecord() configureHOTP.Execute(); - // Return both raw and Base32 representations of the key + // Return both Hex and Base32 representations of the key WriteObject(new { - SecretKey = _HOTPsecretKey.ToArray(), + HexSecret = powershellYK.support.Hex.Encode(_HOTPsecretKey.ToArray()), Base32Secret = powershellYK.support.Base32.Encode(_HOTPsecretKey.ToArray()) }); break; diff --git a/Module/support/Hex.cs b/Module/support/Hex.cs new file mode 100644 index 0000000..6156cfd --- /dev/null +++ b/Module/support/Hex.cs @@ -0,0 +1,59 @@ +/// +/// Utility class for hex encoding and decoding. +/// Provides methods to convert between byte arrays and hex strings. +/// +/// .EXAMPLE +/// # Encode a byte array to hex +/// $bytes = [byte[]]@(1,2,3,4,5) +/// $hex = [powershellYK.support.Hex]::Encode($bytes) +/// +/// .EXAMPLE +/// # Decode a hex string to bytes +/// $hex = "0102030405" +/// $bytes = [powershellYK.support.Hex]::Decode($hex) +/// + +using System; +using System.Linq; + +namespace powershellYK.support +{ + public static class Hex + { + // Encodes a byte array to a hex string + public static string Encode(byte[] data) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); + + if (data.Length == 0) + return string.Empty; + + return string.Concat(data.Select(b => b.ToString("x2"))); + } + + // Decodes a hex string to a byte array + public static byte[] Decode(string hexString) + { + if (string.IsNullOrEmpty(hexString)) + return Array.Empty(); + + // Remove any whitespace and convert to lowercase + hexString = hexString.Replace(" ", "").ToLower(); + + // Ensure the string has an even length + if (hexString.Length % 2 != 0) + throw new ArgumentException("Hex string must have an even length"); + + // Convert each pair of hex characters to a byte + byte[] result = new byte[hexString.Length / 2]; + for (int i = 0; i < result.Length; i++) + { + string hexPair = hexString.Substring(i * 2, 2); + result[i] = Convert.ToByte(hexPair, 16); + } + + return result; + } + } +} \ No newline at end of file