Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Changes
Unreleased
----------

- Correct COSE signing header annotations to accept dictionaries and ResolvedHeader in strict type checkers.

Version 3.3.0
-------------

Expand Down
12 changes: 8 additions & 4 deletions cwt/cose.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ def encode_and_sign(
self,
payload: bytes,
key: Optional[COSEKeyInterface] = None,
protected: Optional[dict] = None,
unprotected: Optional[dict] = None,
protected: Optional[Union[Dict[Any, Any], ResolvedHeader]] = None,
unprotected: Optional[Union[Dict[Any, Any], ResolvedHeader]] = None,
signers: List[Signer] = [],
external_aad: bytes = b"",
out: str = "",
Expand All @@ -288,8 +288,12 @@ def encode_and_sign(
key (Optional[COSEKeyInterface]): A signing key for single signer
cases. When the ``signers`` parameter is set, this ``key`` will
be ignored and should not be set.
protected (Optional[dict]): Parameters that are to be cryptographically protected.
unprotected (Optional[dict]): Parameters that are not cryptographically protected.
protected (Optional[Union[Dict[Any, Any], ResolvedHeader]]): Parameters that are
to be cryptographically protected. Use ``ResolvedHeader`` to bypass
named-header resolution and value conversion.
unprotected (Optional[Union[Dict[Any, Any], ResolvedHeader]]): Parameters that are
not cryptographically protected. Use ``ResolvedHeader`` to bypass
named-header resolution and value conversion.
signers (List[Signer]): A list of signer information objects for
multiple signer cases.
external_aad(bytes): External additional authenticated data supplied
Expand Down
44 changes: 44 additions & 0 deletions tests/test_cose.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import base64
import datetime
from secrets import token_bytes
from typing import Any, Dict, Union

import cbor2
import pytest
Expand All @@ -20,6 +21,7 @@
from cwt.enums import COSEAlgs, COSEHeaders
from cwt.recipient_interface import RecipientInterface
from cwt.signer import Signer
from cwt.utils import ResolvedHeader

from .utils import key_path

Expand Down Expand Up @@ -133,6 +135,48 @@ def test_cose_encode_and_decode_signature1_with_options(self):
encoded = ctx.encode_and_sign(b"Hello world!", sig_key)
assert b"Hello world!" == ctx.decode(encoded, sig_key)

@pytest.mark.parametrize("wrap_protected", [False, True])
@pytest.mark.parametrize("wrap_unprotected", [False, True])
@pytest.mark.parametrize("out", ["", "cbor2/CBORTag"])
def test_cose_signing_resolved_headers(self, ctx, wrap_protected, wrap_unprotected, out):
with open(key_path("private_key_ed25519.pem")) as key_file:
private_key = COSEKey.from_pem(key_file.read())
with open(key_path("public_key_ed25519.pem")) as key_file:
public_key = COSEKey.from_pem(key_file.read(), kid="01")
protected: Dict[Union[str, int], Any] = {1: -8, -65537: "protected text", -65538: b"protected bytes"}
unprotected: Dict[Union[str, int], Any] = {4: b"01", -65539: "unprotected text", -65540: b"unprotected bytes"}
if wrap_protected:
protected["application"] = "protected application text"
if wrap_unprotected:
unprotected["kid"] = "literal application label and text"
encoded = ctx.encode_and_sign(
b"Hello world!",
private_key,
protected=ResolvedHeader(protected) if wrap_protected else protected,
unprotected=ResolvedHeader(unprotected) if wrap_unprotected else unprotected,
out=out,
)
assert isinstance(encoded, CBORTag if out else bytes)
p, u, payload = ctx.decode_with_headers(encoded, public_key)
assert p == protected
assert u == unprotected
assert payload == b"Hello world!"

def test_cose_signing_named_headers(self, ctx):
with open(key_path("private_key_ed25519.pem")) as key_file:
key = COSEKey.from_pem(key_file.read(), kid="01")
encoded = ctx.encode_and_sign(
b"Hello world!",
key,
protected={"alg": "EdDSA", "kid": "01"},
unprotected={"cty": "application/cose", "x5u": "https://example.com/cert"},
)
assert isinstance(encoded, bytes)
p, u, payload = ctx.decode_with_headers(encoded, key)
assert p == {1: -8, 4: b"01"}
assert u == {3: "application/cose", 35: "https://example.com/cert"}
assert payload == b"Hello world!"

# def test_cose_encode_and_decode_mac0_with_protected_bytes(self):
# ctx = COSE.new(kid_auto_inclusion=True)

Expand Down
Loading