FIDO2 credential management
The credential management operations allow you to obtain information about the credentials
on a YubiKey without getting an assertion. Note that you can get information only for
discoverable credentials. Remember that to make a credential discoverable, when you make
it (see MakeCredential), set
the "rk
" option to true
var makeCredentialParameters = new MakeCredentialParameters(relyingParty, userEntity)
{
ClientDataHash = clientDataHash,
};
makeCredParams.AddOption(AuthenticatorOptions.rk, true);
MakeCredentialData credentialData = fido2Session.MakeCredential(makeCredentialParameters);
These are the credential management operations:
- Get Metadata
- Enumerate Relying Parties
- Enumerate Credentials
- Delete Credential
- Update User Information
Support in the YubiKey
Not all YubiKeys support CredentialManagement. To find out if a particular YubiKey can
perform these operations, check for the "credMgmt
" options.
using (fido2Session = new Fido2Session(yubiKeyDevice))
{
if (fido2Session.AuthenticatorInfo.GetOptionValue("credMgmt") == OptionValue.True)
{
. . .
}
}
Commands and Fido2Session methods
In the SDK, there are two ways to perform a CredentialManagement operation:
- Commands
- Fido2Session methods
The commands are
- GetCredentialMetadataCommand
- EnumerateRpsBeginCommand
- EnumerateRpsGetNextCommand
- EnumerateCredentialsBeginCommand
- EnumerateCredentialsGetNextCommand
- DeleteCredentialCommand
- UpdateUserInfoCommand
Some of the commands require a PinToken. You will be responsible for building a PinToken (see next section).
The Fido2Session methods are
- GetCredentialMetadata
- EnumerateRelyingParties
- EnumerateCredentialsForRelyingParty
- DeleteCredential
- UpdateUserInfoForCredential
If you use these methods, the SDK will build the proper PinToken if needed.
PIN/UV Auth Param
In order to perform some credential management operations, it is necessary to compute a
PIN/UV Auth Param. The SDK will build the PIN/UV Auth Param, you do not need to supply it.
The PIN/UV Auth Param is built using an AuthToken
. If you use the Fido2Session methods,
the SDK will also obtain the AuthToken
.
See the User's Manual entry on AuthTokens for a detailed discussion on how they work.
Get metadata
This returns the number of discoverable credentials and the number of "empty" slots. For example, suppose the YubiKey has space for 25 credentials. Currently there are three discoverable credentials and two non-discoverable. The return from the credential management operation of get metadata would be 3 and 22. The number of remaining credential count of 22 means that it is possible to store 22 more discoverable credentials. The YubiKey stores no information on non-discoverable credentials, so the two non-discoverable credentials in this example have no effect on the number of spaces available. See also the User's Manual entry on credentials for more information on non-discoverable credentials.
The return is a Tuple
of 2 integers:
(int residentCredentialCount, int remainingCredentialCount) = fido2Session.GetCredentialMetadata();
// In the example above, residentCredentialCount would be 3 and
// remainingCredentialCount would be 22.
Enumerate relying parties
This helps you to build a list of all the relying parties represented among all the credentials on the YubiKey.
If you use Fido2Session.EnumerateRelyingParties, the SDK will return an array of
RelyingParty
objects.
If you use the commands, you will need to use the EnumerateRpsBeginCommand
command to
obtain the first relying party and the total count of relying parties represented, and
then the EnumerateRpsGetNextCommand
to get each successive relying party.
var enumBeginCmd = new EnumerateRpsBeginCommand(pinToken, protocol);
EnumerateRpsBeginResponse enumBeginRsp = connection.SendCommand(enumBeginCmd);
(int rpCount, RelyingParty firstRp) = enumBeginRsp.GetData();
for (int index = 1; index < rpCount; index++)
{
var getNextCmd = new EnumerateRpsGetNextCommand();
EnumerateRpsGetNextResponse credMgmtRsp = connection.SendCommand(getNextCmd);
RelyingParty nextRp = getNextRsp.GetData();
}
Enumerate credentials
This helps you to build a list of all the credentials on the YubiKey.
If you use Fido2Session.EnumerateCredentialsForRelyingParty, the SDK will return an array
of CredentialUserInfo objects, each one
containing the User
, CredentialId
, CredentialPublicKey
, CredProtectPolicy
, and the
LargeBlobKey
(if there is one) for each credential found on the YubiKey associated with
the specified relying party. You specify which relying party you are interested in by
supplying the RelyingParty
object, which you likely retrieved during a call to obtain a
list of relying parties.
If you use the commands, you will need to use the EnumerateCredentialsBeginCommand
command to obtain the first credential and the total count of credentials available, and
then the EnumerateCredentialsGetNextCommand
to get each successive credential.
var enumBeginCmd = new EnumerateCredentialsBeginCommand(relyingParty.RelyingPartyIdHash, pinToken, protocol);
EnumerateCredentialsBeginResponse enumBeginRsp = connection.SendCommand(enumBeginCmd);
(int credCount, CredentialUserInfo userInfo) = enumBeginRsp.GetData();
for (int index = 1; index < credCount; index++)
{
var getNextCmd = new EnumerateCredentialsGetNextCommand();
EnumerateCredentialsGetNextResponse getNextRsp = connection.SendCommand(getNextCmd);
userInfo = getNextRsp.GetData();
}
Delete credential
This allows you to remove one credential from the YubiKey.
Whether you use the command or the Fido2Session method, you must supply the CredentialId. This tells the YubiKey which credential to remove. You will likely use the Enumerate commands or the Fido2Session.EnumerateCredentialsForRelyingParty method to obtain a list of CredentialUserInfo objects, and choose the credential to delete from that list. Finally, you can use the CredentialId property in the object as the input to the delete call.
This operation needs the PIN/UV Auth Param.
It is possible that there is some large blob data stored against the
credential you are deleting. If so, you will likely want to delete that data as well. If
you use the commands to delete, it is your responsibility to delete the large blob data.
The Fido2Session
method will delete it for you.
Update user information
Each credential contains user information, represented as an instance of the UserEntity class. You can change what user information is stored on the YubiKey in that credential.
The way to change the user information is to create a new UserEntity
object, and then
call the command or the Fido2Session
method. This replaces the information on the
YubiKey, it does not "edit" it.
For example,
// Find the relying party of interest by enumerating all RPs and selecting from the list.
IReadOnlyList<RelyingParty> rpList = fido2Session.EnumerateRelyingParties();
int index = ChooseRelyingParty(rpList);
// Find the credential of interest by enumerating all the credentials associated with
// the relying party of intereset and selecting from the list.
IReadOnlyList<CredentialUserInfo> credList =
fido2Session.EnumerateCredentialsForRelyingParty(rpList[index]);
index = ChooseCredential(credList);
// Create a new UserEntity based on the current.
var updatedUserInfo = new UserEntity(credArray[index].User.Id)
{
Name = credArray[index].User.Name,
DisplayName = "Jane Doe",
};
fido2Session.UpdateUserInfoForCredential(credArray[index].CredentialId, updatedUserInfo);
Suppose the original user information was the following:
- Id = 0x3A 67 ... E9
- Name = jdoe
- DisplayName = J Doe
In the sample, the display name was changed to "Jane Doe". It built a new UserEntity
object with the following:
- Id = 0x3A 67 ... E9
- Name = jdoe
- DisplayName = Jane Doe
Then it called the update method.
If it had supplied a UserEntity
object with only the display name (because that is all
it needed to change), then after the update, the YubiKey would have contained an entry for
a user with no Id
and no Name
, just a DisplayName
.
You must supply all the user information in the updated object. That is, the object you provide as the update must include all the info that does not change as well as the info that does.