Skip to content

Commit 390d187

Browse files
authored
Add encode method to recipient algs.
Add encode method to recipient algs.
2 parents 85f0f2a + 473dcb9 commit 390d187

17 files changed

Lines changed: 381 additions & 384 deletions

README.md

Lines changed: 79 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,7 @@ from cwt import COSE, COSEKey, Recipient
166166
mac_key = COSEKey.generate_symmetric_key(alg="HS512", kid="01")
167167

168168
# The sender side:
169-
r = Recipient.from_jwk({"alg": "direct"})
170-
r.apply(mac_key)
169+
r = Recipient.new(unprotected={"alg": "direct", "kid": mac_key.kid})
171170

172171
sender = COSE.new()
173172
encoded = sender.encode_and_mac(b"Hello world!", mac_key, recipients=[r])
@@ -188,18 +187,17 @@ shared_material = token_bytes(32)
188187
shared_key = COSEKey.from_symmetric_key(shared_material, kid="01")
189188

190189
# The sender side:
191-
r = Recipient.from_jwk(
192-
{
193-
"kty": "oct",
190+
r = Recipient.new(
191+
unprotected={
194192
"alg": "direct+HKDF-SHA-256",
195193
"salt": "aabbccddeeffgghh",
196194
},
197195
)
198-
mac_key = r.apply(shared_key, context={"alg": "HS256"})
196+
mac_key = r.encode(shared_key.to_bytes(), context={"alg": "HS256"})
199197
sender = COSE.new(alg_auto_inclusion=True)
200198
encoded = sender.encode_and_mac(
201199
b"Hello world!",
202-
key=mac_key,
200+
mac_key,
203201
recipients=[r],
204202
)
205203

@@ -215,31 +213,25 @@ The AES key wrap algorithm can be used to wrap a MAC key as follows:
215213
```py
216214
from cwt import COSE, COSEKey, Recipient
217215

218-
# The sender side:
219-
mac_key = COSEKey.generate_symmetric_key(alg="HS512")
220-
r = Recipient.from_jwk(
216+
enc_key = COSEKey.from_jwk(
221217
{
222218
"kty": "oct",
223-
"alg": "A128KW",
224219
"kid": "01",
220+
"alg": "A128KW",
225221
"k": "hJtXIZ2uSN5kbQfbtTNWbg", # A shared wrapping key
226-
},
227-
)
228-
r.apply(mac_key)
222+
}
223+
);
224+
225+
# The sender side:
226+
mac_key = COSEKey.generate_symmetric_key(alg="HS512")
227+
r = Recipient.new(unprotected={"alg": "A128KW"}, sender_key=enc_key)
228+
r.encode(mac_key.to_bytes())
229229
sender = COSE.new(alg_auto_inclusion=True)
230-
encoded = sender.encode_and_mac(b"Hello world!", key=mac_key, recipients=[r])
230+
encoded = sender.encode_and_mac(b"Hello world!", mac_key, recipients=[r])
231231

232232
# The recipient side:
233233
recipient = COSE.new()
234-
shared_key = COSEKey.from_jwk(
235-
{
236-
"kty": "oct",
237-
"alg": "A128KW",
238-
"kid": "01",
239-
"k": "hJtXIZ2uSN5kbQfbtTNWbg",
240-
},
241-
)
242-
assert b"Hello world!" == recipient.decode(encoded, shared_key)
234+
assert b"Hello world!" == recipient.decode(encoded, enc_key)
243235
```
244236

245237
#### Direct Key Agreement for MAC
@@ -263,20 +255,10 @@ pub_key = COSEKey.from_jwk(
263255
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
264256
}
265257
)
266-
r = Recipient.from_jwk(
267-
{
268-
"kty": "EC",
269-
"alg": "ECDH-ES+HKDF-256",
270-
"crv": "P-256",
271-
},
272-
)
273-
mac_key = r.apply(recipient_key=pub_key, context={"alg": "HS256"})
258+
r = Recipient.new({"alg": "ECDH-ES+HKDF-256"}, recipient_key=pub_key)
259+
mac_key = r.encode(context={"alg": "HS256"})
274260
sender = COSE.new(alg_auto_inclusion=True)
275-
encoded = sender.encode_and_mac(
276-
b"Hello world!",
277-
key=mac_key,
278-
recipients=[r],
279-
)
261+
encoded = sender.encode_and_mac(b"Hello world!", mac_key, recipients=[r])
280262

281263
# The recipient side:
282264
# The following key is the private key of the above pub_key.
@@ -304,41 +286,39 @@ assert b"Hello world!" == recipient.decode(encoded, priv_key, context={"alg": "H
304286
```py
305287
from cwt import COSE, COSEKey, Recipient
306288

289+
mac_key = COSEKey.generate_symmetric_key(alg="HS256")
290+
307291
# The sender side:
308-
r = Recipient.from_jwk(
309-
{
310-
"kty": "OKP",
311-
"alg": "ECDH-ES+HKDF-256",
312-
"crv": "X25519",
313-
},
314-
)
315292
pub_key = COSEKey.from_jwk(
316293
{
317-
"kty": "OKP",
318-
"alg": "ECDH-ES+HKDF-256",
294+
"kty": "EC",
295+
"alg": "ECDH-ES+A128KW",
319296
"kid": "01",
320-
"crv": "X25519",
321-
"x": "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI",
297+
"crv": "P-256",
298+
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
299+
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
322300
}
323301
)
324-
mac_key = r.apply(recipient_key=pub_key, context={"alg": "HS256"})
302+
r = Recipient.new(unprotected={"alg": "ECDH-ES+A128KW"}, recipient_key=pub_key)
303+
r.encode(mac_key.to_bytes(), context={"alg": "HS256"})
325304
sender = COSE.new(alg_auto_inclusion=True)
326305
encoded = sender.encode_and_mac(
327306
b"Hello world!",
328-
key=mac_key,
307+
mac_key,
329308
recipients=[r],
330309
)
331310

332311
# The recipient side:
333312
recipient = COSE.new()
334313
priv_key = COSEKey.from_jwk(
335314
{
336-
"kty": "OKP",
337-
"alg": "ECDH-ES+HKDF-256",
315+
"kty": "EC",
316+
"alg": "ECDH-ES+A128KW",
338317
"kid": "01",
339-
"crv": "X25519",
340-
"x": "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI",
341-
"d": "vsJ1oX5NNi0IGdwGldiac75r-Utmq3Jq4LGv48Q_Qc4",
318+
"crv": "P-256",
319+
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
320+
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
321+
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
342322
}
343323
)
344324
assert b"Hello world!" == recipient.decode(encoded, priv_key, context={"alg": "HS256"})
@@ -435,8 +415,8 @@ enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305", kid="01")
435415

436416
# The sender side:
437417
nonce = enc_key.generate_nonce()
438-
r = Recipient.from_jwk({"alg": "direct"})
439-
r.apply(enc_key)
418+
r = Recipient.new(unprotected={"alg": "direct", "kid": enc_key.kid})
419+
# r = Recipient.new(unprotected={1: -6, 4: enc_key.kid}) # is also acceptable
440420

441421
sender = COSE.new()
442422
encoded = sender.encode_and_encrypt(
@@ -461,14 +441,13 @@ shared_material = token_bytes(32)
461441
shared_key = COSEKey.from_symmetric_key(shared_material, kid="01")
462442

463443
# The sender side:
464-
r = Recipient.from_jwk(
465-
{
466-
"kty": "oct",
444+
r = Recipient.new(
445+
unprotected={
467446
"alg": "direct+HKDF-SHA-256",
468447
"salt": "aabbccddeeffgghh",
469448
},
470449
)
471-
enc_key = r.apply(shared_key, context={"alg": "A256GCM"})
450+
enc_key = r.encode(shared_key.to_bytes(), context={"alg": "A256GCM"})
472451
sender = COSE.new(alg_auto_inclusion=True)
473452
encoded = sender.encode_and_encrypt(
474453
b"Hello world!",
@@ -492,15 +471,19 @@ from cwt import COSE, COSEKey, Recipient
492471
enc_key = COSEKey.generate_symmetric_key(alg="ChaCha20/Poly1305")
493472

494473
# The sender side:
495-
r = Recipient.from_jwk(
474+
wrapping_key = COSEKey.from_jwk(
496475
{
497476
"kty": "oct",
498477
"alg": "A128KW",
499478
"kid": "01",
500479
"k": "hJtXIZ2uSN5kbQfbtTNWbg", # A shared wrapping key
501-
},
480+
}
481+
)
482+
r = Recipient.new(
483+
unprotected={"alg": "A128KW"},
484+
sender_key=wrapping_key,
502485
)
503-
r.apply(enc_key)
486+
r.encode(enc_key.to_bytes())
504487
sender = COSE.new(alg_auto_inclusion=True)
505488
encoded = sender.encode_and_encrypt(b"Hello world!", key=enc_key, recipients=[r])
506489

@@ -528,13 +511,6 @@ agreement methods (``ECDH-ES+HKDF-256`` with various curves).
528511
from cwt import COSE, COSEKey, Recipient
529512

530513
# The sender side:
531-
r = Recipient.from_jwk(
532-
{
533-
"kty": "EC",
534-
"alg": "ECDH-ES+HKDF-256",
535-
"crv": "P-256",
536-
},
537-
)
538514
pub_key = COSEKey.from_jwk(
539515
{
540516
"kty": "EC",
@@ -544,7 +520,8 @@ pub_key = COSEKey.from_jwk(
544520
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
545521
}
546522
)
547-
enc_key = r.apply(recipient_key=pub_key, context={"alg": "A128GCM"})
523+
r = Recipient.new(unprotected={"alg": "ECDH-ES+HKDF-256"}, recipient_key=pub_key)
524+
enc_key = r.encode(context={"alg": "A128GCM"})
548525
sender = COSE.new(alg_auto_inclusion=True)
549526
encoded = sender.encode_and_encrypt(
550527
b"Hello world!",
@@ -574,43 +551,51 @@ assert b"Hello world!" == recipient.decode(encoded, priv_key, context={"alg": "A
574551
from cwt import COSE, COSEKey, Recipient
575552

576553
# The sender side:
577-
r = Recipient.from_jwk(
554+
enc_key = COSEKey.generate_symmetric_key(alg="A128GCM")
555+
nonce = enc_key.generate_nonce()
556+
r_pub_key = COSEKey.from_jwk(
578557
{
579-
"kty": "OKP",
580-
"alg": "ECDH-ES+HKDF-256",
581-
"crv": "X25519",
582-
},
558+
"kty": "EC",
559+
"crv": "P-256",
560+
"kid": "meriadoc.brandybuck@buckland.example",
561+
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
562+
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
563+
}
583564
)
584-
pub_key = COSEKey.from_jwk(
565+
s_priv_key = COSEKey.from_jwk(
585566
{
586-
"kty": "OKP",
587-
"alg": "ECDH-ES+HKDF-256",
588-
"kid": "01",
589-
"crv": "X25519",
590-
"x": "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI",
567+
"kty": "EC",
568+
"crv": "P-256",
569+
"alg": "ECDH-SS+A128KW",
570+
"x": "7cvYCcdU22WCwW1tZXR8iuzJLWGcd46xfxO1XJs-SPU",
571+
"y": "DzhJXgz9RI6TseNmwEfLoNVns8UmvONsPzQDop2dKoo",
572+
"d": "Uqr4fay_qYQykwcNCB2efj_NFaQRRQ-6fHZm763jt5w",
591573
}
592574
)
593-
enc_key = r.apply(recipient_key=pub_key, context={"alg": "A128GCM"})
575+
r = Recipient.new(unprotected={"alg": "ECDH-SS+A128KW"}, sender_key=s_priv_key, recipient_key=r_pub_key)
576+
r.encode(enc_key.to_bytes(), context={"alg": "A128GCM"})
594577
sender = COSE.new(alg_auto_inclusion=True)
595578
encoded = sender.encode_and_encrypt(
596579
b"Hello world!",
597580
key=enc_key,
581+
nonce=nonce,
598582
recipients=[r],
599583
)
600584

601585
# The recipient side:
602586
recipient = COSE.new()
603-
priv_key = COSEKey.from_jwk(
587+
r_priv_key = COSEKey.from_jwk(
604588
{
605-
"kty": "OKP",
606-
"alg": "ECDH-ES+HKDF-256",
607-
"kid": "01",
608-
"crv": "X25519",
609-
"x": "y3wJq3uXPHeoCO4FubvTc7VcBuqpvUrSvU6ZMbHDTCI",
610-
"d": "vsJ1oX5NNi0IGdwGldiac75r-Utmq3Jq4LGv48Q_Qc4",
589+
"kty": "EC",
590+
"crv": "P-256",
591+
"alg": "ECDH-SS+A128KW",
592+
"kid": "meriadoc.brandybuck@buckland.example",
593+
"x": "Ze2loSV3wrroKUN_4zhwGhCqo3Xhu1td4QjeQ5wIVR0",
594+
"y": "HlLtdXARY_f55A3fnzQbPcm6hgr34Mp8p-nuzQCE0Zw",
595+
"d": "r_kHyZ-a06rmxM3yESK84r1otSg-aQcVStkRhA-iCM8",
611596
}
612597
)
613-
assert b"Hello world!" == recipient.decode(encoded, priv_key, context={"alg": "A128GCM"})
598+
assert b"Hello world!" == recipient.decode(encoded, r_priv_key, context={"alg": "A128GCM"})
614599
```
615600

616601
#### COSE-HPKE (Encrypt)
@@ -645,8 +630,9 @@ r = Recipient.new(
645630
3: 0x0001, # aead: AES-128-GCM
646631
},
647632
},
633+
recipient_key=rpk,
648634
)
649-
r.encode(enc_key.to_bytes(), recipient_key=rpk)
635+
r.encode(enc_key.to_bytes())
650636
sender = COSE.new()
651637
encoded = sender.encode_and_encrypt(
652638
b"This is the content.",

cwt/algs/raw.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ def __init__(self, params: Dict[int, Any]):
2525
def key(self) -> bytes:
2626
return self._key
2727

28+
def to_bytes(self) -> bytes:
29+
return self._key
30+
2831
def to_dict(self) -> Dict[int, Any]:
2932
res = super().to_dict()
3033
res[-1] = self._key

cwt/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
# COSE Header Parameters
3939
COSE_HEADER_PARAMETERS = {
40+
"salt": -20,
4041
"alg": 1,
4142
"crit": 2,
4243
"cty": 3,

cwt/cose.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ def encode_and_encrypt(
168168
# Encrypt0
169169
if not recipients:
170170
if 1 in p and p[1] == -1: # HPKE
171-
hpke = HPKE(p, u)
172-
hpke.encode(payload, recipient_key=key, external_aad=external_aad, aad_context="Encrypt0")
171+
hpke = HPKE(p, u, recipient_key=key)
172+
hpke.encode(payload, external_aad=external_aad, aad_context="Encrypt0")
173173
res = CBORTag(16, hpke.to_list())
174174
return res if out == "cbor2/CBORTag" else self._dumps(res)
175175
if key is None:

cwt/cose_key_interface.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ def key(self) -> Any:
104104
"""
105105
raise NotImplementedError
106106

107+
@property
108+
def crv(self) -> int:
109+
"""
110+
The curve (``crv`` parameter value) of the key.
111+
"""
112+
raise NotImplementedError
113+
107114
def to_bytes(self) -> bytes:
108115
"""
109116
Serializes the body of the key as a byte string.

cwt/recipient.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def new(
3636
ciphertext: bytes = b"",
3737
recipients: List[Any] = [],
3838
sender_key: Optional[COSEKeyInterface] = None,
39+
recipient_key: Optional[COSEKeyInterface] = None,
3940
) -> RecipientInterface:
4041
"""
4142
Creates a recipient from a CBOR-like dictionary with numeric keys.
@@ -44,7 +45,8 @@ def new(
4445
protected (dict): Parameters that are to be cryptographically protected.
4546
unprotected (dict): Parameters that are not cryptographically protected.
4647
ciphertext (List[Any]): A cipher text.
47-
sender_key (Optional[COSEKeyInterface]): A sender key as COSEKey.
48+
sender_key (Optional[COSEKeyInterface]): A sender private key as COSEKey.
49+
recipient_key (Optional[COSEKeyInterface]): A recipient public key as COSEKey.
4850
Returns:
4951
RecipientInterface: A recipient object.
5052
Raises:
@@ -63,13 +65,13 @@ def new(
6365
if alg in [-3, -4, -5]:
6466
if not sender_key:
6567
sender_key = COSEKey.from_symmetric_key(alg=alg)
66-
return AESKeyWrap(p, u, sender_key, ciphertext, recipients)
68+
return AESKeyWrap(p, u, ciphertext, recipients, sender_key)
6769
if alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_DIRECT.values():
68-
return ECDH_DirectHKDF(p, u, ciphertext, recipients, sender_key)
70+
return ECDH_DirectHKDF(p, u, ciphertext, recipients, sender_key, recipient_key)
6971
if alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_WITH_KEY_WRAP.values():
70-
return ECDH_AESKeyWrap(p, u, ciphertext, recipients, sender_key)
72+
return ECDH_AESKeyWrap(p, u, ciphertext, recipients, sender_key, recipient_key)
7173
if alg in COSE_ALGORITHMS_HPKE.values():
72-
return HPKE(p, u, ciphertext, recipients) # TODO sender_key
74+
return HPKE(p, u, ciphertext, recipients, recipient_key) # TODO sender_key
7375
raise ValueError(f"Unsupported or unknown alg(1): {alg}.")
7476

7577
@classmethod

0 commit comments

Comments
 (0)