The Structure and Implementation of StatusList in the TSL Specification
Introduction
The Token Status List (TSL) specification is nearing completion.
This specification is designed to handle status information (such as valid or invalid) for various token types based on JOSE and COSE, including JWT, SD-JWT VC, CWT, and ISO mdoc.
To efficiently represent the status of a large number of tokens, statuses are encoded as a bit array and compressed using DEFLATE.
The TSL specification defines a data structure called StatusList to represent this list of statuses. In this article, we take a closer look at the StatusList.
Structure of the StatusList
Regardless of whether the format is JSON or CBOR, a StatusList has the following properties:
bits— REQUIRED. The number of bits used to represent a single status. One of 1, 2, 4, or 8.lst— REQUIRED. A DEFLATE-compressed representation of the status values expressed as a byte array. In CBOR, it is represented as a byte array; in JSON, it is represented as a base64url-encoded string.aggregation_uri— OPTIONAL. A location from which a list of URIs that publish Status List Tokens can be obtained.
Below are examples of StatusList objects in JSON format, excerpted from the specification.
{
"bits": 1,
"lst": "eNrbuRgAAhcBXQ"
}{
"bits": 2,
"lst": "eNo76fITAAPfAgc"
}The bits property represents the number of bits used to encode a single status, and its possible values are 1, 2, 4, or 8. When the number of bits is 1, up to 2¹ = 2 different statuses can be represented, for example, “valid” and “invalid.” When the number of bits is 2, up to 2² = 4 statuses can be represented; with 4 bits, up to 2⁴ = 16 statuses; and with 8 bits, up to 2⁸ = 256 statuses can be represented.
The lst property contains the DEFLATE-compressed byte array that enumerates the set of statuses. In the CBOR format, the result of the DEFLATE compression is stored directly as a byte array, whereas in the JSON format, it is base64url-encoded and represented as a string.
For example, suppose the number of bits used to represent a status is set to 1, and the statuses of 16 tokens are as follows:
| Index | Status | Value |
|-------+---------+-------|
| 0 | INVALID | 1 |
| 1 | VALID | 0 |
| 2 | VALID | 0 |
| 3 | INVALID | 1 |
| 4 | INVALID | 1 |
| 5 | INVALID | 1 |
| 6 | VALID | 0 |
| 7 | INVALID | 1 |
| 8 | INVALID | 1 |
| 9 | INVALID | 1 |
| 10 | VALID | 0 |
| 11 | VALID | 0 |
| 12 | VALID | 0 |
| 13 | INVALID | 1 |
| 14 | VALID | 0 |
| 15 | INVALID | 1 |If these are packed as shown in the following figure, the resulting byte array is [0xB9, 0xA3].
This byte array is then compressed using DEFLATE and set as the value of lst.
Similarly, suppose the number of bits used to represent a status is 2, and the statuses of 12 tokens are as follows:
| Index | Status |
|-------+--------|
| 0 | 01 |
| 1 | 10 |
| 2 | 00 |
| 3 | 11 |
| 4 | 00 |
| 5 | 01 |
| 6 | 00 |
| 7 | 01 |
| 8 | 01 |
| 9 | 10 |
| 10 | 11 |
| 11 | 11 |When these statuses are packed as illustrated in the following figure, the resulting byte array is [0xC9, 0x44, 0xF9].
This byte array is then compressed using DEFLATE and set as the value of lst.
Implementation of the StatusList
The CBOR library authlete/cbor (for Java), which Authlete publishes as open source, includes a class named StatusList for representing a StatusList in the com.authlete.cbor.tsl package starting from version 1.21.
The constructor of this StatusList class takes the following arguments: int bits, byte[] lst, and String aggregationUri.
public StatusList(int bits, byte[] lst, String aggregationUri)However, preparing the value of lst in the first place is cumbersome, so the path to calling this constructor is not so easy. To address this, a utility class named StatusListBuilder is provided for creating instances of the StatusList class.
Get Takahiko Kawasaki’s stories in your inbox
Join Medium for free to get updates from this writer.
The usage of StatusListBuilder is as follows:
- Choose the number of bits used to represent each status from among 1, 2, 4, or 8.
- Create a
StatusListBuilderinstance by passing the chosen bit size to its constructor. - Call the
valueAtmethod as many times as needed to set individual status values. - If you want to set
aggregation_uri, call theaggregationUrimethod. - If you want to reserve capacity beyond the number of statuses automatically expanded by the indices specified via
valueAt, call thecapacitymethod. - Finally, call the
buildmethod to create aStatusListinstance.
For example, to create a StatusList that contains the following set of statuses listed in Section 4.1. Compressed Byte Array of the TSL specification,
status[ 0] = 0b1
status[ 1] = 0b0
status[ 2] = 0b0
status[ 3] = 0b1
status[ 4] = 0b1
status[ 5] = 0b1
status[ 6] = 0b0
status[ 7] = 0b1
status[ 8] = 0b1
status[ 9] = 0b1
status[10] = 0b0
status[11] = 0b0
status[12] = 0b0
status[13] = 0b1
status[14] = 0b0
status[15] = 0b1you can write the code as follows:
// Number of bits used to represent a status
int bits = 1;
// Create a StatusListBuilder instance by passing the number
// of bits to the constructor
StatusListBuilder builder = new StatusListBuilder(bits);
// Set status values
builder.valueAt(StatusTypeValue.INVALID, 0);
builder.valueAt(StatusTypeValue.VALID, 1);
builder.valueAt(StatusTypeValue.VALID, 2);
builder.valueAt(StatusTypeValue.INVALID, 3);
builder.valueAt(StatusTypeValue.INVALID, 4);
builder.valueAt(StatusTypeValue.INVALID, 5);
builder.valueAt(StatusTypeValue.VALID, 6);
builder.valueAt(StatusTypeValue.INVALID, 7);
builder.valueAt(StatusTypeValue.INVALID, 8);
builder.valueAt(StatusTypeValue.INVALID, 9);
builder.valueAt(StatusTypeValue.VALID, 10);
builder.valueAt(StatusTypeValue.VALID, 11);
builder.valueAt(StatusTypeValue.VALID, 12);
builder.valueAt(StatusTypeValue.INVALID, 13);
builder.valueAt(StatusTypeValue.VALID, 14);
builder.valueAt(StatusTypeValue.INVALID, 15);
// Create a StatusList instance
StatusList statusList = builder.build();StatusList is a subclass of CBORItem, so you can use the encodeToHex() method to represent it in hexadecimal,
System.out.println(statusList.encodeToHex());a2646269747301636c73744a78dadbb918000217015dor use the toString() or prettify() methods to represent it in CBOR Diagnostic Notation.
System.out.println(statusList.prettify());{
"bits": 1,
"lst": h'78dadbb918000217015d'
}By the way, if you convert the byte array to a base64url string using the Base64UrlAdapter introduced in “GSON: Converting Byte Arrays to and from Base64URL Strings” (or by any other method—what matters is simply converting the lst value from a byte array to a base64url string),
// Create a Gson instance with Base64UrlAdapter enabled
Gson gson = new GsonBuilder()
.registerTypeAdapter(byte[].class, new Base64UrlAdapter())
.setPrettyPrinting()
.create();
// Convert the CBORItem into a general-purpose Java object using
// the parse() method
Object statusListObject = statusList.parse();
// Convert to JSON
String json = gson.toJson(statusListObject);
// Output the JSON
System.out.println(json);you can generate JSON that complies with the requirements defined in Section 4.2. Status List in JSON Format of the TSL specification.
{
"bits": 1,
"lst": "eNrbuRgAAhcBXQ"
}Conclusion
When you need to work with CBOR in Java, please give the authlete/cbor library a try. Authlete itself also uses the authlete/cbor library in its implementation of OpenID for Verifiable Credential Issuance 1.0 (OID4VCI).
We also operate a website called CBOR Zone, which allows you to convert CBOR data between the following formats. It’s quite handy, so be sure to check it out!
- Hexadecimal
- Base64
- Base64URL
- Diagnostic Notation
(This screenshot shows the conversion from the hexadecimal representation of a StatusList to Diagnostic Notation.)









