Welcome to Python CWT

Python CWT is a CBOR Web Token (CWT) and CBOR Object Signing and Encryption (COSE) implementation compliant with:

It is designed to make users who already know about JSON Web Token (JWT) be able to use it in ease. Little knowledge of CBOR, COSE, and CWT is required to use it.

You can install Python CWT with pip:

$ pip install cwt

And then, you can use it as follows:

import cwt
from cwt import COSEKey

key = COSEKey.from_symmetric_key(alg="HMAC 256/256")
token = cwt.encode({"iss": "https://as.example", "sub": "dajiaji", "cti": "123"}, key)
decoded = cwt.decode(token, key)

Index

Installation

You can install Python CWT with pip:

$ pip install cwt

Usage Examples

The following is a simple sample code for command line console.

>>> import cwt
>>> from cwt import Claims, COSEKey
>>> key = COSEKey.from_symmetric_key(alg="HS256")
>>> token = cwt.encode({"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, key)
>>> token.hex()
'd18443a10105a05835a60172636f6170733a2f2f61732e6578616d706c65026764616a69616a69'
'0743313233041a609097b7051a609089a7061a609089a758201fad9b0a76803194bd11ca9b9b3c'
'bbf1028005e15321665a768994f38c7127f7'
>>> decoded = cwt.decode(token, key)
>>> decoded
{1: 'coaps://as.example', 2: 'dajiaji', 7: b'123',
 4: 1620088759, 5: 1620085159, 6: 1620085159}
>>> readable = Claims.from_dict(decoded)
>>> readable.iss
'coaps://as.example'
>>> readable.sub
'dajiaji'
>>> readable.exp
1620088759

This page shows various examples to use this library. Specific examples are as follows:

MACed CWT

Create a MACed CWT, verify and decode it as follows:

import cwt
from cwt import COSEKey

try:
    key = COSEKey.from_symmetric_key(alg="HS256")
    token = cwt.encode(
        {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"},
        key,
    )
    decoded = cwt.decode(token, key)
except Exception as err:
    # All the other examples in this document omit error handling but this CWT library
    # can throw following errors:
    #   ValueError: Invalid arguments.
    #   EncodeError: Failed to encode.
    #   VerifyError: Failed to verify.
    #   DecodeError: Failed to decode.
    print(err)

CBOR-like structure (Dict[int, Any]) can also be used as follows:

import cwt
from cwt import COSEKey

key = COSEKey.from_symmetric_key(alg="HS256")
token = cwt.encode({1: "coaps://as.example", 2: "dajiaji", 7: b"123"}, key)
decoded = cwt.decode(token, key)

Algorithms other than HS256 are listed in Supported COSE Algorithms .

Signed CWT

Create an Ed25519 (Ed25519 for use w/ EdDSA only) key pair:

$ openssl genpkey -algorithm ed25519 -out private_key.pem
$ openssl pkey -in private_key.pem -pubout -out public_key.pem

Create a Signed CWT, verify and decode it with the key pair as follows:

import cwt
from cwt import COSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="01")
with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="01")


token = cwt.encode(
    {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)

decoded = cwt.decode(token, public_key)

JWKs can also be used instead of the PEM-formatted keys as follows:

import cwt
from cwt import COSEKey

private_key = COSEKey.from_jwk(
    {
        "kty": "OKP",
        "d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
        "use": "sig",
        "crv": "Ed25519",
        "kid": "01",
        "x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
        "alg": "EdDSA",
    }
)
public_key = COSEKey.from_jwk(
    {
        "kty": "OKP",
        "use": "sig",
        "crv": "Ed25519",
        "kid": "01",
        "x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
    }
)

token = cwt.encode(
    {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)
decoded = cwt.decode(token, public_key)

Algorithms other than Ed25519 are also supported. The following is an example of ES256:

$ openssl ecparam -genkey -name prime256v1 -noout -out private_key.pem
$ openssl ec -in private_key.pem -pubout -out public_key.pem
import cwt
from cwt import COSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="01")
with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="01")

token = cwt.encode(
    {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)

decoded = cwt.decode(token, public_key)

Other supported algorithms are listed in Supported COSE Algorithms.

Encrypted CWT

Create an encrypted CWT with ChaCha20/Poly1305 (ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag), and decrypt it as follows:

import cwt
from cwt import COSEKey

enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305")
token = cwt.encode(
    {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, enc_key
)
decoded = cwt.decode(token, enc_key)

Algorithms other than ChaCha20/Poly1305 are also supported. The following is an example of AES-CCM-16-64-256:

import cwt
from cwt import COSEKey

enc_key = COSEKey.from_symmetric_key(alg="AES-CCM-16-64-256")
token = cwt.encode(
    {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, enc_key
)
decoded = cwt.decode(token, enc_key)

Other supported algorithms are listed in Supported COSE Algorithms.

Nested CWT

Create a signed CWT and encrypt it, and then decrypt and verify the nested CWT as follows.

import cwt
from cwt import COSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="01")
with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="01")

# Creates a CWT with ES256 signing.
token = cwt.encode(
    {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)

# Encrypts the signed CWT.
enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305")
nested = cwt.encode(token, enc_key)

# Decrypts and verifies the nested CWT.
decoded = cwt.decode(nested, [enc_key, public_key])

CWT with User-Defined Claims

You can use your own claims as follows:

Note that such user-defined claim’s key should be less than -65536.

import cwt
from cwt import COSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="01")
with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="01")
token = cwt.encode(
    {
        1: "coaps://as.example",  # iss
        2: "dajiaji",  # sub
        7: b"123",  # cti
        -70001: "foo",
        -70002: ["bar"],
        -70003: {"baz": "qux"},
        -70004: 123,
    },
    private_key,
)
raw = cwt.decode(token, public_key)
# raw[-70001] == "foo"
# raw[-70002][0] == "bar"
# raw[-70003]["baz"] == "qux"
# raw[-70004] == 123
readable = Claims.from_dict(raw)
# readable.get(-70001) == "foo"
# readable.get(-70002)[0] == "bar"
# readable.get(-70003)["baz"] == "qux"
# readable.get(-70004) == 123

User-defined claims can also be used with JSON-based claims as follows:

import cwt
from cwt import Claims, COSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="01")
with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="01")

cwt.set_private_claim_names(
    {
        "ext_1": -70001,
        "ext_2": -70002,
        "ext_3": -70003,
        "ext_4": -70004,
    }
)
token = cwt.encode(
    {
        "iss": "coaps://as.example",
        "sub": "dajiaji",
        "cti": b"123",
        "ext_1": "foo",
        "ext_2": ["bar"],
        "ext_3": {"baz": "qux"},
        "ext_4": 123,
    },
    private_key,
)
claims.set_private_claim_names()
raw = cwt.decode(token, public_key)
readable = Claims.from_dict(
    raw,
    private_claim_names={
        "ext_1": -70001,
        "ext_2": -70002,
        "ext_3": -70003,
        "ext_4": -70004,
    },
)
# readable.get("ext_1") == "foo"
# readable.get("ext_2")[0] == "bar"
# readable.get("ext_3")["baz"] == "qux"
# readable.get("ext_4") == 123

CWT with PoP key

Create a CWT which has a PoP key as follows:

On the issuer side:

import cwt
from cwt import COSEKey

# Prepares a signing key for CWT in advance.
with open("./private_key_of_issuer.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")

# Sets the PoP key to a CWT for the presenter.
token = cwt.encode(
    {
        "iss": "coaps://as.example",
        "sub": "dajiaji",
        "cti": "123",
        "cnf": {
            "jwk": {  # Provided by the CWT presenter.
                "kty": "OKP",
                "use": "sig",
                "crv": "Ed25519",
                "kid": "01",
                "x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
                "alg": "EdDSA",
            },
        },
    },
    private_key,
)

# Issues the token to the presenter.

On the CWT presenter side:

import cwt
from cwt import COSEKey

# Prepares a private PoP key in advance.
with open("./private_pop_key.pem") as key_file:
    pop_key_private = COSEKey.from_pem(key_file.read(), kid="01")

# Receives a message (e.g., nonce)  from the recipient.
msg = b"could-you-sign-this-message?"  # Provided by recipient.

# Signs the message with the private PoP key.
sig = pop_key_private.sign(msg)

# Sends the msg and the sig with the CWT to the recipient.

On the CWT recipient side:

import cwt
from cwt import Claims, COSEKey

# Prepares the public key of the issuer in advance.
with open("./public_key_of_issuer.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")

# Verifies and decodes the CWT received from the presenter.
raw = cwt.decode(token, public_key)
decoded = Claims.from_dict(raw)

# Extracts the PoP key from the CWT.
extracted_pop_key = COSEKey.from_dict(decoded.cnf)  #  = raw[8][1]

# Then, verifies the message sent by the presenter
# with the signature which is also sent by the presenter as follows:
extracted_pop_key.verify(msg, sig)

In case of another PoP confirmation method Encrypted_COSE_Key:

import cwt
from cwt import Claims, COSEKey, EncryptedCOSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")

enc_key = COSEKey.from_symmetric_key(
    "a-client-secret-of-cwt-recipient",  # Just 32 bytes!
    alg="ChaCha20/Poly1305",
)
pop_key = COSEKey.from_symmetric_key(
    "a-client-secret-of-cwt-presenter",
    alg="HMAC 256/256",
)

token = cwt.encode(
    {
        "iss": "coaps://as.example",
        "sub": "dajiaji",
        "cti": "123",
        "cnf": {
            # 'eck'(Encrypted Cose Key) is a keyword defined by this library.
            "eck": EncryptedCOSEKey.from_cose_key(pop_key, enc_key),
        },
    },
    private_key,
)

with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")
raw = cwt.decode(token, public_key)
decoded = Claims.from_dict(raw)
extracted_pop_key = EncryptedCOSEKey.to_cose_key(decoded.cnf, enc_key)
# extracted_pop_key.verify(message, signature)

In case of another PoP confirmation method kid:

import cwt
from cwt import Claims, COSEKey

with open("./private_key.pem") as key_file:
    private_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")

token = cwt.encode(
    {
        "iss": "coaps://as.example",
        "sub": "dajiaji",
        "cti": "123",
        "cnf": {
            "kid": "pop-key-id-of-cwt-presenter",
        },
    },
    private_key,
)

with open("./public_key.pem") as key_file:
    public_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")
raw = cwt.decode(token, public_key)
decoded = Claims.from_dict(raw)
# decoded.cnf(=raw[8][3]) is kid.

API Reference

A Python implementation of CWT/COSE <https://python-cwt.readthedocs.io>

cwt.encode(claims: Union[cwt.claims.Claims, Dict[str, Any], Dict[int, Any], bytes, str], key: cwt.cose_key_interface.COSEKeyInterface, nonce: bytes = b'', tagged: bool = False, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None)bytes

Encodes CWT with MAC, signing or encryption. This is a wrapper function of the following functions for easy use:

Therefore, it must be clear whether the use of the specified key is for MAC, signing, or encryption. For this purpose, the key must have the key_ops parameter set to identify the usage.

Parameters
  • claims (Union[Claims, Dict[str, Any], Dict[int, Any], bytes, str]) – A CWT claims object, or a JWT claims object, text string or byte string.

  • key (COSEKeyInterface) – A COSE key used to generate a MAC for the claims.

  • recipients (List[RecipientInterface]) – A list of recipient information structures.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

cwt.encode_and_mac(claims: Union[cwt.claims.Claims, Dict[int, Any], bytes], key: cwt.cose_key_interface.COSEKeyInterface, tagged: bool = False, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None)bytes

Encodes with MAC.

Parameters
  • claims (Union[Claims, Dict[int, Any], bytes]) – A CWT claims object or byte string.

  • key (COSEKeyInterface) – A COSE key used to generate a MAC for the claims.

  • recipients (List[RecipientInterface]) – A list of recipient information structures.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

cwt.encode_and_sign(claims: Union[cwt.claims.Claims, Dict[int, Any], bytes], key: Union[cwt.cose_key_interface.COSEKeyInterface, List[cwt.cose_key_interface.COSEKeyInterface]], tagged: bool = False)bytes

Encodes CWT with signing.

Parameters
  • claims (Claims, Union[Dict[int, Any], bytes]) – A CWT claims object or byte string.

  • key (Union[COSEKeyInterface, List[COSEKeyInterface]]) – A COSE key or a list of the keys used to sign claims.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

cwt.encode_and_encrypt(claims: Union[cwt.claims.Claims, Dict[int, Any], bytes], key: cwt.cose_key_interface.COSEKeyInterface, nonce: bytes = b'', tagged: bool = False, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None)bytes

Encodes CWT with encryption.

Parameters
  • claims (Claims, Union[Dict[int, Any], bytes]) – A CWT claims object or byte string.

  • key (COSEKeyInterface) – A COSE key used to encrypt the claims.

  • nonce (bytes) – A nonce for encryption.

  • recipients (List[RecipientInterface]) – A list of recipient information structures.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

cwt.decode(data: bytes, key: Union[cwt.cose_key_interface.COSEKeyInterface, List[cwt.cose_key_interface.COSEKeyInterface]], no_verify: bool = False)Union[Dict[int, Any], bytes]

Verifies and decodes CWT.

Parameters
  • data (bytes) – A byte string of an encoded CWT.

  • key (Union[COSEKeyInterface, List[COSEKeyInterface]]) – A COSE key or a list of the keys used to verify and decrypt the encoded CWT.

  • no_verify (bool) – An indicator whether token verification is skiped or not.

Returns

A byte string of the decoded CWT.

Return type

Union[Dict[int, Any], bytes]

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the CWT.

  • VerifyError – Failed to verify the CWT.

cwt.set_private_claim_names(claim_names: Dict[str, int])

Sets private claim definitions. The definitions will be used in encode when it is called with JSON-based claims.

Parameters

claims (Dict[str, int]) – A set of private claim definitions which consist of a readable claim name(str) and a claim key(int). The claim key should be less than -65536 but you can use the numbers other than pre-registered numbers listed in IANA Registry.

Raises

ValueError – Invalid arguments.

class cwt.CWT(options: Optional[Dict[str, Any]] = None)[source]

Bases: cwt.cbor_processor.CBORProcessor

A CWT (CBOR Web Token) Implementaion, which is built on top of COSE

cwt.cwt is a global object of this class initialized with default settings.

CBOR_TAG = 61
__init__(options: Optional[Dict[str, Any]] = None)[source]

Constructor.

Parameters

options (Optional[Dict[str, Any]]) – Options for the initial configuration of CWT. At this time, expires_in (default value: 3600 ) and leaway (default value: 60) are only supported. See also expires_in, leeway.

Examples

>>> from cwt import CWT, claims, cose_key
>>> ctx = CWT({"expires_in": 3600*24, "leeway": 10})
>>> key = cose_key.from_symmetric_key(alg="HS255")
>>> token = ctx.encode(
...     {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"},
...     key,
... )
property expires_in: int

The default lifetime in seconds of CWT. If exp is not found in claims, this value will be used with current time.

property leeway: int

The default leeway in seconds for validating exp and nbf.

encode(claims: Union[cwt.claims.Claims, Dict[str, Any], Dict[int, Any], bytes, str], key: cwt.cose_key_interface.COSEKeyInterface, nonce: bytes = b'', tagged: bool = False, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None)bytes[source]

Encodes CWT with MAC, signing or encryption. This is a wrapper function of the following functions for easy use:

Therefore, it must be clear whether the use of the specified key is for MAC, signing, or encryption. For this purpose, the key must have the key_ops parameter set to identify the usage.

Parameters
  • claims (Union[Claims, Dict[str, Any], Dict[int, Any], bytes, str]) – A CWT claims object, or a JWT claims object, text string or byte string.

  • key (COSEKeyInterface) – A COSE key used to generate a MAC for the claims.

  • recipients (List[RecipientInterface]) – A list of recipient information structures.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

encode_and_mac(claims: Union[cwt.claims.Claims, Dict[int, Any], bytes], key: cwt.cose_key_interface.COSEKeyInterface, tagged: bool = False, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None)bytes[source]

Encodes with MAC.

Parameters
  • claims (Union[Claims, Dict[int, Any], bytes]) – A CWT claims object or byte string.

  • key (COSEKeyInterface) – A COSE key used to generate a MAC for the claims.

  • recipients (List[RecipientInterface]) – A list of recipient information structures.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

encode_and_sign(claims: Union[cwt.claims.Claims, Dict[int, Any], bytes], key: Union[cwt.cose_key_interface.COSEKeyInterface, List[cwt.cose_key_interface.COSEKeyInterface]], tagged: bool = False)bytes[source]

Encodes CWT with signing.

Parameters
  • claims (Claims, Union[Dict[int, Any], bytes]) – A CWT claims object or byte string.

  • key (Union[COSEKeyInterface, List[COSEKeyInterface]]) – A COSE key or a list of the keys used to sign claims.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

encode_and_encrypt(claims: Union[cwt.claims.Claims, Dict[int, Any], bytes], key: cwt.cose_key_interface.COSEKeyInterface, nonce: bytes = b'', tagged: bool = False, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None)bytes[source]

Encodes CWT with encryption.

Parameters
  • claims (Claims, Union[Dict[int, Any], bytes]) – A CWT claims object or byte string.

  • key (COSEKeyInterface) – A COSE key used to encrypt the claims.

  • nonce (bytes) – A nonce for encryption.

  • recipients (List[RecipientInterface]) – A list of recipient information structures.

  • tagged (bool) – An indicator whether the response is wrapped by CWT tag(61) or not.

Returns

A byte string of the encoded CWT.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode the claims.

decode(data: bytes, key: Union[cwt.cose_key_interface.COSEKeyInterface, List[cwt.cose_key_interface.COSEKeyInterface]], no_verify: bool = False)Union[Dict[int, Any], bytes][source]

Verifies and decodes CWT.

Parameters
  • data (bytes) – A byte string of an encoded CWT.

  • key (Union[COSEKeyInterface, List[COSEKeyInterface]]) – A COSE key or a list of the keys used to verify and decrypt the encoded CWT.

  • no_verify (bool) – An indicator whether token verification is skiped or not.

Returns

A byte string of the decoded CWT.

Return type

Union[Dict[int, Any], bytes]

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the CWT.

  • VerifyError – Failed to verify the CWT.

set_private_claim_names(claim_names: Dict[str, int])[source]

Sets private claim definitions. The definitions will be used in encode when it is called with JSON-based claims.

Parameters

claims (Dict[str, int]) –

A set of private claim definitions which consist of a readable claim name(str) and a claim key(int). The claim key should be less than -65536 but you can use the numbers other than pre-registered numbers listed in IANA Registry.

Raises

ValueError – Invalid arguments.

class cwt.COSE(options: Optional[Dict[str, Any]] = None)[source]

Bases: cwt.cbor_processor.CBORProcessor

A COSE (CBOR Object Signing and Encryption) Implementaion built on top of cbor2.

cwt.cose_key is a global object of this class initialized with default settings.

__init__(options: Optional[Dict[str, Any]] = None)[source]

Constructor.

Parameters

options (Optional[Dict[str, Any]]) – Options for the initial configuration of COSE. At this time, kid_auto_inclusion (default value: True) and alg_auto_inclusion (default value: True) are supported.

encode_and_mac(payload: bytes, key: cwt.cose_key_interface.COSEKeyInterface, protected: Optional[Union[Dict[int, Any], bytes]] = None, unprotected: Optional[Dict[int, Any]] = None, recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None, external_aad: bytes = b'', out: str = '')Union[bytes, _cbor2.CBORTag][source]

Encodes data with MAC.

Parameters
  • payload (bytes) – A content to be MACed.

  • key (COSEKeyInterface) – A COSE key as a MAC Authentication key.

  • protected (Union[Dict[int, Any], bytes]) – Parameters that are to be cryptographically protected.

  • unprotected (Dict[int, Any]) – Parameters that are not cryptographically protected.

  • recipients (Optional[List[RecipientInterface]]) – A list of recipient information structures.

  • external_aad (bytes) – External additional authenticated data supplied by application.

  • out (str) –

    An output format. Only "cbor2/CBORTag" can be used. If "cbor2/CBORTag" is specified. This function will return encoded data as cbor2’s CBORTag object. If any other value is specified, it will return encoded data as bytes.

Returns

A byte string of the encoded COSE or a cbor2.CBORTag object.

Return type

Union[bytes, CBORTag]

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode data.

encode_and_sign(payload: bytes, key: Union[cwt.cose_key_interface.COSEKeyInterface, List[cwt.cose_key_interface.COSEKeyInterface]], protected: Optional[Union[Dict[int, Any], bytes]] = None, unprotected: Optional[Dict[int, Any]] = None, external_aad: bytes = b'', out: str = '')Union[bytes, _cbor2.CBORTag][source]

Encodes data with signing.

Parameters
  • payload (bytes) – A content to be signed.

  • key (Union[COSEKeyInterface, List[COSEKeyInterface]]) – One or more COSE keys as signing keys.

  • protected (Union[Dict[int, Any], bytes]) – Parameters that are to be cryptographically protected.

  • unprotected (Dict[int, Any]) – Parameters that are not cryptographically protected.

  • external_aad (bytes) – External additional authenticated data supplied by application.

  • out (str) –

    An output format. Only "cbor2/CBORTag" can be used. If "cbor2/CBORTag" is specified. This function will return encoded data as cbor2’s CBORTag object. If any other value is specified, it will return encoded data as bytes.

Returns

A byte string of the encoded COSE or a cbor2.CBORTag object.

Return type

Union[bytes, CBORTag]

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode data.

encode_and_encrypt(payload: bytes, key: cwt.cose_key_interface.COSEKeyInterface, protected: Optional[Union[Dict[int, Any], bytes]] = None, unprotected: Optional[Dict[int, Any]] = None, nonce: bytes = b'', recipients: Optional[List[cwt.recipient_interface.RecipientInterface]] = None, external_aad: bytes = b'', out: str = '')bytes[source]

Encodes data with encryption.

Parameters
  • payload (bytes) – A content to be encrypted.

  • key (COSEKeyInterface) – A COSE key as an encryption key.

  • protected (Union[Dict[int, Any], bytes]) – Parameters that are to be cryptographically protected.

  • unprotected (Dict[int, Any]) – Parameters that are not cryptographically protected.

  • nonce (bytes) – A nonce for encryption.

  • recipients (Optional[List[RecipientInterface]]) – A list of recipient information structures.

  • external_aad (bytes) – External additional authenticated data supplied by application.

  • out (str) –

    An output format. Only "cbor2/CBORTag" can be used. If "cbor2/CBORTag" is specified. This function will return encoded data as cbor2’s CBORTag object. If any other value is specified, it will return encoded data as bytes.

Returns

A byte string of the encoded COSE or a cbor2.CBORTag object.

Return type

Union[bytes, CBORTag]

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encode data.

decode(data: Union[bytes, _cbor2.CBORTag], key: Optional[Union[cwt.cose_key_interface.COSEKeyInterface, List[cwt.cose_key_interface.COSEKeyInterface]]] = None, materials: Optional[List[dict]] = None, external_aad: bytes = b'')bytes[source]

Verifies and decodes COSE data.

Parameters
  • data (Union[bytes, CBORTag]) – A byte string or cbor2.CBORTag of an encoded data.

  • key (Optional[Union[COSEKeyInterface, List[COSEKeyInterface]]]) – A COSE key to verify and decrypt the encoded data.

  • materials (Optional[List[dict]]) – A list of key materials to be used to derive an encryption key.

  • external_aad (bytes) – External additional authenticated data supplied by application.

Returns

A byte string of decoded payload.

Return type

bytes

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode data.

  • VerifyError – Failed to verify data.

class cwt.COSEKey[source]

Bases: object

A COSEKeyInterface Builder.

static from_dict(cose_key: Dict[int, Any])cwt.cose_key_interface.COSEKeyInterface[source]

Create a COSE key from a CBOR-like dictionary with numeric keys.

Parameters

cose_key (Dict[int, Any]) – A CBOR-like dictionary with numeric keys of a COSE key.

Returns

A COSE key object.

Return type

COSEKeyInterface

Raises

ValueError – Invalid arguments.

classmethod from_symmetric_key(key: Union[bytes, str] = b'', alg: Union[int, str] = 'HMAC 256/256', kid: Union[bytes, str] = b'', key_ops: Optional[Union[List[int], List[str]]] = None)cwt.cose_key_interface.COSEKeyInterface[source]

Create a COSE key from a symmetric key.

Parameters
  • key (Union[bytes, str]) – A key bytes or string.

  • alg (Union[int, str]) – An algorithm label(int) or name(str). Supported alg are listed in Supported COSE Algorithms.

  • kid (Union[bytes, str]) – A key identifier.

  • key_ops (Union[List[int], List[str]]) – A list of key operation values. Following values can be used: 1("sign"), 2("verify"), 3("encrypt"), 4("decrypt"), 5("wrap key"), 6("unwrap key"), 7("derive key"), 8("derive bits"), 9("MAC create"), 10("MAC verify")

Returns

A COSE key object.

Return type

COSEKeyInterface

Raises

ValueError – Invalid arguments.

classmethod from_bytes(key_data: bytes)cwt.cose_key_interface.COSEKeyInterface[source]

Create a COSE key from CBOR-formatted key data.

Parameters

key_data (bytes) – CBOR-formatted key data.

Returns

A COSE key object.

Return type

COSEKeyInterface

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the key data.

classmethod from_jwk(data: Union[str, bytes, Dict[str, Any]])cwt.cose_key_interface.COSEKeyInterface[source]

Create a COSE key from JWK (JSON Web Key).

Parameters

jwk (Union[str, bytes, Dict[str, Any]]) – JWK-formatted key data.

Returns

A COSE key object.

Return type

COSEKeyInterface

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the key data.

classmethod from_pem(key_data: Union[str, bytes], alg: Union[int, str] = '', kid: Union[bytes, str] = b'', key_ops: Optional[Union[List[int], List[str]]] = None)cwt.cose_key_interface.COSEKeyInterface[source]

Create a COSE key from PEM-formatted key data.

Parameters
  • key_data (bytes) – A PEM-formatted key data.

  • alg (Union[int, str]) – An algorithm label(int) or name(str). Different from ::func::cwt.COSEKey.from_symmetric_key, it is only used when an algorithm cannot be specified by the PEM data, such as RSA family algorithms.

  • kid (Union[bytes, str]) – A key identifier.

  • key_ops (Union[List[int], List[str]]) – A list of key operation values. Following values can be used: 1("sign"), 2("verify"), 3("encrypt"), 4("decrypt"), 5("wrap key"), 6("unwrap key"), 7("derive key"), 8("derive bits"), 9("MAC create"), 10("MAC verify")

Returns

A COSE key object.

Return type

COSEKeyInterface

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the key data.

class cwt.EncryptedCOSEKey[source]

Bases: cwt.cbor_processor.CBORProcessor

An encrypted COSE key.

static from_cose_key(key: cwt.cose_key_interface.COSEKeyInterface, encryption_key: cwt.cose_key_interface.COSEKeyInterface, nonce: bytes = b'', tagged: bool = False)Union[List[Any], bytes][source]

Returns an encrypted COSE key formatted to COSE_Encrypt0 structure.

Parameters
  • key – COSEKeyInterface: A key to be encrypted.

  • encryption_key – COSEKeyInterface: An encryption key to encrypt the target COSE key.

  • nonce (bytes) – A nonce for encryption.

Returns

A COSE_Encrypt0 structure of the target COSE key.

Return type

List[Any]

Raises
  • ValueError – Invalid arguments.

  • EncodeError – Failed to encrypt the COSE key.

static to_cose_key(key: List[Any], encryption_key: cwt.cose_key_interface.COSEKeyInterface)cwt.cose_key_interface.COSEKeyInterface[source]

Returns an decrypted COSE key.

Parameters
  • key – COSEKeyInterface: A key formatted to COSE_Encrypt0 structure to be decrypted.

  • encryption_key – COSEKeyInterface: An encryption key to decrypt the target COSE key.

Returns

A key decrypted.

Return type

COSEKeyInterface

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the COSE key.

  • VerifyError – Failed to verify the COSE key.

class cwt.Claims(claims: Dict[int, Any], claim_names: Dict[str, int] = {'EAT-FDO': - 257, 'EATMAROEPrefix': - 258, 'EUPHNonce': - 259, 'aud': 3, 'cnf': 8, 'cti': 7, 'dbgstat': 16, 'eat_profile': 18, 'exp': 4, 'hcert': - 260, 'iat': 6, 'iss': 1, 'location': 17, 'nbf': 5, 'nonce': 10, 'oemid': 13, 'secboot': 15, 'seclevel': 14, 'sub': 2, 'submods': 20, 'ueid': 11})[source]

Bases: object

A class for handling CWT Claims like JWT claims.

classmethod from_dict(claims: Dict[int, Any], private_claim_names: Dict[str, int] = {})Any[source]

Create a Claims object from a CBOR-like(Dict[int, Any]) claim object.

Parameters
  • claims (Dict[str, Any]) – A CBOR-like(Dict[int, Any]) claim object.

  • private_claim_names (Dict[str, int]) –

    A set of private claim definitions which consist of a readable claim name(str) and a claim key(int). The claim key should be less than -65536 but you can use the numbers other than pre-registered numbers listed in IANA Registry.

Returns

A CWT claims object.

Return type

Claims

Raises

ValueError – Invalid arguments.

classmethod from_json(claims: Union[str, bytes, Dict[str, Any]], private_claim_names: Dict[str, int] = {})cwt.claims.T[source]

Converts a JWT claims object into a CWT claims object which has numeric keys. If a key string in JSON data cannot be mapped to a numeric key, it will be skipped.

Parameters
  • claims (Union[str, bytes, Dict[str, Any]]) – A JWT claims object to be converted.

  • private_claim_names (Dict[str, int]) –

    A set of private claim definitions which consist of a readable claim name(str) and a claim key(int). The claim key should be less than -65536 but you can use the numbers other than pre-registered numbers listed in IANA Registry.

Returns

A CWT claims object.

Return type

Claims

Raises

ValueError – Invalid arguments.

classmethod validate(claims: Dict[int, Any])[source]

Validates a CWT claims object.

Parameters

claims (Dict[int, Any]) – A CWT claims object to be validated.

Raises

ValueError – Failed to verify.

property iss: Optional[str]
property sub: Optional[str]
property aud: Optional[str]
property exp: Optional[int]
property nbf: Optional[int]
property iat: Optional[int]
property cti: Optional[str]
property cnf: Optional[Union[Dict[int, Any], List[Any], str]]
get(key: Union[str, int])Any[source]

Gets a claim value with a claim key.

Parameters

key (Union[str, int]) – A claim key.

Returns

The value of the claim.

Return type

Any

to_dict()Dict[int, Any][source]
class cwt.Recipient[source]

Bases: object

A RecipientInterface Builder.

classmethod from_dict(protected: Dict[int, Any], unprotected: Dict[int, Any], ciphertext: bytes = b'', recipients: List[Any] = [], key_ops: List[int] = [], key: bytes = b'')cwt.recipient_interface.RecipientInterface[source]

Create a recipient from a CBOR-like dictionary with numeric keys.

Parameters
  • protected (Dict[int, Any]) – Parameters that are to be cryptographically protected.

  • unprotected (Dict[int, Any]) – Parameters that are not cryptographically protected.

Returns

A recipient object.

Return type

RecipientInterface

Raises

ValueError – Invalid arguments.

classmethod from_json(data: Union[str, bytes, Dict[str, Any]])cwt.recipient_interface.RecipientInterface[source]

Create a recipient from JSON-formatted recipient data.

Parameters

data (Union[str, bytes, Dict[str, Any]]) – JSON-formatted recipient data.

Returns

A recipient object.

Return type

RecipientInterface

Raises
  • ValueError – Invalid arguments.

  • DecodeError – Failed to decode the key data.

exception cwt.CWTError[source]

Bases: Exception

Base class for all exceptions.

exception cwt.EncodeError[source]

Bases: cwt.exceptions.CWTError

An Exception occurred when a CWT/COSE encoding process failed.

exception cwt.DecodeError[source]

Bases: cwt.exceptions.CWTError

An Exception occurred when a CWT/COSE decoding process failed.

exception cwt.VerifyError[source]

Bases: cwt.exceptions.CWTError

An Exception occurred when a verification process failed.

Supported CWT Claims

IANA Registry for CWT Claims lists all of registered CWT claims. This section shows the claims which this library currently supports. In particular, class CWT can validate the type of the claims and Claims.from_json can convert the following Names(str) into Values(int).

CBOR Web Token (CWT) Claims

Name

Status

Value

Description

hcert

-260

Health Certificate

EUPHNonce

-259

Challenge Nonce defined in FIDO Device Onboarding

EATMAROEPrefix

-258

Signing prefix for multi-app restricted operating
environments

EAT-FDO

-257

EAT-FDO may contain related to FIDO Device Onboarding

iss

1

Issuer

sub

2

Subject

aud

3

Audience

exp

4

Expiration Time

nbf

5

Not Before

iat

6

Issued At

cti

7

CWT ID

cnf

8

Confirmation

nonce

10

Nonce

ueid

11

Universal Entity ID Claim

oemid

13

OEM Identification by IEEE

seclevel

14

Security Level

secboot

15

Secure Boot

dbgstat

16

Debug Status

location

17

Location

eat_profile

18

EAT Profile

submods

20

The Submodules Part of a Token

CWT Confirmation Methods

Name

Status

Value

Description

COSE_Key

1

COSE_Key Representing Public Key

Encrypted_COSE_Key

2

Encrypted COSE_Key

kid

3

Key Identifier

Supported COSE Algorithms

IANA Registry for COSE lists many cryptographic algorithms for MAC, signing, and encryption. This section shows the algorithms which this library currently supports.

  • ✅ : Supported.

  • ➖ : No plan to support.

COSE Key Types

Name

Status

Value

Description

OKP

1

Octet Key Pair

EC2

2

Elliptic Curve Keys w/ x- and y-coordinate pair

RSA

3

RSA Key

Symmetric

4

Symmetric Keys

HSS-LMS

5

Public key for HSS/LMS hash-based digital signature

WalnutDSA

6

WalnutDSA public key

COSE Algorithms

Name

Status

Value

Description

RS1

-65535

RSASSA-PKCS1-v1_5 using SHA-1

WalnutDSA

-260

WalnutDSA signature

RS512

-259

RSASSA-PKCS1-v1_5 using SHA-512

RS384

-258

RSASSA-PKCS1-v1_5 using SHA-384

RS256

-257

RSASSA-PKCS1-v1_5 using SHA-256

ES256K

-47

ECDSA using secp256k1 curve and SHA-256

HSS-LMS

-46

HSS/LMS hash-based digital signature

SHAKE256

-45

SHAKE-256 512-bit Hash Value

SHA-512

-44

SHA-2 512-bit Hash

SHA-384

-43

SHA-2 384-bit Hash

RSAES-OAEP w/ SHA-512

-42

RSAES-OAEP w/ SHA-512

RSAES-OAEP w/ SHA-256

-41

RSAES-OAEP w/ SHA-256

RSAES-OAEP
w/ RFC 8017 default
parameters

-40

RSAES-OAEP w/ SHA-1

PS512

-39

RSASSA-PSS w/ SHA-512

PS384

-38

RSASSA-PSS w/ SHA-384

PS256

-37

RSASSA-PSS w/ SHA-256

ES512

-36

ECDSA w/ SHA-512

ES384

-35

ECDSA w/ SHA-384

ECDH-SS + A256KW

-34

ECDH SS w/ Concat KDF and AES Key Wrap w/ 256-bit
key

ECDH-SS + A192KW

-33

ECDH SS w/ Concat KDF and AES Key Wrap w/ 192-bit
key

ECDH-SS + A128KW

-32

ECDH SS w/ Concat KDF and AES Key Wrap w/ 128-bit
key

ECDH-ES + A256KW

-31

ECDH ES w/ Concat KDF and AES Key Wrap w/ 256-bit
key

ECDH-ES + A192KW

-30

ECDH ES w/ Concat KDF and AES Key Wrap w/ 192-bit
key

ECDH-ES + A128KW

-29

ECDH ES w/ Concat KDF and AES Key Wrap w/ 128-bit
key

ECDH-SS + HKDF-512

-28

ECDH SS w/ HKDF - generate key directly

ECDH-SS + HKDF-256

-27

ECDH SS w/ HKDF - generate key directly

ECDH-ES + HKDF-512

-26

ECDH ES w/ HKDF - generate key directly

ECDH-ES + HKDF-256

-25

ECDH ES w/ HKDF - generate key directly

SHAKE128

-18

SHAKE-128 256-bit Hash Value

SHA-512/256

-17

SHA-2 512-bit Hash truncated to 256-bits

SHA-256

-16

SHA-2 256-bit Hash

SHA-256/64

-15

SHA-2 256-bit Hash truncated to 64-bits

SHA-1

-14

SHA-1 Hash

direct+HKDF-AES-256

-13

Shared secret w/ AES-MAC 256-bit key

direct+HKDF-AES-128

-12

Shared secret w/ AES-MAC 128-bit key

direct+HKDF-SHA-512

-11

Shared secret w/ HKDF and SHA-512

direct+HKDF-SHA-256

-10

Shared secret w/ HKDF and SHA-256

EdDSA

-8

EdDSA

ES256

-7

ECDSA w/ SHA-256

direct

-6

Direct use of CEK

A256KW

-5

AES Key Wrap w/ 256-bit key

A192KW

-4

AES Key Wrap w/ 192-bit key

A128KW

-3

AES Key Wrap w/ 128-bit key

A128GCM

1

AES-GCM mode w/ 128-bit key, 128-bit tag

A192GCM

2

AES-GCM mode w/ 192-bit key, 128-bit tag

A256GCM

3

AES-GCM mode w/ 256-bit key, 128-bit tag

HMAC 256/64

4

HMAC w/ SHA-256 truncated to 64 bits

HMAC 256/256
(“HS256” can also
be used.)

5

HMAC w/ SHA-256

HMAC 384/384
(“HS384” can also
be used.)

6

HMAC w/ SHA-384

HMAC 512/512
(“HS512” can also
be used.)

7

HMAC w/ SHA-512

AES-CCM-16-64-128

10

AES-CCM mode 128-bit key, 64-bit tag,
13-byte nonce

AES-CCM-16-64-256

11

AES-CCM mode 256-bit key, 64-bit tag,
13-byte nonce

AES-CCM-64-64-128

12

AES-CCM mode 128-bit key, 64-bit tag,
7-byte nonce

AES-CCM-64-64-256

13

AES-CCM mode 256-bit key, 64-bit tag,
7-byte nonce

AES-MAC 128/64

14

AES-MAC 128-bit key, 64-bit tag

AES-MAC 256/64

15

AES-MAC 256-bit key, 64-bit tag

ChaCha20/Poly1305

24

ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag

AES-MAC 128/128

25

AES-MAC 128-bit key, 128-bit tag

AES-MAC 128/128

26

AES-MAC 256-bit key, 128-bit tag

AES-CCM-16-128-128

30

AES-CCM mode 128-bit key, 128-bit tag,
13-byte nonce

AES-CCM-16-128-256

31

AES-CCM mode 256-bit key, 128-bit tag,
13-byte nonce

AES-CCM-64-128-128

32

AES-CCM mode 128-bit key, 128-bit tag,
7-byte nonce

AES-CCM-64-128-256

33

AES-CCM mode 256-bit key, 128-bit tag,
7-byte nonce

COSE Elliptic Curves

Name

Status

Value

Description

P-256

1

NIST P-256 also known as secp256r1

P-384

2

NIST P-384 also known as secp384r1

P-521

3

NIST P-521 also known as secp521r1

X25519

4

X25519 for use w/ ECDH only

X448

5

X448 for use w/ ECDH only

Ed25519

6

Ed25519 for use w/ EdDSA only

Ed448

7

Ed448 for use w/ EdDSA only

secp256k1

8

SECG secp256k1 curve

Changes

Unreleased

Version 0.8.0

Released 2021-05-30

  • Refine EncryptedCOSEKey interface. #104

  • Merge RecipientsBuilder into Recipients. #103

  • Rename Key to COSEKeyInterface. #102

  • Rename RecipientBuilder to Recipient. #101

  • Make Key private. #100

  • Merge ClaimsBuilder into Claims. #98

  • Rename KeyBuilder to COSEKey. #97

  • Rename COSEKey to Key. #97

  • Add support for external AAD. #94

  • Make unwrap_key return COSEKey. #93

  • Fix default HMAC key size. #91

  • Add support for AES key wrap. #89

  • Add support for direct+HKDF-SHA256 and SHA512. #87

Version 0.7.1

Released 2021-05-11

  • Add alg validation and fix related bug. #77

  • Update protected/unprotected default value from {} to None. #76

Version 0.7.0

Released 2021-05-09

  • Add support for bytes-formatted protected header. #73

  • Derive alg from kty and crv on from_jwk. #73

  • Add alg_auto_inclusion. #73

  • Move nonce generation from CWT to COSE. #73

  • Re-order arguments of COSE API. #73

  • Add support for COSE algorithm names for KeyBuilder.from_jwk. #72

  • Add tests based on COSE WG examples. #72

  • Move parameter auto-gen function from CWT to COSE. #72

  • Refine COSE API to make the type of payload parameter be bytes only. #71

  • Simplify samples on docs. #69

Version 0.6.1

Released 2021-05-08

  • Add test for error handling of encoding/decoding. #67

  • Fix low level error message. #67

  • Add support for multiple aud. #65

  • Relax the condition of the acceptable private claim value. #64

  • Fix doc version. #63

Version 0.6.0

Released 2021-05-04

  • Make decode accept multiple keys. #61

  • Add set_private_claim_names to ClaimsBuilder and CWT. #60

  • Add sample of CWT with user-defined claims to docs. #60

Version 0.5.0

Released 2021-05-04

  • Make ClaimsBuilder return Claims. #56

  • Add support for JWK keyword of alg and key_ops. #55

  • Add from_jwk. #53

  • Add support for PoP key (cnf claim). #50

  • Add to_dict to COSEKey. #50

  • Add crv property to COSEKey. #50

  • Add key property to COSEKey. #50

  • Add support for RSASSA-PSS. #49

  • Add support for RSASSA-PKCS1-v1_5. #48

Version 0.4.0

Released 2021-04-30

  • Add CWT.encode. #46

  • Fix bug on KeyBuilder.from_dict. #45

  • Add support for key_ops. #44

  • Add support for ChaCha20/Poly1305. #43

  • Make nonce optional for CWT.encode_and_encrypt. #42

  • Add support for AES-GCM (A128GCM, A192GCM and A256GCM). #41

  • Make key optional for KeyBuilder.from_symmetric_key. #41

Version 0.3.0

Released 2021-04-29

  • Add docstring to COSE, KeyBuilder and more. #39

  • Add support for COSE_Encrypt structure. #36

  • Add support for COSE_Signature structure. #35

  • Change protected_header type from bytes to dict. #34

  • Add support for COSE_Mac structure. #32

  • Add test for CWT. #29

Version 0.2.3

Released 2021-04-23

  • Add test for cose_key and fix bugs. #21

  • Add support for exp, nbf and iat. #18

Version 0.2.2

Released 2021-04-19

  • Add support for Ed448, ES384 and ES512. #13

  • Add support for EncodeError and DecodeError. #13

  • Add test for supported algorithms. #13

  • Update supported algorithms and claims on docs. #13

Version 0.2.1

Released 2021-04-18

  • Add VerifyError. #11

  • Fix HMAC alg names. #11

  • Make COSEKey public. #11

  • Add tests for HMAC. #11

Version 0.2.0

Released 2021-04-18

  • Add docs for CWT. #9

  • Raname exceptions. #9

Version 0.1.1

Released 2021-04-18

  • Fix description of installation.

Version 0.1.0

Released 2021-04-18

  • First public preview release.