Skip to content

docs: Updates to challenge-response documentation to improve clarity #221

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

Merged
merged 2 commits into from
Apr 22, 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
84 changes: 37 additions & 47 deletions docs/users-manual/application-otp/challenge-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ limitations under the License. -->

The other OTP application configurations ([Yubico OTP](xref:OtpYubicoOtp), [OATH HOTP](xref:OtpHotp),
and [static password](xref:OtpStaticPassword)) require the user to activate the configured [slot](xref:OtpSlots) (by
touching the YubiKey or scanning it with an [NFC reader](xref:OtpNdef)) in order to generate and submit the password
touching the YubiKey or scanning it with an [NFC reader](xref:OtpNdef)) in order to generate and transmit the password
from the YubiKey to a host device. Challenge-response, on the other hand, begins with a “challenge” that a host sends to
the YubiKey. The YubiKey receives the challenge (as a byte array) and “responds” by encrypting or digesting (hashing)
the challenge with a stored secret key and sending the response code back to the host for authentication.
the YubiKey. The YubiKey receives the challenge as a byte array and “responds” by encrypting or digesting (hashing)
the challenge with a stored secret key and sending the response back to the host for authentication.

Challenge-response is flexible. It can be used in single and multi-factor authentication for logging into applications
or devices, and validation can take place on a host device itself or on a validation server on an internal or external
Expand All @@ -39,34 +39,35 @@ To implement challenge-response authentication with a .NET application, the foll

* The validating party must be able to validate responses and pass the result back to the application.

> [!NOTE]
> [!IMPORTANT]
> All YubiKey-host communication for challenge-response is done via the [HID communication protocol](xref:OtpHID).
> Therefore, challenge-response authentication will only work when a YubiKey is physically plugged into a host over USB
> or
> Lightning. Challenges and responses cannot be communicated wirelessly with NFC.

## Supported challenge-response algorithms

The .NET SDK and the YubiKey support the following encryption and hashing algorithms for challenge-response:
The .NET SDK and the YubiKey support the following algorithms for challenge-response:

* [Yubico OTP](xref:OtpYubicoOtp) (encryption)

* HMAC-SHA1 as defined in [RFC2104](https://datatracker.ietf.org/doc/html/rfc2104) (hashing)

For Yubico OTP challenge-response, the key will receive a 6-byte challenge. The YubiKey will then create a 16-byte
For Yubico OTP challenge-response, an application will send the YubiKey a 6-byte challenge. The YubiKey will then create a 16-byte
string by concatenating the challenge with 10 bytes of unique device fields. For Yubico OTP challenge-response, these 10
bytes of additional data are not important. They are merely added as padding so that the challenge may then be encrypted
bytes of additional data are not important—they are merely added as padding so that the challenge may then be encrypted
with a 16-byte key using the AES encryption algorithm (AES requires that data be encrypted in blocks of the same size as
the encryption key). The resulting Yubico OTP becomes the response code.
the encryption key). The resulting Yubico OTP (as a byte array) becomes the response.

For HMAC-SHA1 challenge-response, the key will receive a challenge of up to 64 bytes in size, which will be digested (
hashed) with a 20-byte secret key, resulting in a 6-10 digit HOTP as the response code.
For HMAC-SHA1 challenge-response, an application will send the YubiKey a challenge of up to 64 bytes in size, which will be digested (hashed) with a 20-byte secret key, resulting in a 20-byte response (the HMAC-SHA1 hash value). Responses can be received
by an application as a byte array or a 6-10 digit numeric code. With HMAC-SHA1, the challenge can be either an
application-specified byte array or the current Unix time.

> [!NOTE]
> Hashing/digesting is a one-way operation, meaning that once a block of data is hashed, it cannot be converted back
> into its original form. Encryption, on the other hand, is a two-way operation. When a block of data is encrypted, it
> can
> be decrypted back into its original form at any time. This is an important distinction because the validating party
> be decrypted back into its original form. This is an important distinction because the validating party
> will
> have to respond differently to Yubico OTP responses (encrypted) and HMAC-SHA1 responses (hashed). For Yubico OTP, the
> validating party will have to decrypt the response and compare the result with the original challenge. For HMAC-SHA1,
Expand All @@ -85,23 +86,18 @@ The challenge-response process works as follows:
1. The YubiKey receives the challenge and encrypts/digests it with the secret key and encryption/hashing algorithm that
the slot was configured with.

1. The YubiKey sends the response back to the host, and the application receives it as a string object containing
numeric digits, a byte array, or a single integer (as determined by the SDK).
1. The YubiKey sends the response back to the host, and the application receives it as a raw byte array, a string object of
numeric digits, or an integer (as configured with the SDK).

1. The application sends the response to the validating party. For Yubico OTP challenge-response, the response must be
decrypted using the YubiKey’s unique secret key. For HMAC-SHA1 challenge-response, the validating party must digest
the challenge with the secret key using the same HMAC-SHA1 algorithm.
the challenge with the secret key and the HMAC-SHA1 algorithm.

1. For Yubico OTP, if the decrypted response matches the original challenge that was sent to the YubiKey, authentication
was successful, and the user is logged in. (For Yubico OTP challenge-response, the 6-byte challenge must match the
first 6 bytes of the decrypted response—the other bytes are ignored.) For HMAC-SHA1, if the response matches the
validating party's digested challenge, authentication was successful, and the user is logged in.

> [!NOTE]
> For the authentication process to succeed, the size of the challenge must align with the algorithm that the YubiKey
> was configured with. Similarly, the validating party must decrypt the response using the same algorithm that the
> challenge was encrypted with.

## SDK functionality

The SDK’s challenge-response functionality centers around the following two methods:
Expand All @@ -112,7 +108,7 @@ The SDK’s challenge-response functionality centers around the following two me

``ConfigureChallengeResponse()`` allows you to configure an OTP application slot on a YubiKey to receive a challenge
from a host and process it based on a specific algorithm and secret key. ``CalculateChallengeResponse()`` allows a host
to send a challenge to a YubiKey and then receive the response from the YubiKey.
to send a challenge to a YubiKey and then receive its response.

### ConfigureChallengeResponse()

Expand All @@ -131,10 +127,8 @@ call ``UseHmacSha1()``, the YubiKey will digest challenges it receives with the
> [!NOTE]
> It’s important that the size of your secret key matches the size that is expected for the algorithm you
> chose ([16 bytes](xref:Yubico.YubiKey.Otp.Operations.ConfigureChallengeResponse.YubiOtpKeySize) for Yubico OTP
> and [20 bytes](xref:Yubico.YubiKey.Otp.Operations.ConfigureChallengeResponse.HmacSha1KeySize) for HMAC-SHA1). For
> example, if you call ``UseYubiOtp()``, the key that you set with ``UseKey()`` must be 16 bytes long. Otherwise, the
> YubiKey will not be able to respond to a challenge correctly. The SDK will throw an exception if the key length is
> incorrect for the chosen configuration.
> and [20 bytes](xref:Yubico.YubiKey.Otp.Operations.ConfigureChallengeResponse.HmacSha1KeySize) for HMAC-SHA1). The SDK will throw an exception if the key length is
> incorrect for the chosen algorithm.

The ``ConfigureChallengeResponse`` class also provides optional methods for requiring users to touch the YubiKey to
initiate the challenge-response
Expand All @@ -146,29 +140,35 @@ bytes ([UseSmallChallenge()](xref:Yubico.YubiKey.Otp.Operations.ConfigureChallen
> ``UseSmallChallenge()`` is included for compatibility with legacy systems whose implementations break data sets into
> multiple blocks, which often results in the last element being smaller than 64 bytes.

For a full list of the methods in the ``ConfigureChallengeResponse`` class, please see
For a full list of the methods in the ``ConfigureChallengeResponse`` class, see
the [API documentation](xref:Yubico.YubiKey.Otp.Operations.ConfigureChallengeResponse).

For an example of how to use ``ConfigureChallengeResponse()``, please
see [How to program a slot with a challenge-response credential](xref:OtpProgramChallengeResponse).
For an example of how to use ``ConfigureChallengeResponse()``, see
[How to program a slot with a challenge-response credential](xref:OtpProgramChallengeResponse).

### CalculateChallengeResponse()

In order for a host to send a challenge to a YubiKey and receive a response, an application on the host must
call ``CalculateChallengeResponse()``. With this method, you can:

* send the challenge to the YubiKey as a byte array
with [UseChallenge()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.UseChallenge%28System.Byte%5B%5D%29).
* send a Yubico OTP or HMAC-SHA1 challenge to the YubiKey as an application-specified byte array
with [UseChallenge()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.UseChallenge%28System.Byte%5B%5D%29).
Alternatively, the current Unix time can be sent as a challenge with
[UseTotp()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.UseTotp) for HMAC-SHA1 challenge-response.

* send a message to the user to notify them to touch the YubiKey to initiate the challenge-response operation
with [UseTouchNotifier()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.UseTouchNotifier%28System.Action%29).
This is only needed if the YubiKey slot was configured to require the button touch with ``UseButton()``.

* receive the response from the YubiKey. The response can be received as a string object of numeric digits
via [GetCode()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.GetCode%28System.Int32%29), as a byte
array via [GetDataBytes()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.GetDataBytes) (the only
response type that is compatible with Yubico OTPs), or as a single 32-bit integer
via [GetDataInt()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.GetDataInt).
* receive the response from the YubiKey. The response can be received as a string object of 6-10 numeric digits
via [GetCode()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.GetCode%28System.Int32%29) (HMAC-SHA1), as a byte
array via [GetDataBytes()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.GetDataBytes) (Yubico OTP, HMAC-SHA1), or as a single 10-digit, 32-bit integer
via [GetDataInt()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.GetDataInt) (HMAC-SHA1).

In addition, the time period for time-based challenges sent with ``UseTotp()`` (i.e. how long a TOTP response is valid for) can be set
via [WithPeriod()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.WithPeriod%28System.Int32%29). The
default period is 30 seconds. Time-based challenges can only be used with keys configured for HMAC-SHA1 challenge-response.
The SDK will throw an exception if you call both ``UseTotp()`` and ``UseChallenge()``.

> [!NOTE]
> The size of the challenge sent to the YubiKey with ``UseChallenge()`` must align with the slot's configuration. If the
Expand All @@ -178,20 +178,10 @@ call ``CalculateChallengeResponse()``. With this method, you can:
> configured for HMAC-SHA1, the challenge must
> be [64 bytes](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.MaxHmacChallengeSize) long. However, if
> the
> slot has been configured with ``UseSmallChallenge()``, an HMAC-SHA1 challenge smaller than 64 bytes is acceptable. The
> slot has been configured with ``UseSmallChallenge()``, a challenge smaller than 64 bytes is acceptable. The
> SDK will throw an exception if the challenge size does not match the YubiKey slot's configuration.

Alternatively, the application can send a TOTP challenge to the YubiKey
with [UseTotp()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.UseTotp). The time period of the TOTP
challenge (i.e. how long a TOTP is valid for) can be set
via [WithPeriod()](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse.WithPeriod%28System.Int32%29) (the
default period is 30 seconds). TOTP challenges can only be used with keys configured for HMAC-SHA1 challenge-response.
With ``UseTotp()``, the application will send the current time as the challenge, and the YubiKey will digest it with the
stored secret key and the HMAC-SHA1 algorithm. The SDK will throw an exception if you call both ``UseTotp()``
and ``UseChallenge()``.

For a full list of the methods in the ``CalculateChallengeResponse`` class, please see
For a full list of the methods in the ``CalculateChallengeResponse`` class, see
the [API documentation](xref:Yubico.YubiKey.Otp.Operations.CalculateChallengeResponse).

For an example of how to use ``CalculateChallengeResponse()``, please
see [How to calculate a response code for a challenge-response credential](xref:OtpCalcChallengeResponseCode).
For an example of how to use ``CalculateChallengeResponse()``, see [How to calculate a response code for a challenge-response credential](xref:OtpCalcChallengeResponseCode).
Loading