Python CWT - A Python implementation of CWT/COSE

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 JWS/JWE/JWT
be able to use it in ease. Little knowledge of CBOR/COSE/CWT
is required to use it.
You can install Python CWT with pip:
$ pip install cwt
And then, you can use it as follows:
COSE API
from cwt import COSE, COSEKey
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
mac_key,
protected={"alg": "HS256"},
unprotected={"kid": "01"},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, mac_key)
CWT API
import cwt
from cwt import COSEKey, CWTClaims
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
token = encode(
{
CWTClaims.ISS: "coaps://as.example",
CWTClaims.SUB: "dajiaji",
CWTClaims.CTI: b"123",
},
mac_key,
)
decoded = decode(token, mac_key)
Various usage examples are shown in this README.
See Documentation for details of the APIs.
Index
Installation
Install with pip:
pip install cwt
COSE Usage Examples
Followings are typical and basic examples which encode various types of COSE messages and decode them.
See API Reference.
COSE MAC0
MAC with HMAC with SHA256
Create a COSE MAC0 message, verify and decode it as follows:
from cwt import COSE, COSEKey
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
mac_key,
protected={"alg": "HS256"},
unprotected={"kid": "01"},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, mac_key)
Following two samples are other ways of writing the above example.
CBOR object can be used for protected and unprotected header parameters as follows:
from cwt import COSE, COSEHeaders, COSEKey
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
mac_key,
protected={COSEHeaders.ALG: 5},
unprotected={COSEHeaders.KID: b"01"},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, mac_key)
alg_auto_inclusion and kid_auto_inclusion can be used to omit to specify alg and kid header parameters respectively as follows:
from cwt import COSE, COSEKey
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
encoded = sender.encode(
b"Hello world!",
mac_key,
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, mac_key)
Countersign (MAC0)
python-cwt supports RFC9338: COSE Countersignatures.
The notary below adds a countersignature to a MACed COSE message.
The recipinet has to call counterverify to verify the countersignature explicitly.
from cwt import COSE, COSEKey, COSEMessage
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", mac_key)
notary = Signer.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
},
)
countersigned = COSEMessage.loads(encoded).countersign(notary).dumps()
pub_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(countersigned, mac_key)
try:
sig = COSEMessage.loads(countersigned).counterverify(pub_key)
except Exception as err:
pytest.fail(f"failed to verify: {err}")
countersignature = COSEMessage.from_cose_signature(sig)
assert countersignature.protected[1] == -8
assert countersignature.unprotected[4] == b"01"
COSE MAC
Direct Key Distribution for MAC
The direct key distribution shares a MAC key between the sender and the recipient that is used directly.
The follwing example shows the simplest way to make a COSE MAC message, verify and decode it with the direct
key distribution method.
from cwt import COSE, COSEKey, Recipient
mac_key = COSEKey.generate_symmetric_key(alg="HS512", kid="01")
r = Recipient.new(unprotected={"alg": "direct", "kid": mac_key.kid})
sender = COSE.new()
encoded = sender.encode(
b"Hello world!", mac_key, protected={"alg": "HS512"}, recipients=[r]
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, mac_key)
Direct Key with KDF for MAC
from secrets import token_bytes
from cwt import COSE, COSEKey, Recipient
shared_material = token_bytes(32)
shared_key = COSEKey.from_symmetric_key(shared_material, kid="01")
r = Recipient.new(
unprotected={
"alg": "direct+HKDF-SHA-256",
"salt": "aabbccddeeffgghh",
},
context={"alg": "HS256"},
)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(
b"Hello world!",
shared_key,
recipients=[r],
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(
encoded, shared_key, context={"alg": "HS256"}
)
AES Key Wrap for MAC
The AES key wrap algorithm can be used to wrap a MAC key as follows:
from cwt import COSE, COSEKey, Recipient
enc_key = COSEKey.from_jwk(
{
"kty": "oct",
"kid": "01",
"alg": "A128KW",
"k": "hJtXIZ2uSN5kbQfbtTNWbg",
}
)
mac_key = COSEKey.generate_symmetric_key(alg="HS512")
r = Recipient.new(unprotected={"alg": "A128KW"}, sender_key=enc_key)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", mac_key, recipients=[r])
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, enc_key)
Direct Key Agreement for MAC
The direct key agreement methods can be used to create a shared secret. A KDF (Key Distribution Function) is then
applied to the shared secret to derive a key to be used to protect the data.
The follwing example shows a simple way to make a COSE Encrypt message, verify and decode it with the direct key
agreement methods.
from cwt import COSE, COSEKey, Recipient
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
}
)
r = Recipient.new(
unprotected={"alg": "ECDH-ES+HKDF-256"},
recipient_key=pub_key,
context={"alg": "HS256"},
)
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
protected={"alg": "HS256"},
recipients=[r],
)
priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"alg": "ECDH-ES+HKDF-256",
"kid": "01",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
}
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, priv_key, context={"alg": "HS256"})
Key Agreement with Key Wrap for MAC
from cwt import COSE, COSEKey, Recipient
mac_key = COSEKey.generate_symmetric_key(alg="HS256")
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"alg": "ECDH-ES+A128KW",
"kid": "01",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
}
)
r = Recipient.new(
unprotected={"alg": "ECDH-ES+A128KW"},
recipient_key=pub_key,
context={"alg": "HS256"},
)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(
b"Hello world!",
mac_key,
recipients=[r],
)
recipient = COSE.new()
priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"alg": "ECDH-ES+A128KW",
"kid": "01",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
}
)
assert b"Hello world!" == recipient.decode(encoded, priv_key, context={"alg": "HS256"})
Countersign (MAC)
python-cwt supports RFC9338: COSE Countersignatures.
The notary below adds a countersignature to a MACed COSE message.
The recipinet has to call counterverify to verify the countersignature explicitly.
from cwt import COSE, COSEKey, COSEMessage, Recipient
mac_key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
r = Recipient.new(unprotected={"alg": "direct", "kid": mac_key.kid})
sender = COSE.new()
encoded = sender.encode(
b"Hello world!", mac_key, protected={"alg": "HS256"}, recipients=[r]
)
notary = Signer.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
},
)
countersigned = COSEMessage.loads(encoded).countersign(notary).dumps()
pub_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(countersigned, mac_key)
try:
sig = COSEMessage.loads(countersigned).counterverify(pub_key)
except Exception as err:
pytest.fail(f"failed to verify: {err}")
countersignature = COSEMessage.from_cose_signature(sig)
assert countersignature.protected[1] == -8
assert countersignature.unprotected[4] == b"01"
COSE-HPKE (MAC)
Experimental Implementation. DO NOT USE for production.
Create a COSE-HPKE MAC message, verify and decode it as follows:
from cwt import COSE, COSEAlgs, COSEHeaders, COSEKey, Recipient
mac_key = COSEKey.generate_symmetric_key(alg="HS256")
rpk = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
r = Recipient.new(
protected={
COSEHeaders.ALG: COSEAlgs.HPKE_BASE_P256_SHA256_AES128GCM,
},
unprotected={
COSEHeaders.KID: b"01",
},
recipient_key=rpk,
)
sender = COSE.new()
encoded = sender.encode(
b"This is the content.",
mac_key,
protected={COSEHeaders.ALG: COSEAlgs.HS256},
recipients=[r],
)
rsk = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
}
)
recipient = COSE.new()
assert b"This is the content." == recipient.decode(encoded, rsk)
COSE Encrypt0
Encryption with ChaCha20/Poly1305
Create a COSE Encrypt0 message and decrypt it as follows:
from cwt import COSE, COSEKey
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
nonce = enc_key.generate_nonce()
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", enc_key, unprotected={COSEHeaders.IV: nonce})
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, enc_key)
The following sample is another way of writing the above:
from cwt import COSE, COSEKey
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
nonce = enc_key.generate_nonce()
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
enc_key,
protected={"alg": "ChaCha20/Poly1305"},
unprotected={"kid": "01", "iv": nonce},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, enc_key)
Countersign (Encrypt0)
python-cwt supports RFC9338: COSE Countersignatures.
The notary below adds a countersignature to an encrypted COSE message.
The recipinet has to call counterverify to verify the countersignature explicitly.
from cwt import COSE, COSEKey, COSEMessage
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
nonce = enc_key.generate_nonce()
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", enc_key, unprotected={COSEHeaders.IV: nonce})
notary = Signer.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
},
)
countersigned = COSEMessage.loads(encoded).countersign(notary).dumps()
pub_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(countersigned, enc_key)
try:
sig = COSEMessage.loads(countersigned).counterverify(pub_key)
except Exception as err:
pytest.fail(f"failed to verify: {err}")
countersignature = COSEMessage.from_cose_signature(sig)
assert countersignature.protected[1] == -8
assert countersignature.unprotected[4] == b"01"
COSE-HPKE (Encrypt0)
Experimental Implementation. DO NOT USE for production.
Create a COSE-HPKE Encrypt0 message and decrypt it as follows:
from cwt import COSE, COSEAlgs, COSEHeaders, COSEKey
rpk = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
sender = COSE.new()
encoded = sender.encode(
b"This is the content.",
rpk,
protected={
COSEHeaders.ALG: COSEAlgs.HPKE_BASE_P256_SHA256_AES128GCM,
},
unprotected={
COSEHeaders.KID: b"01",
},
)
rsk = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
}
)
recipient = COSE.new()
assert b"This is the content." == recipient.decode(encoded, rsk)
COSE Encrypt
Direct Key Distribution for encryption
The direct key distribution shares a MAC key between the sender and the recipient that is used directly.
The follwing example shows the simplest way to make a COSE MAC message, verify and decode it with the direct
key distribution method.
from cwt import COSE, COSEHeaders, COSEKey, Recipient
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
nonce = enc_key.generate_nonce()
r = Recipient.new(unprotected={"alg": "direct"})
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
enc_key,
protected={"alg": "ChaCha20/Poly1305"},
unprotected={"kid": enc_key.kid, "iv": nonce},
recipients=[r],
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, enc_key)
Direct Key with KDF for encryption
from cwt import COSE, COSEKey, Recipient
shared_material = token_bytes(32)
shared_key = COSEKey.from_symmetric_key(shared_material, kid="01")
r = Recipient.new(
unprotected={
"alg": "direct+HKDF-SHA-256",
"salt": "aabbccddeeffgghh",
},
context={"alg": "A256GCM"},
)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(
b"Hello world!",
shared_key,
recipients=[r],
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(
encoded, shared_key, context={"alg": "A256GCM"}
)
AES Key Wrap for encryption
The AES key wrap algorithm can be used to wrap a MAC key as follows:
from cwt import COSE, COSEKey, Recipient
wrapping_key = COSEKey.from_jwk(
{
"kty": "oct",
"alg": "A128KW",
"kid": "01",
"k": "hJtXIZ2uSN5kbQfbtTNWbg",
}
)
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305")
r = Recipient.new(
unprotected={"alg": "A128KW"},
sender_key=wrapping_key,
)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", key=enc_key, recipients=[r])
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, wrapping_key)
Direct Key Agreement for encryption
The direct key agreement methods can be used to create a shared secret. A KDF (Key Distribution Function) is then
applied to the shared secret to derive a key to be used to protect the data.
The follwing example shows a simple way to make a COSE Encrypt message, verify and decode it with the direct key
agreement methods.
from cwt import COSE, COSEKey, Recipient
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
}
)
r = Recipient.new(
unprotected={"alg": "ECDH-ES+HKDF-256"},
recipient_key=pub_key,
context={"alg": "A128GCM"},
)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(
b"Hello world!",
recipients=[r],
)
recipient = COSE.new()
priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"alg": "ECDH-ES+HKDF-256",
"kid": "01",
"crv": "P-256",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
}
)
assert b"Hello world!" == recipient.decode(
encoded, priv_key, context={"alg": "A128GCM"}
)
Key Agreement with Key Wrap for encryption
from cwt import COSE, COSEKey, Recipient
enc_key = COSEKey.generate_symmetric_key(alg="A128GCM")
nonce = enc_key.generate_nonce()
r_pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"crv": "P-256",
"kid": "meriadoc.brandybuck@buckland.example",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
}
)
s_priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"crv": "P-256",
"alg": "ECDH-SS+A128KW",
"x": "7cvYCcdU22WCwW1tZXR8iuzJLWGcd46xfxO1XJs-SPU",
"y": "DzhJXgz9RI6TseNmwEfLoNVns8UmvONsPzQDop2dKoo",
"d": "Uqr4fay_qYQykwcNCB2efj_NFaQRRQ-6fHZm763jt5w",
}
)
r = Recipient.new(
unprotected={"alg": "ECDH-SS+A128KW"},
sender_key=s_priv_key,
recipient_key=r_pub_key,
context={"alg": "A128GCM"},
)
sender = COSE.new(alg_auto_inclusion=True)
encoded = sender.encode(
b"Hello world!",
key=enc_key,
unprotected={COSEHeaders.IV: nonce},
recipients=[r],
)
recipient = COSE.new()
r_priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"crv": "P-256",
"alg": "ECDH-SS+A128KW",
"kid": "meriadoc.brandybuck@buckland.example",
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
}
)
assert b"Hello world!" == recipient.decode(
encoded, r_priv_key, context={"alg": "A128GCM"}
)
Countersign (Encrypt)
python-cwt supports RFC9338: COSE Countersignatures.
The notary below adds a countersignature to an encrypted COSE message.
The recipinet has to call counterverify to verify the countersignature explicitly.
from cwt import COSE, COSEKey, COSEMessage, Recipient
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
nonce = enc_key.generate_nonce()
r = Recipient.new(unprotected={"alg": "direct"})
sender = COSE.new()
encoded = sender.encode(
b"Hello world!",
enc_key,
protected={"alg": "ChaCha20/Poly1305"},
unprotected={"kid": enc_key.kid, "iv": nonce},
recipients=[r],
)
notary = Signer.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
},
)
countersigned = COSEMessage.loads(encoded).countersign(notary).dumps()
pub_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(countersigned, enc_key)
try:
sig = COSEMessage.loads(countersigned).counterverify(pub_key)
except Exception as err:
pytest.fail(f"failed to verify: {err}")
countersignature = COSEMessage.from_cose_signature(sig)
assert countersignature.protected[1] == -8
assert countersignature.unprotected[4] == b"01"
COSE-HPKE (Encrypt)
Experimental Implementation. DO NOT USE for production.
Create a COSE-HPKE Encrypt message and decrypt it as follows:
from cwt import COSE, COSEAlgs, COSEHeaders, COSEKey, Recipient
enc_key = COSEKey.generate_symmetric_key(alg="A128GCM")
rpk = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
r = Recipient.new(
protected={
COSEHeaders.ALG: COSEAlgs.HPKE_BASE_P256_SHA256_AES128GCM,
},
unprotected={
COSEHeaders.KID: b"01",
},
recipient_key=rpk,
)
sender = COSE.new()
encoded = sender.encode(
b"This is the content.",
enc_key,
protected={
COSEHeaders.ALG: 1,
},
recipients=[r],
)
rsk = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
}
)
recipient = COSE.new()
assert b"This is the content." == recipient.decode(encoded, rsk)
COSE Signature1
Sign1 with EC P-256
Create a COSE Signature1 message, verify and decode it as follows:
from cwt import COSE, COSEKey, Signer
priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
}
)
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", priv_key)
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, pub_key)
Countersign (Sign1)
python-cwt supports RFC9338: COSE Countersignatures.
The notary below adds a countersignature to a signed COSE message.
The recipinet has to call counterverify to verify the countersignature explicitly.
from cwt import COSE, COSEKey, COSEMessage
priv_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
}
)
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
encoded = sender.encode(b"Hello world!", priv_key)
notary = Signer.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
},
)
countersigned = COSEMessage.loads(encoded).countersign(notary).dumps()
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
notary_pub_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(countersigned, pub_key)
try:
sig = COSEMessage.loads(countersigned).counterverify(notary_pub_key)
except Exception as err:
pytest.fail(f"failed to verify: {err}")
countersignature = COSEMessage.from_cose_signature(sig)
assert countersignature.protected[1] == -8
assert countersignature.unprotected[4] == b"01"
COSE Signature
Sign with EC P-256
Create a COSE Signature message, verify and decode it as follows:
from cwt import COSE, COSEKey, Signer
signer = Signer.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
},
)
sender = COSE.new()
encoded = sender.encode(b"Hello world!", signers=[signer])
recipient = COSE.new()
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
assert b"Hello world!" == recipient.decode(encoded, pub_key)
Countersign (Sign)
python-cwt supports RFC9338: COSE Countersignatures.
The notary below adds a countersignature to a signed COSE message.
The recipinet has to call counterverify to verify the countersignature explicitly.
from cwt import COSE, COSEKey, COSEMessage, Signer
signer = Signer.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM",
},
)
sender = COSE.new()
encoded = sender.encode(b"Hello world!", signers=[signer])
notary = Signer.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
},
)
countersigned = COSEMessage.loads(encoded).countersign(notary).dumps()
pub_key = COSEKey.from_jwk(
{
"kty": "EC",
"kid": "01",
"crv": "P-256",
"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
}
)
notary_pub_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
},
)
recipient = COSE.new()
assert b"Hello world!" == recipient.decode(encoded, pub_key)
try:
sig = COSEMessage.loads(countersigned).counterverify(notary_pub_key)
except Exception as err:
pytest.fail(f"failed to verify: {err}")
countersignature = COSEMessage.from_cose_signature(sig)
assert countersignature.protected[1] == -8
assert countersignature.unprotected[4] == b"01"
CWT Usage Examples
Followings are typical and basic examples which encode various types of CWTs, verify and decode them.
CWT API in the examples are built
on top of COSE API.
See API Reference.
MACed CWT
Create a MACed CWT with HS256, verify and decode it as follows:
import cwt
from cwt import Claims, COSEKey
try:
key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
token = cwt.encode(
{"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, key
)
decoded = cwt.decode(token, key)
readable = Claims.new(decoded)
assert readable.iss == "coaps://as.example"
assert readable.sub == "dajiaji"
assert readable.cti == "123"
except Exception as err:
print(err)
A raw CWT structure (Dict[int, Any]) can also be used as follows:
import cwt
from cwt import COSEKey, CWTClaims
key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
token = cwt.encode(
{
CWTClaims.ISS: "coaps://as.example",
CWTClaims.SUB: "dajiaji",
CWTClaims.CTI: b"123",
},
key,
)
decoded = cwt.decode(token, key)
MAC algorithms other than HS256 are listed in
Supported COSE Algorithms.
Signed CWT
Create an Ed25519 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 with Ed25519, 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")
token = cwt.encode(
{"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)
with open("./public_key.pem") as key_file:
public_key = COSEKey.from_pem(key_file.read(), kid="01")
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(
{
"kid": "01",
"kty": "OKP",
"key_ops": ["sign"],
"alg": "EdDSA",
"crv": "Ed25519",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"d": "L8JS08VsFZoZxGa9JvzYmCWOwg7zaKcei3KZmYsj7dc",
}
)
token = cwt.encode(
{"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)
public_key = COSEKey.from_jwk(
{
"kid": "01",
"kty": "OKP",
"key_ops": ["verify"],
"crv": "Ed25519",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
}
)
decoded = cwt.decode(token, public_key)
Signing algorithms other than Ed25519 are listed in
Supported COSE Algorithms.
Encrypted CWT
Create an encrypted CWT with ChaCha20/Poly1305 and decrypt it as follows:
import cwt
from cwt import COSEKey
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
token = cwt.encode(
{"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, enc_key
)
decoded = cwt.decode(token, enc_key)
Encryption algorithms other than ChaCha20/Poly1305 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
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="enc-01")
with open("./private_key.pem") as key_file:
private_key = COSEKey.from_pem(key_file.read(), kid="sig-01")
token = cwt.encode(
{"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)
nested = cwt.encode(token, enc_key)
with open("./public_key.pem") as key_file:
public_key = COSEKey.from_pem(key_file.read(), kid="sig-01")
decoded = cwt.decode(nested, [enc_key, public_key])
CWT with User Settings
The cwt in cwt.encode() and cwt.decode() above is a global CWT class instance created
with default settings in advance. The default settings are as follows:
expires_in: 3600 seconds. This is the default lifetime in seconds of CWTs.
leeway: 60 seconds. This is the default leeway in seconds for validating exp and nbf.
If you want to change the settings, you can create your own CWT class instance as follows:
from cwt import COSEKey, CWT
key = COSEKey.generate_symmetric_key(alg="HS256", kid="01")
mycwt = CWT.new(expires_in=3600 * 24, leeway=10)
token = mycwt.encode({"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, key)
decoded = mycwt.decode(token, 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, CWTClaims
with open("./private_key.pem") as key_file:
private_key = COSEKey.from_pem(key_file.read(), kid="01")
token = cwt.encode(
{
CWTClaims.ISS: "coaps://as.example",
CWTClaims.SUB: "dajiaji",
CWTClaims.CTI: b"123",
-70001: "foo",
-70002: ["bar"],
-70003: {"baz": "qux"},
-70004: 123,
},
private_key,
)
with open("./public_key.pem") as key_file:
public_key = COSEKey.from_pem(key_file.read(), kid="01")
raw = cwt.decode(token, public_key)
assert raw[-70001] == "foo"
assert raw[-70002][0] == "bar"
assert raw[-70003]["baz"] == "qux"
assert raw[-70004] == 123
readable = Claims.new(raw)
assert readable.get(-70001) == "foo"
assert readable.get(-70002)[0] == "bar"
assert readable.get(-70003)["baz"] == "qux"
assert 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")
my_claim_names = {
"ext_1": -70001,
"ext_2": -70002,
"ext_3": -70003,
"ext_4": -70004,
}
set_private_claim_names(my_claim_names)
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,
)
with open("./public_key.pem") as key_file:
public_key = COSEKey.from_pem(key_file.read(), kid="01")
raw = cwt.decode(token, public_key)
readable = Claims.new(
raw,
private_claims_names=my_claim_names,
)
assert readable.get("ext_1") == "foo"
assert readable.get("ext_2")[0] == "bar"
assert readable.get("ext_3")["baz"] == "qux"
assert readable.get("ext_4") == 123
Python CWT supports CWT Claims in COSE Headers experimentally.
If a CWT message has a CWT Claims header parameter in its protected header, cwt.decode() checks whether the values of the claims included in that parameter match the values of the corresponding claims in the payload. If they do not match, VerifyError is raised.
from cwt import COSE, COSEHeaders, COSEKey, CWT, CWTClaims
enc_key = COSEKey.from_symmetric_key(alg="A128GCM", kid="01")
sender = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True)
payload = cbor2.dumps(
{
CWTClaims.ISS: "coap://as.example.com",
CWTClaims.SUB: "erikw",
CWTClaims.AUD: "coap://light.example.com",
CWTClaims.EXP: now() + 3600,
CWTClaims.NBF: now(),
CWTClaims.IAT: now(),
CWTClaims.CTI: bytes.fromhex("0b71"),
},
)
protected = {
COSEHeaders.CWT_CLAIMS: {
CWTClaims.ISS: "coap://as.example.com",
}
}
token = sender.encode(payload, enc_key, protected)
recipient = CWT.new()
decoded = recipient.decode(token, enc_key)
assert decoded[CWTClaims.ISS] == "coap://as.example.com"
assert decoded[CWTClaims.SUB] == "erikw"
CWT with PoP Key
Python CWT supports Proof-of-Possession Key Semantics for CBOR Web Tokens (CWTs).
A CWT can include a PoP key as follows:
On the issuer side:
import cwt
from cwt import COSEKey
with open("./private_key_of_issuer.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": {
"jwk": {
"kty": "OKP",
"use": "sig",
"crv": "Ed25519",
"kid": "presenter-01",
"x": "2E6dX83gqD_D0eAmqnaHe1TC1xuld6iAKXfw2OVATr0",
"alg": "EdDSA",
},
},
},
private_key,
)
On the CWT presenter side:
import cwt
from cwt import COSEKey
with open("./private_pop_key.pem") as key_file:
pop_key_private = COSEKey.from_pem(key_file.read(), kid="presenter-01")
msg = b"could-you-sign-this-message?"
sig = pop_key_private.sign(msg)
On the CWT recipient side:
import cwt
from cwt import Claims, COSEKey
with open("./public_key_of_issuer.pem") as key_file:
public_key = COSEKey.from_pem(key_file.read(), kid="issuer-01")
raw = cwt.decode(token, public_key)
decoded = Claims.new(raw)
extracted_pop_key = COSEKey.new(decoded.cnf)
extracted_pop_key.verify(msg, sig)
Usage Examples
shows other examples which use other confirmation methods for PoP keys.
CWT with Private CA
Python CWT supports the case of using an arbitrary private CA as a root of trust.
In this case, a COSE message sender needs to specify the trust relationship chaining up to the root CA by using x5chain header parameter.
On the other hand, a COSE message receiver needs to specify trusted root CAs by using ca_certs parameter of CWT/COSE constructor (CWT.new() or COSE.new()).
import cwt
from cwt import Claims, COSEKey
with open("./private_key_of_cert.pem") as f:
private_key = COSEKey.from_pem(f.read(), kid="01")
token = cwt.encode(
{"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key
)
public_key = COSEKey.from_jwk(
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "P-256-01",
"x": "oONCv1QoiajIbcW21Dqy6EnGvBTuF26GU7dy6JzOfXk",
"y": "sl6k77K0TS36FW-TyEGLHY14ovZfdZ9DZWsbA8BTHGc",
"x5c": [
"MIIClDCCAXygAwIBAgIBBDANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJKUDEOMAwGA1UECAwFVG9reW8xEDAOBgNVBAoMB2RhamlhamkxEzARBgNVBAMMCnB5dGhvbi1jd3QxIDAeBgkqhkiG9w0BCQEWEWRhamlhamlAZ21haWwuY29tMB4XDTIxMTAwMzEzMDE1MFoXDTMxMTAwMTEzMDE1MFowZDELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMQ0wCwYDVQQKDAR0ZXN0MRUwEwYDVQQDDAx0ZXN0LmV4YW1wbGUxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASg40K_VCiJqMhtxbbUOrLoSca8FO4XboZTt3LonM59ebJepO-ytE0t-hVvk8hBix2NeKL2X3WfQ2VrGwPAUxxnoxowGDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAZFfvFbaDk_DmG2cPGTwqwnFok1QnH2Tzkjk7p4vs1ycWzEDltkhyzcJxTSHoQGdykf7fG8NCrEqfi1G3hOyAtGxVIVcqsI-KIJCESp43zrNz5HsbwEY8l5rvcwohKGlE_idIFt5IuDTv7vsg_FaCIDeruw0NrXAACnLTwksawsxaCvtY12U0wsI2aC2Sb6V3HL-OLgcN6ZWzZ054L88JllckYnqJB8wCVBzzX2K2sZH3yeS39oRWZOVG6fwXsX4k0fHFx-Fn6KlrBU15pbjMLMn0ow0X3Y8e7FOgfkkph-N7e2SxceXNjrLiumOdclPm9yGSWoGsOJdId53dPvqAsQ",
"MIIDrzCCApegAwIBAgIUIK_CYzdq4BLLVXqSclNBgXy6mgswDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMRAwDgYDVQQKDAdkYWppYWppMRMwEQYDVQQDDApweXRob24tY3d0MSAwHgYJKoZIhvcNAQkBFhFkYWppYWppQGdtYWlsLmNvbTAgFw0yMTEwMDIyMzU0NTZaGA8yMDcxMDkyMDIzNTQ1NlowZjELMAkGA1UEBhMCSlAxDjAMBgNVBAgMBVRva3lvMRAwDgYDVQQKDAdkYWppYWppMRMwEQYDVQQDDApweXRob24tY3d0MSAwHgYJKoZIhvcNAQkBFhFkYWppYWppQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFg4sw-uPWbPBbkJuohXc89O0gaqG1H2i1wzxxka32XNKIdwrxOJvsB2eALo3q7dTqLKCgzrjdd5N07gi0KzqjoIXIXqKpV5tm0fP5gCzEOWgxySCfBJOJyyvO6WvYXdvukEBnL-48D8RSjQH9fQEju5RG0taFZE-0nQ7n3P0J-Q-OfBUEoRiHvCd8oUx0s-fBpKdfhMAbD1sGAQ9CokUFeWc49em8inNqia5xljBtSYo6_2Zx9eb7B53wvBC0EmtS4SRyksR2emlr6GxMj_EZW7hcTfZCM4V2JYXliuAEdxA0sB7q-WqLg4OvltBQxCBgTTEXRCzxj3XXZy7QyUacCAwEAAaNTMFEwHQYDVR0OBBYEFA9id2cL_Chjv6liRN3HD849TARsMB8GA1UdIwQYMBaAFA9id2cL_Chjv6liRN3HD849TARsMA8GA1UdEwEB_wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAArIej5eJN1OmD3l3ef9QzosCxKThNwqNY55CoSSC3IRl-IAXy9Lvx7cgiliwBgCv99RbXZ1ZnptTHC_1kzMzPhPg9pGKDowFP-rywaB9-NTuHTWQ4hkKDsru5dpf75ILNI5PTUi1iiBM7TdgSerpEVroUWZiOpGAdlKkmE1h4gkR6eQY9Q0IvVXwagy_PPoQ1XO1i5Hyg3aXeDZBgkE7AuW9uxtYQHzg8JG2TNko_yp497yf_Ew4t6KzGDhSa8L1euMPtclALDWFhgl6WmYsHOqAOsyZOLwpsifWa533wI9mtTvLEg8TFKMOdU0sbAoQSbrrI9m4QS7mzDLchngj3E",
],
"alg": "ES256",
}
)
decoder = CWT.new(ca_certs="/path/to/cacerts.pem")
decoded = decoder.decode(token, public_key)
assert 1 in decoded and decoded[1] == "coaps://as.example"
CWT for EUDCC (EU Digital COVID Certificate)
Python CWT supports Electronic Health Certificate Specification
and EUDCC (EU Digital COVID Certificate) compliant with Technical Specifications for Digital Green Certificates Volume 1
A following example shows how to verify an EUDCC:
import cwt
from cwt import load_pem_hcert_dsc
dsc = "-----BEGIN CERTIFICATE-----\nMIIBvTCCAWOgAwIBAgIKAXk8i88OleLsuTAKBggqhkjOPQQDAjA2MRYwFAYDVQQDDA1BVCBER0MgQ1NDQSAxMQswCQYDVQQGEwJBVDEPMA0GA1UECgwGQk1TR1BLMB4XDTIxMDUwNTEyNDEwNloXDTIzMDUwNTEyNDEwNlowPTERMA8GA1UEAwwIQVQgRFNDIDExCzAJBgNVBAYTAkFUMQ8wDQYDVQQKDAZCTVNHUEsxCjAIBgNVBAUTATEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASt1Vz1rRuW1HqObUE9MDe7RzIk1gq4XW5GTyHuHTj5cFEn2Rge37+hINfCZZcozpwQKdyaporPUP1TE7UWl0F3o1IwUDAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFO49y1ISb6cvXshLcp8UUp9VoGLQMB8GA1UdIwQYMBaAFP7JKEOflGEvef2iMdtopsetwGGeMAoGCCqGSM49BAMCA0gAMEUCIQDG2opotWG8tJXN84ZZqT6wUBz9KF8D+z9NukYvnUEQ3QIgdBLFSTSiDt0UJaDF6St2bkUQuVHW6fQbONd731/M4nc=\n-----END CERTIFICATE-----"
eudcc = bytes.fromhex(
"d2844da20448d919375fc1e7b6b20126a0590133a4041a61817ca0061a60942ea001624154390103a101a4617681aa62646e01626d616d4f52472d3130303033303231356276706a313131393334393030376264746a323032312d30322d313862636f624154626369783155524e3a555643493a30313a41543a31303830373834334639344145453045453530393346424332353442443831332342626d706c45552f312f32302f31353238626973781b4d696e6973747279206f66204865616c74682c20417573747269616273640262746769383430353339303036636e616da463666e74754d5553544552465241553c474f455353494e47455262666e754d7573746572667261752d47c3b6c39f696e67657263676e74684741425249454c4562676e684761627269656c656376657265312e302e3063646f626a313939382d30322d323658405812fce67cb84c3911d78e3f61f890d0c80eb9675806aebed66aa2d0d0c91d1fc98d7bcb80bf00e181806a9502e11b071325901bd0d2c1b6438747b8cc50f521"
)
public_key = load_pem_hcert_dsc(dsc)
decoded = cwt.decode(eudcc, keys=[public_key])
claims = Claims.new(decoded)
API Reference
See Documentation.
Supported CWT Claims
See Documentation.
Supported COSE Algorithms
See Documentation.
Referenced Specifications
Python CWT is (partially) compliant with following specifications:
Tests
You can run tests from the project root after cloning with:
$ tox
Contributing
We welcome all kind of contributions, filing issues, suggesting new features or sending PRs.