Class SerializedLargeBlobArray
Contains the Serialized Large Blob Array data. See also the user's manual entry on large blobs.
public class SerializedLargeBlobArray
- Inheritance
-
objectSerializedLargeBlobArray
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 isEncodedData
and aDigest
, 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).