diff --git a/CHANGES.rst b/CHANGES.rst index 29ba41c..0c22d20 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,10 @@ Changes Unreleased ---------- +- Introduce new() into CWT/COSE. `#115 `__ +- Rename Claims.from_dict to Claims.new. `#115 `__ +- Rename COSEKey.from_dict to COSEKey.new. `#115 `__ +- Rename Recipient.from_dict to Recipient.new. `#115 `__ - Add Signer for encode_and_sign function. `#114 `__ - Divide CWT options into independent parameters. `#113 `__ diff --git a/README.md b/README.md index d911cc7..32d91f4 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ decoded = cwt.decode(token, key) # } # If you want to treat the result like a JWT; -readable = Claims.from_dict(decoded) +readable = Claims.new(decoded) # readable.iss == 'coaps://as.example' # readable.sub == 'dajiaji' @@ -228,7 +228,7 @@ raw = cwt.decode(token, public_key) # raw[-70002][0] == "bar" # raw[-70003]["baz"] == "qux" # raw[-70004] == 123 -readable = Claims.from_dict(raw) +readable = Claims.new(raw) # readable.get(-70001) == "foo" # readable.get(-70002)[0] == "bar" # readable.get(-70003)["baz"] == "qux" @@ -267,7 +267,7 @@ token = cwt.encode( private_key, ) raw = cwt.decode(token, public_key) -readable = Claims.from_dict( +readable = Claims.new( raw, private_claims_names={ "ext_1": -70001, @@ -352,10 +352,10 @@ with open("./public_key_of_issuer.pem") as key_file: # Verifies and decodes the CWT received from the presenter. raw = cwt.decode(token, public_key) -decoded = Claims.from_dict(raw) +decoded = Claims.new(raw) # Extracts the PoP key from the CWT. -extracted_pop_key = COSEKey.from_dict(decoded.cnf) # = raw[8][1] +extracted_pop_key = COSEKey.new(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: diff --git a/cwt/claims.py b/cwt/claims.py index 0f95c3c..6fcb93a 100644 --- a/cwt/claims.py +++ b/cwt/claims.py @@ -1,11 +1,9 @@ import json -from typing import Any, Dict, List, TypeVar, Union +from typing import Any, Dict, List, Union from .const import CWT_CLAIM_NAMES from .cose_key import COSEKey -T = TypeVar("T", bound="Claims") - class Claims: """ @@ -71,9 +69,7 @@ def __init__( return @classmethod - def from_dict( - cls, claims: Dict[int, Any], private_claim_names: Dict[str, int] = {} - ) -> Any: + def new(cls, claims: Dict[int, Any], private_claim_names: Dict[str, int] = {}): """ Create a Claims object from a CBOR-like(Dict[int, Any]) claim object. @@ -105,7 +101,7 @@ def from_json( cls, claims: Union[str, bytes, Dict[str, Any]], private_claim_names: Dict[str, int] = {}, - ) -> T: + ): """ 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, @@ -166,7 +162,7 @@ def from_json( for i in [-259, -258, 7]: if i in cbor_claims and isinstance(cbor_claims[i], str): cbor_claims[i] = cbor_claims[i].encode("utf-8") - return cls.from_dict(cbor_claims, private_claim_names) + return cls.new(cbor_claims, private_claim_names) @classmethod def validate(cls, claims: Dict[int, Any]): diff --git a/cwt/cose.py b/cwt/cose.py index 70c716a..5b22d7b 100644 --- a/cwt/cose.py +++ b/cwt/cose.py @@ -20,6 +20,16 @@ class COSE(CBORProcessor): def __init__( self, alg_auto_inclusion: int = False, kid_auto_inclusion: int = False ): + if not isinstance(alg_auto_inclusion, bool): + raise ValueError("alg_auto_inclusion should be bool.") + self._alg_auto_inclusion = alg_auto_inclusion + + if not isinstance(kid_auto_inclusion, bool): + raise ValueError("kid_auto_inclusion should be bool.") + self._kid_auto_inclusion = kid_auto_inclusion + + @classmethod + def new(cls, alg_auto_inclusion: int = False, kid_auto_inclusion: int = False): """ Constructor. @@ -29,13 +39,7 @@ def __init__( kid_auto_inclusion(bool): The indicator whether ``kid`` parameter is included in a proper header bucket automatically or not. """ - if not isinstance(alg_auto_inclusion, bool): - raise ValueError("alg_auto_inclusion should be bool.") - self._alg_auto_inclusion = alg_auto_inclusion - - if not isinstance(kid_auto_inclusion, bool): - raise ValueError("kid_auto_inclusion should be bool.") - self._kid_auto_inclusion = kid_auto_inclusion + return cls(alg_auto_inclusion, kid_auto_inclusion) def encode_and_mac( self, diff --git a/cwt/cose_key.py b/cwt/cose_key.py index 97c5017..0ce4e22 100644 --- a/cwt/cose_key.py +++ b/cwt/cose_key.py @@ -56,7 +56,7 @@ class COSEKey: """ @staticmethod - def from_dict(cose_key: Dict[int, Any]) -> COSEKeyInterface: + def new(cose_key: Dict[int, Any]) -> COSEKeyInterface: """ Create a COSE key from a CBOR-like dictionary with numeric keys. @@ -148,7 +148,7 @@ def from_symmetric_key( except Exception: raise ValueError("Unsupported or unknown key_ops.") cose_key[4] = key_ops_labels - return cls.from_dict(cose_key) + return cls.new(cose_key) @classmethod def from_bytes(cls, key_data: bytes) -> COSEKeyInterface: @@ -164,7 +164,7 @@ def from_bytes(cls, key_data: bytes) -> COSEKeyInterface: DecodeError: Failed to decode the key data. """ cose_key = cbor2.loads(key_data) - return cls.from_dict(cose_key) + return cls.new(cose_key) @classmethod def from_jwk(cls, data: Union[str, bytes, Dict[str, Any]]) -> COSEKeyInterface: @@ -270,7 +270,7 @@ def from_jwk(cls, data: Union[str, bytes, Dict[str, Any]]) -> COSEKeyInterface: if use != 0: cose_key[4] = [] cose_key[4].append(use) - return cls.from_dict(cose_key) + return cls.new(cose_key) @classmethod def from_pem( @@ -438,4 +438,4 @@ def from_pem( ) else: raise ValueError(f"Unsupported or unknown key: {type(k)}.") - return cls.from_dict(cose_key) + return cls.new(cose_key) diff --git a/cwt/cwt.py b/cwt/cwt.py index d939f16..beb14cc 100644 --- a/cwt/cwt.py +++ b/cwt/cwt.py @@ -29,6 +29,25 @@ class CWT(CBORProcessor): def __init__( self, expires_in: int = CWT_DEFAULT_EXPIRES_IN, leeway: int = CWT_DEFAULT_LEEWAY + ): + if not isinstance(expires_in, int): + raise ValueError("expires_in should be int.") + if expires_in <= 0: + raise ValueError("expires_in should be positive number.") + self._expires_in = expires_in + + if not isinstance(leeway, int): + raise ValueError("leeway should be int.") + if leeway <= 0: + raise ValueError("leeway should be positive number.") + self._leeway = leeway + + self._cose = COSE(kid_auto_inclusion=True, alg_auto_inclusion=True) + self._claim_names: Dict[str, int] = {} + + @classmethod + def new( + cls, expires_in: int = CWT_DEFAULT_EXPIRES_IN, leeway: int = CWT_DEFAULT_LEEWAY ): """ Constructor. @@ -42,27 +61,14 @@ def __init__( Examples: >>> from cwt import CWT, COSEKey - >>> ctx = CWT(expires_in=3600*24, leeway=10) + >>> ctx = CWT.new(expires_in=3600*24, leeway=10) >>> key = COSEKey.from_symmetric_key(alg="HS256") >>> token = ctx.encode( ... {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, ... key, ... ) """ - if not isinstance(expires_in, int): - raise ValueError("expires_in should be int.") - if expires_in <= 0: - raise ValueError("expires_in should be positive number.") - self._expires_in = expires_in - - if not isinstance(leeway, int): - raise ValueError("leeway should be int.") - if leeway <= 0: - raise ValueError("leeway should be positive number.") - self._leeway = leeway - - self._cose = COSE(kid_auto_inclusion=True, alg_auto_inclusion=True) - self._claim_names: Dict[str, int] = {} + return cls(expires_in, leeway) @property def expires_in(self) -> int: @@ -81,7 +87,7 @@ def leeway(self) -> int: def encode( self, - claims: Union[Claims, Dict[str, Any], Dict[int, Any], bytes, str], + claims: Union[Claims, Dict[str, Any], Dict[int, Any], bytes], key: COSEKeyInterface, nonce: bytes = b"", recipients: Optional[List[RecipientInterface]] = None, @@ -101,7 +107,7 @@ def encode( parameter set to identify the usage. Args: - claims (Union[Claims, Dict[str, Any], Dict[int, Any], bytes, str]): A CWT + claims (Union[Claims, Dict[str, Any], Dict[int, Any], bytes]): 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. diff --git a/cwt/encrypted_cose_key.py b/cwt/encrypted_cose_key.py index 70cab24..0918d35 100644 --- a/cwt/encrypted_cose_key.py +++ b/cwt/encrypted_cose_key.py @@ -75,4 +75,4 @@ def to_cose_key( VerifyError: Failed to verify the COSE key. """ res = cbor2.loads(COSE().decode(CBORTag(16, key), encryption_key)) - return COSEKey.from_dict(res) + return COSEKey.new(res) diff --git a/cwt/recipient.py b/cwt/recipient.py index 6e1ee45..8b8609d 100644 --- a/cwt/recipient.py +++ b/cwt/recipient.py @@ -19,7 +19,7 @@ class Recipient: """ @classmethod - def from_dict( + def new( cls, protected: dict = {}, unprotected: dict = {}, @@ -120,4 +120,4 @@ def from_json(cls, data: Union[str, bytes, Dict[str, Any]]) -> RecipientInterfac raise ValueError("k should be str.") key = base64url_decode(recipient["k"]) - return cls.from_dict(protected, unprotected, key_ops=key_ops, key=key) + return cls.new(protected, unprotected, key_ops=key_ops, key=key) diff --git a/cwt/recipients.py b/cwt/recipients.py index cd815af..2875b7c 100644 --- a/cwt/recipients.py +++ b/cwt/recipients.py @@ -42,13 +42,13 @@ def _create_recipient(cls, recipient: List[Any]) -> RecipientInterface: if not isinstance(recipient[2], bytes): raise ValueError("ciphertext should be bytes.") if len(recipient) == 3: - return Recipient.from_dict(protected, recipient[1], recipient[2]) + return Recipient.new(protected, recipient[1], recipient[2]) if not isinstance(recipient[3], list): raise ValueError("recipients should be list.") recipients: List[RecipientInterface] = [] for r in recipient[3]: recipients.append(cls._create_recipient(r)) - return Recipient.from_dict(protected, recipient[1], recipient[2], recipients) + return Recipient.new(protected, recipient[1], recipient[2], recipients) def derive_key( self, diff --git a/docs/cose_usage.rst b/docs/cose_usage.rst index 4112316..a2b90b4 100644 --- a/docs/cose_usage.rst +++ b/docs/cose_usage.rst @@ -6,7 +6,7 @@ The following is a simple sample code for command line console. .. code-block:: pycon >>> from cwt import COSE, COSEKey - >>> ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + >>> ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) >>> mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") >>> encoded = ctx.encode_and_mac(b"Hello world!", mac_key) >>> encoded.hex() @@ -29,7 +29,7 @@ Create a COSE MAC0 message, verify and decode it as follows: from cwt import COSE, COSEKey mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_mac(b"Hello world!", mac_key) decoded = ctx.decode(encoded, mac_key) @@ -42,7 +42,7 @@ Following two samples are other ways of writing the above example: from cwt import COSE, COSEKey mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac( b"Hello world!", mac_key, @@ -56,7 +56,7 @@ Following two samples are other ways of writing the above example: from cwt import COSE, COSEKey mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac( b"Hello world!", mac_key, @@ -76,7 +76,7 @@ Create a COSE MAC message, verify and decode it as follows: recipient = Recipient.from_json({"alg": "direct", "kid": "01"}) mac_key = COSEKey.from_symmetric_key(alg="HS512", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac(b"Hello world!", mac_key, recipients=[recipient]) decoded = ctx.decode(encoded, mac_key) @@ -88,9 +88,9 @@ Following sample is another way of writing the above example: from cwt import COSE, COSEKey - recipient = Recipient.from_dict(unprotected={"alg": "direct", "kid": "01"}) + recipient = Recipient.new(unprotected={"alg": "direct", "kid": "01"}) mac_key = COSEKey.from_symmetric_key(alg="HS512", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac(b"Hello world!", mac_key, recipients=[recipient]) decoded = ctx.decode(encoded, mac_key) @@ -105,7 +105,7 @@ Create a COSE Encrypt0 message, verify and decode it as follows: from cwt import COSE, COSEKey enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305", kid="01") - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_encrypt(b"Hello world!", enc_key) decoded = ctx.decode(encoded, enc_key) @@ -118,7 +118,7 @@ Following two samples are other ways of writing the above example: from cwt import COSE, COSEKey enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_encrypt( b"Hello world!", enc_key, @@ -133,7 +133,7 @@ Following two samples are other ways of writing the above example: from cwt import COSE, COSEKey enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_encrypt( b"Hello world!", enc_key, @@ -154,7 +154,7 @@ Create a COSE Encrypt message, verify and decode it as follows: recipient = Recipient.from_json({"alg": "direct", "kid": "01"}) enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_encrypt( b"Hello world!", enc_key, @@ -168,9 +168,9 @@ Following sample is another way of writing the above example: from cwt import COSE, COSEKey - recipient = Recipient.from_dict(unprotected={"alg": "direct", "kid": "01"}) + recipient = Recipient.new(unprotected={"alg": "direct", "kid": "01"}) enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305", kid="01") - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac(b"Hello world!", enc_key, recipients=[recipient]) decoded = ctx.decode(encoded, enc_key) @@ -193,7 +193,7 @@ Create a COSE Signature1 message, verify and decode it as follows: "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_sign(b"Hello world!", sig_key) decoded = ctx.decode(encoded, sig_key) @@ -213,7 +213,7 @@ Following two samples are other ways of writing the above example: "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"Hello world!", sig_key, @@ -237,7 +237,7 @@ Following two samples are other ways of writing the above example: "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"Hello world!", sig_key, @@ -265,7 +265,7 @@ Create a COSE Signature message, verify and decode it as follows: "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", }, ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign(b"Hello world!", signers=[signer]) decoded = ctx.decode(encoded, signer.cose_key) @@ -289,6 +289,7 @@ Following two samples are other ways of writing the above example: protected={"alg": "ES256"}, unprotected={"kid": "01"}, ) + ctx = COSE.new() encoded = ctx.encode_and_sign(b"Hello world!", signers=[signer]) decoded = ctx.decode(encoded, signer.cose_key) @@ -311,7 +312,8 @@ Following two samples are other ways of writing the above example: protected={1: -7}, unprotected={4: b"01"}, ) - encoded3 = ctx.encode_and_sign(b"Hello world!", signers=[signer]) + ctx = COSE.new() + encoded = ctx.encode_and_sign(b"Hello world!", signers=[signer]) decoded = ctx.decode(encoded, signer.cose_key) .. _`Supported COSE Algorithms`: ./algorithms.html diff --git a/docs/cwt_usage.rst b/docs/cwt_usage.rst index 0ba6255..fae2647 100644 --- a/docs/cwt_usage.rst +++ b/docs/cwt_usage.rst @@ -17,7 +17,7 @@ The following is a simple sample code for command line console. >>> decoded {1: 'coaps://as.example', 2: 'dajiaji', 7: b'123', 4: 1620088759, 5: 1620085159, 6: 1620085159} - >>> readable = Claims.from_dict(decoded) + >>> readable = Claims.new(decoded) >>> readable.iss 'coaps://as.example' >>> readable.sub @@ -250,7 +250,7 @@ Note that such user-defined claim's key should be less than -65536. # raw[-70002][0] == "bar" # raw[-70003]["baz"] == "qux" # raw[-70004] == 123 - readable = Claims.from_dict(raw) + readable = Claims.new(raw) # readable.get(-70001) == "foo" # readable.get(-70002)[0] == "bar" # readable.get(-70003)["baz"] == "qux" @@ -290,7 +290,7 @@ User-defined claims can also be used with JSON-based claims as follows: ) claims.set_private_claim_names() raw = cwt.decode(token, public_key) - readable = Claims.from_dict( + readable = Claims.new( raw, private_claim_names={ "ext_1": -70001, @@ -374,10 +374,10 @@ On the CWT recipient side: # Verifies and decodes the CWT received from the presenter. raw = cwt.decode(token, public_key) - decoded = Claims.from_dict(raw) + decoded = Claims.new(raw) # Extracts the PoP key from the CWT. - extracted_pop_key = COSEKey.from_dict(decoded.cnf) # = raw[8][1] + extracted_pop_key = COSEKey.new(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: @@ -418,7 +418,7 @@ In case of another PoP confirmation method ``Encrypted_COSE_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 = Claims.new(raw) extracted_pop_key = EncryptedCOSEKey.to_cose_key(decoded.cnf, enc_key) # extracted_pop_key.verify(message, signature) @@ -447,7 +447,7 @@ In case of another PoP confirmation method ``kid``: 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 = Claims.new(raw) # decoded.cnf(=raw[8][3]) is kid. .. _`Supported COSE Algorithms`: ./algorithms.html diff --git a/tests/test_cose.py b/tests/test_cose.py index 162ac46..070834f 100644 --- a/tests/test_cose.py +++ b/tests/test_cose.py @@ -25,7 +25,7 @@ @pytest.fixture(scope="session", autouse=True) def ctx(): - return COSE(alg_auto_inclusion=True) + return COSE.new(alg_auto_inclusion=True) class TestCOSE: @@ -34,11 +34,11 @@ class TestCOSE: """ def test_cose_constructor_with_options(self): - ctx = COSE() + ctx = COSE.new() assert isinstance(ctx, COSE) def test_cose_encode_and_decode_with_options(self): - ctx = COSE() + ctx = COSE.new() # MAC0 mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") @@ -82,7 +82,7 @@ def test_cose_encode_and_decode_with_options(self): assert b"Hello world!" == ctx.decode(encoded, sig_key) def test_cose_encode_and_decode_with_protected_bytes(self): - ctx = COSE() + ctx = COSE.new() # MAC0 mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") @@ -127,7 +127,7 @@ def test_cose_encode_and_decode_with_protected_bytes(self): assert b"Hello world!" == ctx.decode(encoded, sig_key) def test_cose_encode_and_decode_with_recipient_builder(self): - ctx = COSE() + ctx = COSE.new() mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") recipient = Recipient.from_json( @@ -145,13 +145,13 @@ def test_cose_encode_and_decode_with_recipient_builder(self): def test_cose_constructor_with_invalid_kid_auto_inclusion(self): with pytest.raises(ValueError) as err: - COSE(kid_auto_inclusion="xxx") + COSE.new(kid_auto_inclusion="xxx") pytest.fail("COSE should fail.") assert "kid_auto_inclusion should be bool." in str(err.value) def test_cose_constructor_with_invalid_alg_auto_inclusion(self): with pytest.raises(ValueError) as err: - COSE(alg_auto_inclusion="xxx") + COSE.new(alg_auto_inclusion="xxx") pytest.fail("COSE should fail.") assert "alg_auto_inclusion should be bool." in str(err.value) @@ -204,7 +204,7 @@ def test_cose_sample_cose_wg_examples_sign1_pass_01(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", key, @@ -226,7 +226,7 @@ def test_cose_sample_cose_wg_examples_sign1_pass_02(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", key, @@ -261,7 +261,7 @@ def test_cose_sample_cose_wg_examples_sign_pass_01(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", signers=[signer], @@ -282,7 +282,7 @@ def test_cose_sample_cose_wg_examples_sign_pass_02(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", signers=[signer], @@ -317,7 +317,7 @@ def test_cose_sample_cose_wg_examples_ecdsa_01(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", signers=[signer], @@ -352,7 +352,7 @@ def test_cose_sample_cose_wg_examples_eddsa_01(self): .decode("ascii"), } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", signers=[signer], @@ -384,7 +384,7 @@ def test_cose_sample_cose_wg_examples_eddsa_02(self): .decode("ascii"), } ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign( b"This is the content.", signers=[signer], @@ -415,7 +415,7 @@ def test_cose_sample_cose_wg_examples_eddsa_sig_01(self): .decode("ascii"), } ) - ctx = COSE(kid_auto_inclusion=True) + ctx = COSE.new(kid_auto_inclusion=True) encoded = ctx.encode_and_sign( b"This is the content.", key, @@ -447,7 +447,7 @@ def test_cose_sample_cose_wg_examples_eddsa_sig_02(self): .decode("ascii"), } ) - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_sign( b"This is the content.", key, @@ -555,7 +555,7 @@ def test_cose_sample_cose_wg_rfc8152_c_3_2(self): base64url_decode("hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"), context=context, ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_encrypt( b"This is the content.", key=enc_key, @@ -607,7 +607,7 @@ def test_cose_sample_cose_wg_rfc8152_c_3_2_with_json(self): base64url_decode("hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"), context=context, ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_encrypt( b"This is the content.", key=enc_key, @@ -650,7 +650,7 @@ def test_cose_sample_cose_wg_aes_wrap_128_03(self): }, ) recipient.wrap_key(mac_key.key) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac( b"This is the content.", key=mac_key, diff --git a/tests/test_cose_key.py b/tests/test_cose_key.py index d895a27..173445d 100644 --- a/tests/test_cose_key.py +++ b/tests/test_cose_key.py @@ -19,11 +19,6 @@ # from secrets import token_bytes -@pytest.fixture(scope="session", autouse=True) -def ctx(): - return COSEKey() - - class TestCOSEKey: """ Tests for COSEKey. @@ -42,8 +37,8 @@ def test_key_builder_constructor(self): ("HMAC 512/512", 7), ], ) - def test_key_builder_from_symmetric_key_hmac(self, ctx, alg, alg_label): - k = ctx.from_symmetric_key("mysecret", alg=alg) + def test_key_builder_from_symmetric_key_hmac(self, alg, alg_label): + k = COSEKey.from_symmetric_key("mysecret", alg=alg) assert isinstance(k, COSEKeyInterface) assert k.alg == alg_label assert 9 in k.key_ops @@ -72,9 +67,9 @@ def test_key_builder_from_symmetric_key_hmac(self, ctx, alg, alg_label): "AES-CCM-64-128-256", ], ) - def test_key_builder_from_symmetric_key_without_key(self, ctx, alg): + def test_key_builder_from_symmetric_key_without_key(self, alg): try: - k = ctx.from_symmetric_key(alg=alg) + k = COSEKey.from_symmetric_key(alg=alg) assert k.kty == 4 except Exception: pytest.fail("from_symmetric_key should not fail.") @@ -117,10 +112,8 @@ def test_key_builder_from_symmetric_key_without_key(self, ctx, alg): ("ChaCha20/Poly1305", ["wrap key", "unwrap key"], [5, 6]), ], ) - def test_key_builder_from_symmetric_key_with_key_ops( - self, ctx, alg, key_ops, expected - ): - k = ctx.from_symmetric_key(alg=alg, key_ops=key_ops) + def test_key_builder_from_symmetric_key_with_key_ops(self, alg, key_ops, expected): + k = COSEKey.from_symmetric_key(alg=alg, key_ops=key_ops) assert len(k.key_ops) == len(key_ops) for ops in k.key_ops: assert ops in expected @@ -129,9 +122,9 @@ def test_key_builder_from_symmetric_key_with_key_ops( "alg", ["xxx", 0, 8, 9, 34], ) - def test_key_builder_from_symmetric_key_with_invalid_alg(self, ctx, alg): + def test_key_builder_from_symmetric_key_with_invalid_alg(self, alg): with pytest.raises(ValueError) as err: - ctx.from_symmetric_key("mysecret", alg=alg) + COSEKey.from_symmetric_key("mysecret", alg=alg) pytest.fail("from_symmetric_key should fail.") assert f"Unsupported or unknown alg(3): {alg}." in str(err.value) @@ -139,9 +132,9 @@ def test_key_builder_from_symmetric_key_with_invalid_alg(self, ctx, alg): "key_ops", [["xxx"], ["MAC create", "MAC verify", "xxx"]], ) - def test_key_builder_from_symmetric_key_with_invalid_key_ops(self, ctx, key_ops): + def test_key_builder_from_symmetric_key_with_invalid_key_ops(self, key_ops): with pytest.raises(ValueError) as err: - ctx.from_symmetric_key("mysecret", key_ops=key_ops) + COSEKey.from_symmetric_key("mysecret", key_ops=key_ops) pytest.fail("from_symmetric_key should fail.") assert "Unsupported or unknown key_ops." in str(err.value) @@ -219,9 +212,9 @@ def test_key_builder_from_pem_private_with_key_ops(self, key_ops, expected): (b"invalidbytes", "Failed to decode PEM."), ], ) - def test_key_builder_from_pem_with_invalid_key(self, ctx, invalid, msg): + def test_key_builder_from_pem_with_invalid_key(self, invalid, msg): with pytest.raises(ValueError) as err: - ctx.from_pem(invalid) + COSEKey.from_pem(invalid) pytest.fail("from_pem should not fail.") assert msg in str(err.value) @@ -232,7 +225,7 @@ def test_key_builder_from_pem_with_invalid_key(self, ctx, invalid, msg): (["sign"], "Unknown or not permissible key_ops(4) for SignatureKey: 1"), ], ) - def test_key_builder_from_pem_public_with_invalid_key_ops(self, ctx, invalid, msg): + def test_key_builder_from_pem_public_with_invalid_key_ops(self, invalid, msg): with open(key_path("public_key_ed25519.pem")) as key_file: with pytest.raises(ValueError) as err: COSEKey.from_pem(key_file.read(), key_ops=invalid) @@ -250,7 +243,7 @@ def test_key_builder_from_pem_public_with_invalid_key_ops(self, ctx, invalid, ms (["xxx"], "Unsupported or unknown key_ops."), ], ) - def test_key_builder_from_pem_private_with_invalid_key_ops(self, ctx, invalid, msg): + def test_key_builder_from_pem_private_with_invalid_key_ops(self, invalid, msg): with open(key_path("private_key_ed25519.pem")) as key_file: with pytest.raises(ValueError) as err: COSEKey.from_pem(key_file.read(), key_ops=invalid) @@ -342,11 +335,11 @@ def test_key_builder_from_pem_private_with_invalid_alg(self, alg, msg): {1: 4, 3: 33}, ], ) - def test_key_builder_from_dict_with_valid_args(self, ctx, cose_key): + def test_key_builder_new_with_valid_args(self, cose_key): try: - ctx.from_dict(cose_key) + COSEKey.new(cose_key) except Exception: - pytest.fail("from_dict should not fail.") + pytest.fail("new should not fail.") @pytest.mark.parametrize( "invalid, msg", @@ -362,10 +355,10 @@ def test_key_builder_from_dict_with_valid_args(self, ctx, cose_key): ({1: 4, 3: 0}, "Unsupported or unknown alg(3): 0."), ], ) - def test_key_builder_from_dict_with_invalid_args(self, ctx, invalid, msg): + def test_key_builder_new_with_invalid_args(self, invalid, msg): with pytest.raises(ValueError) as err: - ctx.from_dict(invalid) - pytest.fail("from_dict should fail.") + COSEKey.new(invalid) + pytest.fail("new should fail.") assert msg in str(err.value) @pytest.mark.parametrize( @@ -390,28 +383,28 @@ def test_key_builder_from_dict_with_invalid_args(self, ctx, invalid, msg): "public_key_rsa.json", ], ) - def test_key_builder_from_jwk(self, ctx, key): + def test_key_builder_from_jwk(self, key): try: with open(key_path(key)) as key_file: - ctx.from_jwk(key_file.read()) + COSEKey.from_jwk(key_file.read()) except Exception: pytest.fail("from_jwk should not fail.") - def test_key_builder_from_jwk_with_key_ops(self, ctx): + def test_key_builder_from_jwk_with_key_ops(self): try: with open(key_path("public_key_ed25519.json")) as key_file: obj = json.loads(key_file.read()) obj["key_ops"] = ["verify"] - ctx.from_jwk(obj) + COSEKey.from_jwk(obj) except Exception: pytest.fail("from_jwk should not fail.") - def test_key_builder_from_jwk_without_use(self, ctx): + def test_key_builder_from_jwk_without_use(self): try: with open(key_path("public_key_ed25519.json")) as key_file: obj = json.loads(key_file.read()) del obj["use"] - ctx.from_jwk(obj) + COSEKey.from_jwk(obj) except Exception: pytest.fail("from_jwk should not fail.") @@ -428,12 +421,12 @@ def test_key_builder_from_jwk_without_use(self, ctx): ], ) def test_key_builder_from_jwk_with_encode_and_sign( - self, ctx, private_key_path, public_key_path + self, private_key_path, public_key_path ): with open(key_path(private_key_path)) as key_file: - private_key = ctx.from_jwk(key_file.read()) + private_key = COSEKey.from_jwk(key_file.read()) with open(key_path(public_key_path)) as key_file: - public_key = ctx.from_jwk(key_file.read()) + public_key = COSEKey.from_jwk(key_file.read()) token = cwt.encode_and_sign( Claims.from_json( {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"} @@ -459,13 +452,11 @@ def test_key_builder_from_jwk_with_encode_and_sign( ("private_key_rsa.json", "public_key_rsa.json"), ], ) - def test_key_builder_from_jwk_with_encode( - self, ctx, private_key_path, public_key_path - ): + def test_key_builder_from_jwk_with_encode(self, private_key_path, public_key_path): with open(key_path(private_key_path)) as key_file: - private_key = ctx.from_jwk(key_file.read()) + private_key = COSEKey.from_jwk(key_file.read()) with open(key_path(public_key_path)) as key_file: - public_key = ctx.from_jwk(key_file.read()) + public_key = COSEKey.from_jwk(key_file.read()) token = cwt.encode( {"iss": "coaps://as.example", "sub": "dajiaji", "cti": "123"}, private_key, @@ -525,8 +516,8 @@ def test_key_builder_from_jwk_with_encode( ), ], ) - def test_key_builder_from_jwk_with_invalid_arg(self, ctx, invalid, msg): + def test_key_builder_from_jwk_with_invalid_arg(self, invalid, msg): with pytest.raises(ValueError) as err: - ctx.from_jwk(invalid) + COSEKey.from_jwk(invalid) pytest.fail("from_jwk should fail.") assert msg in str(err.value) diff --git a/tests/test_cose_sample.py b/tests/test_cose_sample.py index b6976f5..b126a8f 100644 --- a/tests/test_cose_sample.py +++ b/tests/test_cose_sample.py @@ -9,11 +9,11 @@ class TestCOSESample: def test_cose_usage_examples_cose_mac0(self): mac_key = COSEKey.from_symmetric_key(alg="HS256", kid="01") - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_mac(b"Hello world!", mac_key) assert b"Hello world!" == ctx.decode(encoded, mac_key) - ctx = COSE() + ctx = COSE.new() encoded2 = ctx.encode_and_mac( b"Hello world!", mac_key, @@ -36,15 +36,15 @@ def test_cose_usage_examples_cose_mac(self): mac_key = COSEKey.from_symmetric_key(alg="HS512", kid="01") recipient = Recipient.from_json({"alg": "direct", "kid": "01"}) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_mac(b"Hello world!", mac_key, recipients=[recipient]) assert b"Hello world!" == ctx.decode(encoded, mac_key) - recipient2 = Recipient.from_dict(unprotected={"alg": "direct", "kid": "01"}) + recipient2 = Recipient.new(unprotected={"alg": "direct", "kid": "01"}) encoded2 = ctx.encode_and_mac(b"Hello world!", mac_key, recipients=[recipient2]) assert b"Hello world!" == ctx.decode(encoded2, mac_key) - recipient3 = Recipient.from_dict(unprotected={1: -6, 4: b"01"}) + recipient3 = Recipient.new(unprotected={1: -6, 4: b"01"}) encoded3 = ctx.encode_and_mac(b"Hello world!", mac_key, recipients=[recipient3]) assert b"Hello world!" == ctx.decode(encoded3, mac_key) @@ -54,11 +54,11 @@ def test_cose_usage_examples_cose_encrypt0(self): enc_key = COSEKey.from_symmetric_key(alg="ChaCha20/Poly1305", kid="01") nonce = enc_key.generate_nonce() - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_encrypt(b"Hello world!", enc_key, nonce=nonce) assert b"Hello world!" == ctx.decode(encoded, enc_key) - ctx = COSE() + ctx = COSE.new() encoded2 = ctx.encode_and_encrypt( b"Hello world!", enc_key, @@ -84,7 +84,7 @@ def test_cose_usage_examples_cose_encrypt(self): nonce = enc_key.generate_nonce() recipient = Recipient.from_json({"alg": "direct", "kid": "01"}) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_encrypt( b"Hello world!", enc_key, @@ -93,7 +93,7 @@ def test_cose_usage_examples_cose_encrypt(self): ) assert b"Hello world!" == ctx.decode(encoded, enc_key) - recipient = Recipient.from_dict(unprotected={"alg": "direct", "kid": "01"}) + recipient = Recipient.new(unprotected={"alg": "direct", "kid": "01"}) encoded2 = ctx.encode_and_encrypt( b"Hello world!", enc_key, @@ -124,11 +124,11 @@ def test_cose_usage_examples_cose_signature1(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", } ) - ctx = COSE(alg_auto_inclusion=True, kid_auto_inclusion=True) + ctx = COSE.new(alg_auto_inclusion=True, kid_auto_inclusion=True) encoded = ctx.encode_and_sign(b"Hello world!", sig_key) assert b"Hello world!" == ctx.decode(encoded, sig_key) - ctx = COSE() + ctx = COSE.new() encoded2 = ctx.encode_and_sign( b"Hello world!", sig_key, @@ -157,7 +157,7 @@ def test_cose_usage_examples_cose_signature(self): "d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM", }, ) - ctx = COSE() + ctx = COSE.new() encoded = ctx.encode_and_sign(b"Hello world!", signers=[signer]) assert b"Hello world!" == ctx.decode(encoded, signer.cose_key) diff --git a/tests/test_cwt.py b/tests/test_cwt.py index eabbaa7..6a8c28c 100644 --- a/tests/test_cwt.py +++ b/tests/test_cwt.py @@ -22,7 +22,7 @@ @pytest.fixture(scope="session", autouse=True) def ctx(): - return CWT() + return CWT.new() class TestCWT: @@ -31,18 +31,18 @@ class TestCWT: """ def test_cwt_constructor_without_args(self): - ctx = CWT() + ctx = CWT.new() assert isinstance(ctx, CWT) assert ctx.expires_in == 3600 assert ctx.leeway == 60 def test_cwt_constructor_with_expires_in(self): - ctx = CWT(expires_in=7200) + ctx = CWT.new(expires_in=7200) assert isinstance(ctx, CWT) assert ctx.expires_in == 7200 def test_cwt_constructor_with_leeway(self): - ctx = CWT(leeway=10) + ctx = CWT.new(leeway=10) assert isinstance(ctx, CWT) assert ctx.leeway == 10 @@ -56,8 +56,8 @@ def test_cwt_constructor_with_leeway(self): ) def test_cwt_constructor_with_invalid_expires_in(self, invalid): with pytest.raises(ValueError) as err: - CWT(expires_in=invalid) - pytest.fail("CWT() should fail.") + CWT.new(expires_in=invalid) + pytest.fail("CWT.new() should fail.") assert "should be" in str(err.value) @pytest.mark.parametrize( @@ -70,8 +70,8 @@ def test_cwt_constructor_with_invalid_expires_in(self, invalid): ) def test_cwt_constructor_with_invalid_leeway(self, invalid): with pytest.raises(ValueError) as err: - CWT(leeway=invalid) - pytest.fail("CWT() should fail.") + CWT.new(leeway=invalid) + pytest.fail("CWT.new() should fail.") assert "should be" in str(err.value) def test_cwt_encode_with_claims_object(self, ctx): @@ -99,7 +99,7 @@ def test_cwt_encode_with_claims_object(self, ctx): def test_cwt_encode_with_invalid_key(self, ctx, invalid_key): with pytest.raises(ValueError) as err: ctx.encode({1: "https://as.example", 2: "someone", 7: b"123"}, invalid_key) - pytest.fail("CWT() should fail.") + pytest.fail("CWT.new() should fail.") assert "The key operation could not be specified." in str(err.value) def test_cwt_encode_and_mac_with_default_alg(self, ctx): diff --git a/tests/test_recipient.py b/tests/test_recipient.py index 3284398..834eec8 100644 --- a/tests/test_recipient.py +++ b/tests/test_recipient.py @@ -226,9 +226,9 @@ class TestRecipient: ), ], ) - def test_recipient_from_dict_with_invalid_arg(self, protected, unprotected, msg): + def test_recipient_new_with_invalid_arg(self, protected, unprotected, msg): with pytest.raises(ValueError) as err: - Recipient.from_dict(protected, unprotected) + Recipient.new(protected, unprotected) pytest.fail("Recipient() should fail.") assert msg in str(err.value) diff --git a/tests/test_sample.py b/tests/test_sample.py index 0777939..5b5c308 100644 --- a/tests/test_sample.py +++ b/tests/test_sample.py @@ -456,8 +456,8 @@ def test_sample_readme_cwt_with_pop_jwk(self): decoded = cwt.decode(token, public_key) assert 8 in decoded and isinstance(decoded[8], dict) assert 1 in decoded[8] and isinstance(decoded[8][1], dict) - c = Claims.from_dict(decoded) - extracted = COSEKey.from_dict(c.cnf) + c = Claims.new(decoded) + extracted = COSEKey.new(c.cnf) try: extracted.verify(msg, sig) except Exception: @@ -492,7 +492,7 @@ def test_sample_readme_cwt_with_pop_encrypted_cose_key_readable(self): decoded = cwt.decode(token, public_key) assert 8 in decoded and isinstance(decoded[8], dict) assert 2 in decoded[8] and isinstance(decoded[8][2], list) - c = Claims.from_dict(decoded) + c = Claims.new(decoded) extracted = EncryptedCOSEKey.to_cose_key(c.cnf, enc_key) assert extracted.kty == 4 # Symmetric assert extracted.alg == 5 # HMAC 256/256 @@ -519,7 +519,7 @@ def test_sample_readme_cwt_with_pop_kid_readable(self): decoded = cwt.decode(token, public_key) assert 8 in decoded and isinstance(decoded[8], dict) assert 3 in decoded[8] and decoded[8][3] == b"pop-key-id-of-cwt-presenter" - c = Claims.from_dict(decoded) + c = Claims.new(decoded) assert c.cnf == "pop-key-id-of-cwt-presenter" def test_sample_readme_cwt_with_pop_cose_key(self): @@ -544,7 +544,7 @@ def test_sample_readme_cwt_with_pop_cose_key(self): decoded = cwt.decode(token, public_key) assert 8 in decoded and isinstance(decoded[8], dict) assert 1 in decoded[8] and isinstance(decoded[8][1], dict) - extracted = COSEKey.from_dict(decoded[8][1]) + extracted = COSEKey.new(decoded[8][1]) assert extracted.kty == 2 # EC2 assert extracted.crv == 1 # P-256 @@ -626,7 +626,7 @@ def test_sample_readme_cwt_with_user_defined_claims(self): assert isinstance(raw[-70003], dict) assert raw[-70003]["baz"] == "qux" assert raw[-70004] == 123 - readable = Claims.from_dict(raw) + readable = Claims.new(raw) assert readable.get(-70001) == "foo" assert readable.get(-70002)[0] == "bar" assert readable.get(-70003)["baz"] == "qux" @@ -658,7 +658,7 @@ def test_sample_readme_cwt_with_user_defined_claims_readable(self): private_key, ) raw = cwt.decode(token, public_key) - readable = Claims.from_dict( + readable = Claims.new( raw, private_claim_names={ "ext_1": -70001, @@ -737,7 +737,7 @@ def test_sample_rfc8392_a3_with_encoding(self): assert 1 in decoded and decoded[1] == "coap://as.example.com" def test_sample_rfc8392_a4_old(self): - key = COSEKey.from_dict( + key = COSEKey.new( { -1: bytes.fromhex( "403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388" @@ -765,7 +765,7 @@ def test_sample_rfc8392_a4_old(self): assert 1 in decoded and decoded[1] == "coap://as.example.com" def test_sample_rfc8392_a4(self): - key = COSEKey.from_dict( + key = COSEKey.new( { -1: bytes.fromhex( "403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388"