Skip to content

Commit

Permalink
yesdk 795 u2f session PIN methods (#440)
Browse files Browse the repository at this point in the history
* add U2F Authenticate, update Register, start adding U2F PIN operations

* fix error, make sure appId is entered before clientDataHash

* update based on review

* fixed merge conflicts

* add PIN functionality, update docs

* remove invalid tests

* update based on review comments
  • Loading branch information
burnett86 authored Jun 28, 2022
1 parent 0be5a35 commit 0dba76a
Show file tree
Hide file tree
Showing 9 changed files with 1,631 additions and 573 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The data is the PIN itself, there is no further encoding.

### Response APDU info

#### Response APDU for successful verifying PIN
#### Response APDU for successfully verifying PIN

Total Length: 2\
Data Length: 0
Expand Down
22 changes: 22 additions & 0 deletions Yubico.YubiKey/docs/users-manual/application-u2f/fips-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,28 @@ end user at the keyboard, which would make it a normal password.
Once you set the password, the YubiKey will be in FIPS mode and the
`VerifyFipsModeCommand` will return true.

## Retries

If a caller wants to verify or change a PIN, the current PIN must be entered. If a wrong
value is provided, the PIN won't be verified or changed and the caller can try again.
However, there are limits to how many times a wrong value can be entered.

If an incorrect PIN is entered three times in a row, the U2F application is temporarily
blocked. To unblock it, remove the YubiKey and reinsert it.

If an incorrect PIN is entered eight times in a row (three times, reinserted, three times,
reinserted, two times), the U2F application is permanently blocked. At this point, to be
able to use the U2F application on that YubiKey again, it must be reset. Of course, after
resetting, the YubiKey can no longer be put into FIPS mode.

If the correct PIN is verified before the U2F application is blocked, the retries
remaining count returns to eight.

Unfortunately in the version 4 FIPS series YubiKey, it is not possible to know how many
U2F PIN retries are remaining. That is, if the wrong PIN has been entered, the SDK will
return to the caller indicating that the wrong PIN was entered, but will not be able to
report the number of retries remaining.

## Removing the PIN

Once a PIN is set on the U2F application, it is not possible to remove it with the
Expand Down
1,477 changes: 1,061 additions & 416 deletions Yubico.YubiKey/src/Resources/ExceptionMessages.Designer.cs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Yubico.YubiKey/src/Resources/ExceptionMessages.resx
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,12 @@
<data name="UnknownYubiKeyFeature" xml:space="preserve">
<value>Unknown YubiKey feature.</value>
</data>
<data name="YubiKeyNotFips" xml:space="preserve">
<value>The requested operation is not available on non-FIPS YubiKeys.</value>
</data>
<data name="AlreadySet" xml:space="preserve">
<value>The YubiKey is already set with the attribute specified.</value>
</data>
<data name="InvalidU2fHidErrorCodeLength" xml:space="preserve">
<value>The U2F HID error code should be 1 byte long. The supplied error code contains {0} bytes.</value>
</data>
Expand Down
60 changes: 16 additions & 44 deletions Yubico.YubiKey/src/Yubico/YubiKey/U2f/Commands/SetPinCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,16 @@ public sealed class SetPinCommand : IYubiKeyCommand<SetPinResponse>
{
private const byte Ctap1MessageInstruction = 0x03;
private const byte SetPinInstruction = 0x44;
private const int MinimumPinLength = 6;
private const int MaximumPinLength = 32;

private ReadOnlyMemory<byte> _currentPin = ReadOnlyMemory<byte>.Empty;
private ReadOnlyMemory<byte> _newPin = ReadOnlyMemory<byte>.Empty;

/// <summary>
/// The PIN needed to perform U2F operations on a FIPS YubiKey. If this is
/// empty, then the caller expects that there is no PIN yet set.
/// </summary>
/// <remarks>
/// If there is a PIN, it must be from 6 to 32 bytes long (inclusive). It
/// is binary data.
/// is binary data. This command class will use whatever PIN you supply,
/// so if it is an incorrect length, you will get the error when trying
/// to execute the command.
/// <para>
/// This class will copy a reference to the PIN provided. Do not
/// overwrite the data until after the command has executed. After it has
Expand All @@ -51,56 +48,29 @@ public sealed class SetPinCommand : IYubiKeyCommand<SetPinResponse>
/// PIN for the first time), there is no need to set this property.
/// </para>
/// </remarks>
public ReadOnlyMemory<byte> CurrentPin
{
get => _currentPin;

set
{
if ((value.Length != 0) && ((value.Length < MinimumPinLength) || (value.Length > MaximumPinLength)))
{
throw new ArgumentException(
string.Format(
CultureInfo.CurrentCulture,
ExceptionMessages.InvalidPinLength));
}

_currentPin = value;
}
}
public ReadOnlyMemory<byte> CurrentPin { get; set; }

/// <summary>
/// The PIN that will replace the current PIN.
/// </summary>
/// <remarks>
/// The PIN must be from 6 to 32 bytes long (inclusive). It is binary
/// data. It is not possible to pass in an Empty PIN (changing a YubiKey
/// from PIN required to no PIN). Once a PIN is set, the U2F application
/// on that YubiKey must always have a PIN. The only way to remove a PIN
/// is to reset the application.
/// The PIN must be from 6 to 32 bytes long (inclusive). This command
/// class will use whatever PIN you supply, so if it is an incorrect
/// length, you will get the error when trying to execute the command.
/// <para>
/// It is binary data. It is not possible to pass in an Empty PIN
/// (changing a YubiKey from PIN required to no PIN). Once a PIN is set,
/// the U2F application on that YubiKey must always have a PIN. The only
/// way to remove a PIN is to reset the application.
/// </para>
/// <para>
/// This class will copy a reference to the PIN provided. Do not
/// overwrite the data until after the command has executed. After it has
/// executed, overwrite the buffer for security reasons.
/// </para>
/// </remarks>
public ReadOnlyMemory<byte> NewPin
{
get => _newPin;
public ReadOnlyMemory<byte> NewPin { get; set; }

set
{
//if ((value.Length < MinimumPinLength) || (value.Length > MaximumPinLength))
//{
// throw new ArgumentException(
// string.Format(
// CultureInfo.CurrentCulture,
// ExceptionMessages.InvalidPinLength));
//}

_newPin = value;
}
}
/// <summary>
/// Gets the YubiKeyApplication to which this command belongs.
/// </summary>
Expand All @@ -125,6 +95,8 @@ public ReadOnlyMemory<byte> NewPin
/// </remarks>
private SetPinCommand()
{
CurrentPin = ReadOnlyMemory<byte>.Empty;
NewPin = ReadOnlyMemory<byte>.Empty;
}

/// <summary>
Expand Down
26 changes: 4 additions & 22 deletions Yubico.YubiKey/src/Yubico/YubiKey/U2f/Commands/VerifyPinCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,40 +29,22 @@ public sealed class VerifyPinCommand : IYubiKeyCommand<VerifyPinResponse>
{
private const byte Ctap1MessageInstruction = 0x03;
private const byte VerifyPinInstruction = 0x43;
private const int MinimumPinLength = 6;
private const int MaximumPinLength = 32;

private ReadOnlyMemory<byte> _pin = ReadOnlyMemory<byte>.Empty;

/// <summary>
/// The PIN needed to perform U2F operations on a FIPS YubiKey.
/// </summary>
/// <remarks>
/// The PIN must be from 6 to 32 bytes long (inclusive). It is binary
/// data.
/// data. This command class will use whatever PIN you supply, so if it
/// is an incorrect length, you will get the error when trying to
/// execute tht command.
/// <para>
/// This class will copy a reference to the PIN provided. Do not
/// overwrite the data until after the command has executed. After it has
/// executed, overwrite the buffer for security reasons.
/// </para>
/// </remarks>
public ReadOnlyMemory<byte> Pin
{
get => _pin;

set
{
if ((value.Length < MinimumPinLength) || (value.Length > MaximumPinLength))
{
throw new ArgumentException(
string.Format(
CultureInfo.CurrentCulture,
ExceptionMessages.InvalidPinLength));
}

_pin = value;
}
}
public ReadOnlyMemory<byte> Pin { get; set; }

/// <summary>
/// Gets the YubiKeyApplication to which this command belongs.
Expand Down
Loading

0 comments on commit 0dba76a

Please sign in to comment.