0

I am building a web application in C# to program Slot1 on a YubiKey for OTP. I am successfully able to program the key unless the configuration has been protected. The serial number was used to protect Slot1's configuration, but whenever I try to convert the serial number into the necessary Memory get 7 bytes and the .Execute() command complains about the max length accepted being 6 bytes... The serial number is 7 digits.

The error message is - {"Access code must be 6 or fewer bytes."}

I can't find any helpful information in the Yubico API documentation and was hoping someone here could assist. I need to be able to use the current access code (always the S/N) to overwrite Slot1 if its protected.

try
{
    OtpSession otp_session = new OtpSession(_yubiKey);

    Memory<byte> hmac_key = Generate_Seed();
    Memory<byte> serial_number = new Memory<byte>();

    serial_number = Encoding.ASCII.GetBytes(_yubiKey.SerialNumber.ToString());

    SlotAccessCode access_code = new SlotAccessCode(serial_number);

    using (OtpSession otp = new OtpSession(_yubiKey))
    {
        otp.ConfigureHotp(Slot.ShortPress)
            .UseCurrentAccessCode(access_code)
            .AppendCarriageReturn(true)
            .UseNumericKeypad(true)
            .UseKey(hmac_key)         
            .Execute();
    }
}
catch (Exception ex)
{
    if (ex.ToString() == "{YubiKey Operation Failed [Warning, state of non-volatile memory is 
        unchanged.]}")
        lblMessage.Text = "YubiKey could not be configured. Please use the proper access 
        code.";
}        
Steve Vinoski
  • 19,847
  • 3
  • 31
  • 46

1 Answers1

1

The ASCII encoding of the serial number cannot be used as an access code, simply because such ASCII strings can exceed the SlotAccessCode.MaxAccessCodeLength value, which is 6.

However, as the SerialNumber is an int (4 bytes), simply use the binary representation of the serial number as the access code:

int serial = yubikey.SerialNumber;
byte[] bytes = new byte[4];
bytes[0] = (byte)(serial >> 24);
bytes[1] = (byte)(serial >> 16);
bytes[2] = (byte)(serial >> 8);
bytes[3] = (byte)serial;

SlotAccessCode access_code = new SlotAccessCode(bytes);

On recent .NET version, you can probably also use class BinaryPrimitives , but you need to take endianness into account to make your code portable.

As a side note: why do you want to use the serial as an access code? Access codes usually need to be secret information.

Update 1: Reading your question more carefully, I understand your access codes are using Binary Coded Decimal (BCD) encoding.

There is probably a library somewhere that does that for you, but otherwise you can use something like this for the encoding:

serial = 7112345; // example serial number
byte[] bcdBytes = new byte[6];
for (int i = 5; i >= 0; i--)
{
    int right = serial % 10;
    serial /= 10;
    int left = serial % 10;
    serial /= 10;
    bcdBytes[i] = (byte) (left << 4 | right);
}
SlotAccessCode access_code = new SlotAccessCode(bcdBytes);

Update 2: For a big-endian hex encoding:

serial = 7112345;
byte[] serialBytes = new byte[4];
BinaryPrimitives.TryWriteInt32BigEndian(serialBytes,serial);
byte[] hexBytes = new byte[6];
Array.Copy(serialBytes,0,hexBytes,2,4);
SlotAccessCode access_code = new SlotAccessCode(hexBytes);

This would result in the byte array 00 00 00 6c 86 99, where 0x6c8699 == 7112345

joostd
  • 51
  • 4
  • This was a decision made to provide a basic level of protection while allowing the company's MFA team to know how to reprogram the keys if needed in the future. Its a bit of security by obscurity I know, but it was decided that it was better than nothing and its not really that important that the keys can't be overwritten. The company has nearly 200k YubiKeys in distribution. There needed to be a common/known access code for all keys. – Dennis Robare Aug 31 '22 at 16:04
  • I still get the wrong values in the Memory array... According to Yubico, it should be the actual digits on the serial number. i.e.- S/N 7112345 should be "00 00 07 11 23 45" for the access code, but converting to bytes changes the values and it doesn't work. How do I get the actual digits into the byte array without converting them to actual bytes? – Dennis Robare Aug 31 '22 at 16:45
  • Apologies, reading your question more carefully I now see what you mean. I will update my answer. – joostd Sep 01 '22 at 07:28
  • So that works! The byte array (in the Memory obj) is not {00 00 07 11 23 45} like I would expect, but its overwriting the configuration in Slot 1 just fine. Thank you! One additional question, do you happen to know what the encoding would look like if the hex value of the S/N was used? Yubico actually programs the keys for the customer using the hex value come to find out... The above code (bcd encoding) only works for the decimal S/N. – Dennis Robare Sep 01 '22 at 16:59
  • See the 2nd update for an example hex-encoding. – joostd Sep 01 '22 at 21:41
  • That works for the hex serial number. Thank you very much for your support. I understand the YubiKeys well enough, but obviously needed some guidance on the various encodings and byte values. My programming experience hasn't required that I do much with encodings and converting numbers/values that often. This has been a good learning experience for me. Thanks again! – Dennis Robare Sep 02 '22 at 12:34