Table of Contents

Class SerializedLargeBlobArray

Namespace
Yubico.YubiKey.Fido2
Assembly
Yubico.YubiKey.dll

Contains the Serialized Large Blob Array data. See also the user's manual entry on large blobs.

public class SerializedLargeBlobArray
Inheritance
object
SerializedLargeBlobArray

Remarks

The Large Blob data is stored on the YubiKey as a "Serialized Large Blob Array". This is the "Large Blob Array" followed by a message digest value:

Large Blob Array || digest value

The digest value is the first 16 bytes of the SHA-256 digest of the Large Blob Array.

The Large Blob Array is a CBOR array (major type 4). For example, an array of 3 elements is encoded as

0x83  element0  element1  element2

A YubiKey begins with no Large Blob data. It is possible to retrieve the Serialized Large Blob Array and the result will be a zero-count array with digest value:

80 76be8b528d0075f7aae98d6fa57a6d3c
The 80 is the Large Blob Array (an array with zero elements), followed by the first 16 bytes of the SHA-256 digest of the single byte 0x80.

Each element in the Large Blob Array is a CBOR map consisting of three key/value pairs:

 A3                      -- map of 3 key/value pairs
   01  --byte string--    -- key = 1, value is a byte string
   02  --byte string--    -- key = 2, value is a byte string
   03  --unsigned int--   -- key = 3, value is an unsigned int
where the byte string for key 01 is the AEAD-AES-GCM ciphertext
  containing the encrypted data and an authentication tag
the byte string for key 02 is the AES-GCM nonce, 12 bytes
and the unsigned int is the length, in bytes, of the original,
  uncompressed data
The key used to encrypt is the LargeBlobKey There is a different LargeBlobKey for each credential. Hence, each element in the Large Blob Array is data associated with one credential.

This class is the input to the SetSerializedLargeBlobArray(SerializedLargeBlobArray). To set a Large Blob Array, get the current array (GetSerializedLargeBlobArray()) and remove, replace, or add entries. Even if there are no entries in the YubiKey (e.g. it is a new YubiKey with the initial serialized large blob array) get the current array.

To add an entry, you will need the LargeBlobKey for one of the credentials.

This class is also the return from GetSerializedLargeBlobArray(). After getting the array, if there are any elements, they will be encrypted. Determine which elements you want to decrypt, obtain the LargeBlobKey for the associated credential and call the decryption method.

Constructors

SerializedLargeBlobArray(ReadOnlyMemory<byte>)

Build a new instance of SerializedLargeBlobArray based on the given CBOR encoding.

public SerializedLargeBlobArray(ReadOnlyMemory<byte> cborEncoding)

Parameters

cborEncoding ReadOnlyMemory<byte>

The serialized large blob array, encoded following the CTAP 2.1 and CBOR (RFC 8949) standards. That is, the expected encoding is either

 80
   80 76 be 8b 52 8d 00 75 f7 aa e9 8d 6f a5 7a 6d 3c
for the initial array (the 80 is an array with zero elements)
or
 8x
   A3 --entry map--
    . . .
   A3 --entry map--
 --digest value--
where x is the number of entries and there are x
   A3 --entry map--
and the digest value is 16 bytes.

Remarks

The encoding must follow the definition of serialized large blob array in section 6.10 of the CTAP 2.1 standard.

Exceptions

Ctap2DataException

The cborEncoding is not a valid CBOR encoding, or it is not a correct encoding for FIDO2 large blob data.

Properties

Digest

The digest of the array elements (left 16 bytes of SHA-256).

public ReadOnlyMemory<byte>? Digest { get; }

Property Value

ReadOnlyMemory<byte>?

Remarks

When you get the Serialized Large Blob Array from the YubiKey, this property (and the EncodedArray property) are set. You can verify the digest at this point.

As soon as this class detects a change to one of the entries, this property and the EncodedArray property are no longer valid and will be set to null. If you call Encode() or SetSerializedLargeBlobArray(SerializedLargeBlobArray) and this property is null, the array will be rebuilt and a new digest will be computed.

EncodedArray

The encoded Large Blob Array. This is the data that is digested. That is, perform Left16Bytes(SHA-256(EncodedArray)) and it should equal the Digest.

public ReadOnlyMemory<byte>? EncodedArray { get; }

Property Value

ReadOnlyMemory<byte>?

Remarks

When you get the Serialized Large Blob Array from the YubiKey, this property (and the Digest property) are set. You can verify the digest at this point.

As soon as this class detects a change to one of the entries, this property and the Digest property are no longer valid and will be set to null. If you call Encode() or SetSerializedLargeBlobArray(SerializedLargeBlobArray) and this property is null, the array will be rebuilt.

Entries

The list of entries in the Large Blob Array.

public IReadOnlyList<LargeBlobEntry> Entries { get; }

Property Value

IReadOnlyList<LargeBlobEntry>

Remarks

After getting a Serialized Large Blob Array from a YubiKey, this list will contain all of the entries currently stored. You can now delete or add entries. If you want to "edit" an existing entry, add a new entry with the updated information, then delete the previous version.

Upon retrieval, each entry's blob data is still encrypted. Use TryDecrypt(ReadOnlyMemory<byte>, out Memory<byte>) to see the actual data.

Methods

AddEntry(ReadOnlyMemory<byte>, ReadOnlyMemory<byte>)

Add a new entry to the Entries. This method will build a new LargeBlobEntry from the blobData and the largeBlobKey

public void AddEntry(ReadOnlyMemory<byte> blobData, ReadOnlyMemory<byte> largeBlobKey)

Parameters

blobData ReadOnlyMemory<byte>

The data to store in the Large Blob Array.

largeBlobKey ReadOnlyMemory<byte>

The 32-byte key returned by the YubiKey in an assertion, it will be used to encrypt the blobData.

Remarks

Generally you will obtain the current Large Blob Array, then remove or add entries. If you want to add an entry, call this method providing the data you want to store along with the LargeBlobKey in the returned GetAssertionData object. To "edit" an existing entry, add a new entry with the updated information, then delete the previous version.

Encode()

Build the Serialized Large Blob Array. This builds the CBOR encoding of the large blob array, digests that array, and appends the digest.

public byte[] Encode()

Returns

byte[]

A new byte array containing the Serialized Large Blob Array.

Remarks

There is the Large Blob Array, which is the CBOR encoded array of entries. Then the Serialized Large Blob Array is the concatenation of the Large Blob Array with the digest of the Large Blob Array. This builds the Serialized Large Blob Array.

This is simply the concatenation of the EncodedArray and Digest properties. However, those can be null until a call is made to encode. For example, suppose you get a Large Blob Array from a YubiKey, and the EncodedArray and Digest properties are set. But now you AddEntry(ReadOnlyMemory<byte>, ReadOnlyMemory<byte>), which will mean the array and digest must change. This class will not update the array and digest until you call this method to encode (in case you want to add another entry).

Once you call this method, the array and digest will be computed and those properties will be set.

IsDigestVerified()

Determine if the Digest verifies for the given EncodedArray.

public bool IsDigestVerified()

Returns

bool

A boolean, true if there is EncodedData and a Digest, and the digest is correct. false otherwise.

Remarks

If either or both EncodedData and Digest is null, this method returns false. If they are both present, this method will compute the SHA-256 digest of the EncodedData, and compare the "left" 16 bytes of that result with Digest.

RemoveEntry(int)

Remove the LargeBlobEntry at the given index from the Entries. Note that this can change the indices of the remaining entries.

public void RemoveEntry(int index)

Parameters

index int

Remarks

The LargeBlobEntry is a disposable class. This method will call the Dispose method for the given entry as well as removing it from the list.

If there is no entry at the index (index >= list.Count), this method will do nothing (i.e. that is not an error).