Table of Contents

Class TlvReader

Namespace
Yubico.Core.Tlv
Assembly
Yubico.Core.dll

Use this class to parse TLV (tag-length-value) constructions.

public sealed class TlvReader
Inheritance
object
TlvReader

Remarks

See the User's Manual entry on TLV for an in-depth discussion of what TLV is and a general description of how to use this class.

Constructors

TlvReader(ReadOnlyMemory<byte>)

Build a new Reader object based on the given encoding.

public TlvReader(ReadOnlyMemory<byte> encoding)

Parameters

encoding ReadOnlyMemory<byte>

The TLV encoding to read.

Remarks

This sets the position of the reader to the leading byte, the first tag.

Note that the object will copy a reference to the encoding. Do not clear or alter the encoding until after the full encoding has been read and each value operated on.

Properties

HasData

Indicates whether there is more data to read or not.

public bool HasData { get; }

Property Value

bool

Methods

PeekLength(int)

Skip the tagLength bytes and read the length octets, decode and return the length without advancing the reader.

public int PeekLength(int tagLength = 1)

Parameters

tagLength int

The length of the tag to read. If this argument is not given the default length of 1 will be used.

Returns

int

The length.

Remarks

For example, if the next byte after the tag is 0x20, this method will return 0x00000020. If the next bytes are 0x81 80, this method will return 0x00000080. For 0x82 01 00, the return is 0x0000000100.

If the next byte after the tag is an invalid or unsupported initial length octet, the method will throw an exception. Invalid initial length octets are those where the most significant bit is set but the value is not 0x81, 0x82, or 0x83. Although 0x84, and 0x85 (and higher) are valid initial length octets, they are not supported by this class. Note that 0x80 is an unsupported initial length octet (it is BER for indefinite length, and this class supports only DER length rules).

If there are not enough bytes left in the encoding to build the length, this method will throw an exception. For example, if there are only two bytes left in encoding, and you pass in 2 for the tag length, this method will throw an exception. If there are two bytes left in encoding, you pass in 1 as the tagLength, and the initial length octet is0x81 (which means there should be one length octet following the 0x81), the method will throw an exception.

Note that this method does not verify if there are at least length octets available in the encoding after the tag and length, it only returns the length and the number of octets that make up the length. For example, if at the current pointer in the encoding are the octets 01 81 80, and nothing more, the method will return 0x00000080. If you try to decode (i.e. call ReadValue), that will throw an exception. But this method will return the length, even though the encoding is not valid.

Exceptions

TlvException

The tagLength is unsupported, the length read is unsupported, or there was not enough data to read.

PeekTag(int)

Read the tag at the current data position, without advancing the reader.

public int PeekTag(int tagLength = 1)

Parameters

tagLength int

The length of the tag to read. If this argument is not given the default length of 1 will be used.

Returns

int

The tag as an int.

Remarks

The caller passes in either no argument, or 1 or 2 as the tag length. No argument passed in means the default tag length of 1 will be used. If 1, the method returns the next byte as an int. If 2, the method returns the next two bytes as an int (e.g. 71 01 is returned as 0x00007101). If you pass in any other length other than 1 or 2, this method will throw an exception.

If there is no data at the current position (the entire encoding has been read), this method will throw an exception (check the HasData property to verify there is a tag available). If the tagLength is 2 and there is only one byte left in the encoding, this method throws an exception.

Exceptions

TlvException

The tagLength is unsupported, or there was not enough data to read.

ReadByte(int)

Read the TLV at the current position, return the value as a byte, and move the position to the byte beyond the current TLV.

public byte ReadByte(int expectedTag)

Parameters

expectedTag int

The tag that should be at the current position.

Returns

byte

A byte, the value.

Remarks

The method will verify that the tag is expected. If it is, it will read the length, verify that it is one, then read that one-byte value, and return it.

If the length of the value is not 1 (even if it is 0), this method will not advance the reader and throw an exception.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will not advance the reader and throw an exception.

Exceptions

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

ReadEncoded(int)

Return the entire encoding of the next element.

public ReadOnlyMemory<byte> ReadEncoded(int expectedTag)

Parameters

expectedTag int

The tag that should be at the current position.

Returns

ReadOnlyMemory<byte>

A new ReadOnlyMemory object containing the tag, length and value of the current TLV.

Remarks

The method will verify that the tag is expected. If it is, it will read the length, verify that the encoding has at least that many bytes, then create a new ReadOnlyMemory object that points to the entire TLV (not just the value). Note that the ReadOnlyMemory object will point to the existing encoding, it will not copy the data into a new buffer.

Note that this will not treat a NestedTlv any different from a single element. That is, if the current position points to a NestedTlv, the method will return the entire encoding, including the sub-elements.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will throw an exception.

For example, suppose the encoding is the following.

7C 0D 01 01 14 02 02 01 80 05 04 00 89 2C 33
This is a NestedTlv
7C 0D
   01 01
      14
   02 02
      01 80
   05 04
      00 89 2C 33
Suppose the internal position is at 0, the beginning of full encoding.
encoded = reader.ReadEncoded(0x7C);
This will return a new ReadOnlyMemory object that points to
  7C 0D 01 01 14 02 02 01 80 05 04 00 89 2C 33
Length is 15
After the call, the internal position of the reader is at the
position just after the last byte of the value, which is beyond the
end of the full encoding.
Suppose the internal position is at 5, the beginning of the second sub-element.
value = reader.ReadEncoded(0x02);
This will return a new ReadOnlyMemory object that points to
  02 02 01 80
Length is 4
After the call, the internal position of the reader is at the
position just after the last byte of the value, which is the
next TLV: 05 04 etc.

Exceptions

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

ReadInt16(int, bool)

Read the TLV at the current position, return the value as a short (a two-byte integer), and move the position to the byte beyond the current TLV.

public short ReadInt16(int expectedTag, bool bigEndian = true)

Parameters

expectedTag int

The tag that should be at the current position.

bigEndian bool

Specifies whether the result should be returned as big endian (true or no argument given) or little endian (false).

Returns

short

A short, the value.

Remarks

The method will verify that the tag is expected. If it is, it will read the length, verify that it is two, then read that two-byte value, and return it as a short.

If the length of the value is not 2 (even if it is 0 or 1), this method will not advance the reader and throw an exception.

The method will return the value in big endian (most significant byte of the short is taken from value[0]), unless the bigEndian argument is false. If it is false, the result is returned in little endian (least significant byte of the short is taken from value[0]). The bigEndian argument has a default of true. This means you can call this method and leave out the bigEndian argument, and the method will return the short in big endian format.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will not advance the reader and throw an exception.

Exceptions

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

ReadInt32(int, bool)

Read the TLV at the current position, return the value as an int (a four-byte integer), and move the position to the byte beyond the current TLV.

public int ReadInt32(int expectedTag, bool bigEndian = true)

Parameters

expectedTag int

The tag that should be at the current position.

bigEndian bool

Specifies whether the result should be returned as big endian (true or no argument given) or little endian (false).

Returns

int

An int, the value.

Remarks

The method will verify that the tag is expected. If it is, it will read the length, verify that it is four, then read that four-byte value, and return it as an int.

If the length of the value is not 4 (even if it is 0, 1, 2, or 3), this method will not advance the reader and throw an exception.

The method will return the value in big endian (most significant byte of the int is taken from value[0]), unless the bigEndian argument is false. If it is false, the result is returned in little endian (least significant byte of the int is taken from value[0]). The bigEndian argument has a default of true. This means you can call this method and leave out the bigEndian argument, and the method will return the int in big endian format.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will not advance the reader and throw an exception.

Exceptions

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

ReadNestedTlv(int)

Read the TLV at the current position as a NestedTlv. Return a new TlvReader object whose position is the beginning of the NestedTlv's value, which is the tag of the NestedTlv's first sub-element. Move the position of the original reader to the byte beyond the current TLV.

public TlvReader ReadNestedTlv(int expectedTag)

Parameters

expectedTag int

The tag that should be at the current position, the NestedTlv's tag.

Returns

TlvReader

A new TlvReader object that contains the sub-elements of the NestedTlv, with the position set at the first sub-element.

Remarks

The new object returned will contain the encoding of the sub-elements only.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will throw an exception.

For example, suppose the encoding is the following.

7C 0D 01 01 14 02 02 01 80 05 04 00 89 2C 33
This is a NestedTlv
7C 0D
   01 01
      14
   02 02
      01 80
   05 04
      00 89 2C 33
Suppose the internal position is at 0, the beginning of full encoding.
TlvReader newReader = reader.ReadNestedTlv(0x7C);
 This will return a new TlvReader object:
  new reader: 01 01 14 02 02 01 80 05 04 00 89 2C 33
              ^
              +--current position
 After the call, the internal position of the original reader is at
 the position just after the NestedTlv's value.
  original: 7C 0D 01 01 14 02 02 01 80 05 04 00 89 2C 33
                                                         ^
                                                         +--current position
Suppose the original reader is parsing something like this:
  01 01 14 7A 07 31 02 01 00 32 01 20 05 04 00 89 2C 33
This is a concatenation with a NestedTlv as one of the elements.
     01 01
        14
     7A 07
        31 02
           01 00
        32 01
           20
     05 04
        00 89 2C 33
Suppose the internal position is at 0, the beginning of full encoding.
ReadOnlyMemory<byte> value = reader.ReadValue(0x01);
 This returned a new ReadOnlyMemory object with the contents of the
 first TLV, namely the byte array { 0x14 }. It moved the pointer to
 the next TLV.
 01 01 14 7A 07 31 02 01 00 32 01 20 05 04 00 89 2C 33
          ^
          +--current position
TlvReader newReader = reader.ReadNestedTlv(0x7A);
 This will return a new TlvReader object:
  new reader: 31 02 01 00 32 01 20
              ^
              +--current position
 After the call, the internal position of the original reader is at
 the position just after the NestedTlv's value.
 01 01 14 7A 07 31 02 01 00 32 01 20 05 04 00 89 2C 33
                                     ^
                                     +--current position

Exceptions

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

ReadString(int, Encoding)

Read the TLV at the current position, return the value as a string, and move the position to the byte beyond the current TLV.

public string ReadString(int expectedTag, Encoding encoding)

Parameters

expectedTag int

The tag that should be at the current position.

encoding Encoding

The scheme the method will use to convert the byte array into a string, such as System.Text.Encoding.ASCII or UTF8.

Returns

string

A string, the value.

Remarks

See the documentation for the method TlvWriter.WriteString for a discussion of strings and encodings.

The method will verify that the tag is expected. If it is, it will read the length, verify that there are enough bytes in the buffer to read, then read the value (a byte array) and returning it as a string, converting the bytes array following the scheme specified by the encoding argument.

Exceptions

ArgumentNullException

The encoding argument is null.

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

ReadUInt16(int, bool)

Read the TLV at the current position, return the value as an unsigned short (a two-byte unsigned integer), and move the position to the byte beyond the current TLV.

[CLSCompliant(false)]
public ushort ReadUInt16(int expectedTag, bool bigEndian = true)

Parameters

expectedTag int

The tag that should be at the current position.

bigEndian bool

Specifies whether the result should be returned as big endian (true or no argument given) or little endian (false).

Returns

ushort

The unsigned short value.

Remarks

The method will verify that the tag is expected. If it is, it will read the length, verify that it is two, then read and return that two-byte unsigned value.

If the length of the value is not 2 (even if it is 0 or 1), this method will not advance the reader and throw an exception.

The method will return the value in big endian (most significant byte of the short is taken from value[0]), unless the bigEndian argument is false. If it is false, the result is returned in little endian (least significant byte of the short is taken from value[0]). The bigEndian argument has a default of true. This means you can call this method and leave out the bigEndian argument, and the method will return the unsigned short in big endian format.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will not advance the reader and throw an exception.

Exceptions

TlvException

An unexpected error occurred while reading from the TLV.

ReadValue(int)

Read the TLV at the current position, return the value as a byte array, and move the position to the byte beyond the current TLV.

public ReadOnlyMemory<byte> ReadValue(int expectedTag)

Parameters

expectedTag int

The tag that should be at the current position.

Returns

ReadOnlyMemory<byte>

A new ReadOnlyMemory object containing the value, and only the value, of the current TLV. If there is no value (length is 0), the result will be an empty object (Length is 0).

Remarks

The method will verify that the tag is expected. If it is, it will read the length, verify that the encoding has at least that many bytes, then create a new ReadOnlyMemory object that points to the value. Note that the ReadOnlyMemory object will point to the existing encoding, it will not copy the data into a new buffer.

Note that this will not treat a NestedTlv any different from a single element. That is, if the current position points to a NestedTlv, the method will return a value that is the collection of sub-elements.

If the tag at the current position in the encoding is not what was given as the expectedTag, or if the tag and/or length make up an invalid encoding, or if there are not enough bytes left in the encoding to read the tag, length, and value, this method will throw an exception.

For example, suppose the encoding is the following.

7C 0D 01 01 14 02 02 01 80 05 04 00 89 2C 33
This is a NestedTlv
7C 0D
   01 01
      14
   02 02
      01 80
   05 04
      00 89 2C 33
Suppose the internal position is at 0, the beginning of full encoding.
value = reader.ReadValue(0x7C);
This will return a new ReadOnlyMemory object that points to
  01 01 14 02 02 01 80 05 04 00 89 2C 33
Length is 13
After the call, the internal position of the reader is at the
position just after the last byte of the value, which is beyond the
end of the full encoding. There's nothing more to read.
Suppose the internal position is at 5, the beginning of the second sub-element.
value = reader.ReadValue(0x02);
This will return a new ReadOnlyMemory object that points to
  01 80
Length is 2
After the call, the internal position of the reader is at the
position just after the last byte of the value, which is the
next TLV: 05 04 etc.

Note that the value returned is a reference to the input encoding. Do not clear or alter the encoding until after the full encoding has been read and each value operated on.

Exceptions

TlvException

The tag was not the expected value, the tag or length is unsupported, or there was not enough data for the lengths given.

TryReadByte(out byte, int)

Try to read the TLV at the current position. If this succeeds, return true and set the value argument to the byte that is the V part of the TLV, and move the position to the byte beyond the current TLV.

public bool TryReadByte(out byte value, int expectedTag)

Parameters

value byte

The output parameter where the value will be deposited.

expectedTag int

The tag that should be at the current position.

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadByte, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or the value is not a single byte, or there is not enough data in the buffer for the length given, this method will set value to 0 and will return false.

See the documentation for ReadByte for more information on what this method does.

Note that if there is a valid TLV with the expected tag, but the length is not 1, this method will return false.

TryReadEncoded(out ReadOnlyMemory<byte>, int)

Try to read the TLV at the current position. If this succeeds, return true and set the encoded argument to a new ReadOnlyMemory object containing the full TLV, and move the position to the byte beyond the current TLV.

public bool TryReadEncoded(out ReadOnlyMemory<byte> encoded, int expectedTag)

Parameters

encoded ReadOnlyMemory<byte>

The output parameter where the encoded TLV will be deposited.

expectedTag int

The tag that should be at the current position.

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadEncoded, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or there is not enough data in the buffer for the length given, this method will return false.

See the documentation for ReadEncoded for more information on what this method does.

TryReadInt16(out short, int, bool)

Try to read the TLV at the current position. If this succeeds, return true and set the value argument to the short that is the V part of the TLV, and move the position to the byte beyond the current TLV.

public bool TryReadInt16(out short value, int expectedTag, bool bigEndian = true)

Parameters

value short

The output parameter where the value will be deposited.

expectedTag int

The tag that should be at the current position.

bigEndian bool

Specifies whether the result should be returned as big endian (true or no argument given) or little endian (false).

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadInt16, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or the value is not exactly two bytes, or there is not enough data in the buffer for the length given, this method will set value to 0 and will return false.

See the documentation for ReadInt16 for more information on what this method does.

Note that if there is a valid TLV with the expected tag, but the length is not 2, this method will return false.

TryReadInt32(out int, int, bool)

Try to read the TLV at the current position. If this succeeds, return true and set the value argument to the int that is the V part of the TLV, and move the position to the byte beyond the current TLV.

public bool TryReadInt32(out int value, int expectedTag, bool bigEndian = true)

Parameters

value int

The output parameter where the value will be deposited.

expectedTag int

The tag that should be at the current position.

bigEndian bool

Specifies whether the result should be returned as big endian (true or no argument given) or little endian (false).

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadInt32, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or the value is not exactly four bytes, or there is not enough data in the buffer for the length given, this method will set value to 0 and will return false.

See the documentation for ReadInt32 for more information on what this method does.

Note that if there is a valid TLV with the expected tag, but the length is not 4, this method will return false.

TryReadNestedTlv(out TlvReader, int)

Try to read the TLV at the current position as a NestedTlv. If this succeeds, return true and set the nestedReader argument to a new TlvReader object whose position is the beginning of the NestedTlv's value, which is the tag of the NestedTlv's first sub-element. Move the position of the original reader to the byte beyond the current TLV.

public bool TryReadNestedTlv(out TlvReader nestedReader, int expectedTag)

Parameters

nestedReader TlvReader

On success, receives the new TlvReader object.

expectedTag int

The tag that should be at the current position, the NestedTlv's tag.

Returns

bool

Remarks

This is the same as ReadNestedTlv, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or there is not enough data in the buffer for the length given, this method will return false.

See the documentation for ReadNestedTlv for more information on what this method does.

TryReadString(out string, int, Encoding)

Try to read the TLV at the current position. If this succeeds, return true and set the value argument to a string, and move the position to the byte beyond the current TLV.

public bool TryReadString(out string value, int expectedTag, Encoding encoding)

Parameters

value string

The output parameter where the value will be deposited.

expectedTag int

The tag that should be at the current position.

encoding Encoding

The scheme the method will use to convert the byte array into a string, such as System.Text.Encoding.ASCII or UTF8.

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadString, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or there is not enough data in the buffer for the length given, this method will return false.

See the documentation for ReadString for more information on what this method does.

TryReadUInt16(out ushort, int, bool)

Try to read the TLV at the current position. If this succeeds, return true and set the value argument to the unsigned short that is the V part of the TLV, and move the position to the byte beyond the current TLV.

[CLSCompliant(false)]
public bool TryReadUInt16(out ushort value, int expectedTag, bool bigEndian = true)

Parameters

value ushort

The output parameter where the value will be deposited.

expectedTag int

The tag that should be at the current position.

bigEndian bool

Specifies whether the result should be returned as big endian (true or no argument given) or little endian (false).

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadUInt16(int, bool), except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or the value is not exactly two bytes, or there is not enough data in the buffer for the length given, this method will set value to 0 and will return false.

See ReadUInt16(int, bool) for more information on what this method does.

Note that if there is a valid TLV with the expected tag, but the length is not 2, this method will return false.

TryReadValue(out ReadOnlyMemory<byte>, int)

Try to read the TLV at the current position. If this succeeds, return true and set the value argument to a new ReadOnlyMemory object containing the value as a byte array, and move the position to the byte beyond the current TLV.

public bool TryReadValue(out ReadOnlyMemory<byte> value, int expectedTag)

Parameters

value ReadOnlyMemory<byte>

The output parameter where the value will be deposited.

expectedTag int

The tag that should be at the current position.

Returns

bool

A boolean, true if the read succeeds, false otherwise.

Remarks

This is the same as ReadValue, except this method will not throw an exception if there is an error in reading, only return false. That is, if the expected tag is not found at the current position, or the length octets are not a valid encoding, or there is not enough data in the buffer for the length given, this method will return false.

See the documentation for ReadValue for more information on what this method does.

Note that the value returned is a reference to the input encoding. Do not clear or alter the encoding until after the full encoding has been read and each value operated on.