Table of Contents

Class PutDataCommand

Namespace
Yubico.YubiKey.Piv.Commands
Assembly
Yubico.YubiKey.dll

Put a Data Object onto the YubiKey in a storage location.

public sealed class PutDataCommand : IYubiKeyCommand<PutDataResponse>
Inheritance
object
PutDataCommand
Implements

Remarks

The partner Response class is PutDataResponse.

See also the User's Manual entries on Get and Put Data and PIV objects.

A Data Object is a DataTag/Data pair. Think of it as a "key-value pair", with the DataTag a key and the Data a value. The DataTag is simply a number and associated with each is a definition of data elements and how they are encoded.

The PIV standard specifies a set of Data Objects. See a description of each DataTag and the associated Data in the User's Manual entry on the GET DATA command. Yubico specifies some non-standard DataTags as well. See descriptions in the User's Manual entry on Get and Put vendor data. The YubiKey also allows an application to use undefined DataTags as well. See the See the User's Manual entry on Data Objects for a table describing the undefined DataTags.

The caller supplies the DataTag and the data, this command will store that data on the YubiKey under the DataTag. If the YubiKey already contains information under the given DataTag, this command replaces it.

Note that for some Data Objects there are higher-level APIs that are easier to use. An application that needs to store information often will not need to use this command. For example, if you want to put a certificate onto a YubiKey, use ImportCertificate(byte, X509Certificate2, bool). Or if you want to store/retrieve Key History, use ReadObject<T>() and WriteObject(PivDataObject) along with the KeyHistory class. Under the covers, these APIs will ultimately call this command. But the application that uses the SDK can simply make the specific API calls, rather than use Put Data.

There are a number of ways to use this command. The old, obsolete way is to provide the DataTag using the PivDataTag enum, along with data encoded as defined by PIV. The constructor PutDataCommand(PivDataTag, ReadOnlyMemory<byte>) and the properties Tag and EncodedData require using PIV-defined DataTags with PIV-defined encoded data only. This constructor and these properties are marked "Obsolete" and will be removed from the SDK in the future. However, it will still be possible to get the same functionality using the updated API.

The API you should use are the constructors PutDataCommand(), and PutDataCommand(int, ReadOnlyMemory<byte>)c>, along with the properties DataTag and Data. Using these will allow you to use any DataTag (not just those defined by PIV) and any data (not just the data and encoding specified by PIV). There are two restrictions on the data. One, each storage area on the YubiKey can only store up to 2,800 bytes, and two, the data must be encoded as

  53 length
     data
For example, to store 04 02 55 44 02 01 7F
the data must be provided as
  53 07
     04 02 55 44 02 01 7F

While you can store any data under a PIV-defined DataTag, if you want to use only PIV-defined DataTags, and want to verify that the encoding follows the PIV standard (as happens using the old, obsolete API), you can use the PivDataTag class. For example,

// Store IrisImages
if (PivDataTag.IrisImages.IsValidEncodingForPut(encodedDataToStore))
{
    var putCmd = new PutDataCommand(
        (int)PivDataTag.IrisImages, encodedDataToStore);
}

Note that even though it is possible, using this command, to store any data under a PIV-defined DataTag, it is not recommended. If you have some other data you would like to store, you can use an "undefined" DataTag. For example, the YubiKey will store data under the number 0x005F0101. That is a number not used by PIV and not used by Yubico. But the YubiKey will accept it. So if you have non-PIV data you want to store, rather than use a PIV-defined number, just use one of the undefined values. See the User's Manual entry on Data Objects for tables describing possible DataTags.

There is probably only one reason to store non-PIV data under a PIV-defined DataTag, and that is to require the PIN to retrieve. There are four PIV-defined DataTags for which the PIN is required to retrieve the data: Printed, Iris, FacialImages, and Fingerprints. Data stored under any other DataTag can be retrieved by anyone with access to the YubiKey. If you want to store some data and want it to be accessible only with PIN verification, then you must store it under one of those DataTags. However, Yubico already stores data under the Printed DataTag. That data is needed to configure the YubiKey in "PIN-Protected mode". Hence, it would be a good idea to stay away from that DataTag.

This command will not allow putting data for all PIV data tags. The Discovery and BITGT DataTags will not be accepted. The Discovery Data object is fixed data and it would inhibit the YubiKey's ability to operate if that value were changed from what was installed at manufacture. In virtually all real-world applications, this will likely not be a problem.

Note that when you set an object with the DataTag or Data using either the old constructor/properties or the new versions, when you get it (using either old or new), you are getting the same thing. For example,

// Use the old, obsolete API to set the tag and data.
var putCmd = new PutDataCommand()
{
    Tag = PivDataTag.KeyHistory,
    EncodedData = encodedKeyHistory,
}
PivDataTag pivDataTag = putCmd.Tag;
int dataTag = putCmd.DataTag;
// At this point pivDataTag will equal PivDataTag.KeyHistory = 0x005FC10C
// dataTag will equal 0x005FC10C
// Even though the code used the old API to set the Tag property
// the new API DataTag property will return the same value.

This class will copy a reference to the data to put, so you should not clear or alter that input data until this class is done with it, which is after the call to SendCommand.

Example:

/* This example assumes there is some code that will build an encoded
   certificate from an X509Certificate2 object. */
byte[] encodedCertificate = PivPutDataEncodeCertificate(certObject);
IYubiKeyConnection connection = key.Connect(YubiKeyApplication.Piv);
PutDataCommand putDataCommand = new PutDataCommand(
    (int)PivDataTag.Authentication, encodedCertificate);
PutDataResponse putDataResponse = connection.SendCommand(putDataCommand);
if (getDataResponse.StatusWord != SWConstants.Success)
{
    /* handle case where the the PUT did not work. */
}

Constructors

PutDataCommand()

Initializes a new instance of the PutDataCommand class.

public PutDataCommand()

Remarks

This constructor is provided for those developers who want to use the object initializer pattern. For example:

var command = new PutDataCommand()
{
    DataTag = (int)PivDataTag.Authentication;
    Data = GetEncodedCertificate();
};

There is no default Tag or EncodedData, hence, for this command to be valid, the Tag and EncodedData must be specified. So if you create an object using this constructor, you must set the Tag and EncodedData properties at some time before using it. Otherwise you will get an exception when you do use it.

PutDataCommand(int, ReadOnlyMemory<byte>)

Initializes a new instance of the PutDataCommand class.

public PutDataCommand(int dataTag, ReadOnlyMemory<byte> data)

Parameters

dataTag int

The DataTag indicating where the data will be stored.

data ReadOnlyMemory<byte>

The data to put, encoded as 53 length data.

Remarks

Note that this constructor requires using a DataTag that is a number from 0x005F0000 to 0x005FFFFF inclusive. The data can be anything but it must be encoded as 53 length data

To encode, you can use the TlvWriter class.

var tlvWriter = new TlvWriter();
tlvWriter.WriteValue(0x53, dataToStore);
byte[] encoding = tlvWriter.Encode();

Exceptions

ArgumentException

The DataTag specified is not a number between 0x005F0000 and 0x005FFFFF (inclusive), or 0x00007F61.

Properties

Application

Gets the YubiKeyApplication to which this command belongs. For this command it's PIV.

public YubiKeyApplication Application { get; }

Property Value

YubiKeyApplication

YubiKeyApplication.Piv

Data

The data that will be put onto the YubiKey.

public ReadOnlyMemory<byte> Data { get; set; }

Property Value

ReadOnlyMemory<byte>

Exceptions

ArgumentException

The data supplied is not encoded 53 length data.

DataTag

The tag specifying where the data will be put.

public int DataTag { get; set; }

Property Value

int

Exceptions

ArgumentException

The DataTag specified is not a number between 0x005F0000 and 0x005FFFFF (inclusive), or 0x00007F61.

Methods

CreateCommandApdu()

Creates a well-formed CommandApdu to send to the YubiKey.

public CommandApdu CreateCommandApdu()

Returns

CommandApdu

A valid CommandApdu that is ready to be sent to the YubiKey, or passed along to additional encoders for further processing.

Remarks

This method will first perform validation on all of the parameters and data provided to it. The CommandAPDU it creates should contain all of the data payload for the command, even if it exceeds 65,535 bytes as specified by the ISO 7816-4 specification. The APDU will be properly chained by the device connection prior to being sent to the YubiKey, and the responses will collapsed into a single result.

CreateResponseForApdu(ResponseApdu)

Creates the corresponding IYubiKeyResponse implementation for the current command.

public PutDataResponse CreateResponseForApdu(ResponseApdu responseApdu)

Parameters

responseApdu ResponseApdu

The ResponseApdu returned by the YubiKey.

Returns

PutDataResponse

The implementation of IYubiKeyResponse that parses and presents ths response APDU.