Skip to content

Commit 473dcb9

Browse files
committed
Add recipient_key to Recipient.new().
1 parent e17c035 commit 473dcb9

14 files changed

Lines changed: 197 additions & 265 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/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/recipient.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def new(
4545
protected (dict): Parameters that are to be cryptographically protected.
4646
unprotected (dict): Parameters that are not cryptographically protected.
4747
ciphertext (List[Any]): A cipher text.
48-
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.
4950
Returns:
5051
RecipientInterface: A recipient object.
5152
Raises:
@@ -64,13 +65,13 @@ def new(
6465
if alg in [-3, -4, -5]:
6566
if not sender_key:
6667
sender_key = COSEKey.from_symmetric_key(alg=alg)
67-
return AESKeyWrap(p, u, sender_key, ciphertext, recipients)
68+
return AESKeyWrap(p, u, ciphertext, recipients, sender_key)
6869
if alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_DIRECT.values():
69-
return ECDH_DirectHKDF(p, u, ciphertext, recipients, sender_key)
70+
return ECDH_DirectHKDF(p, u, ciphertext, recipients, sender_key, recipient_key)
7071
if alg in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT_WITH_KEY_WRAP.values():
71-
return ECDH_AESKeyWrap(p, u, ciphertext, recipients, sender_key)
72+
return ECDH_AESKeyWrap(p, u, ciphertext, recipients, sender_key, recipient_key)
7273
if alg in COSE_ALGORITHMS_HPKE.values():
73-
return HPKE(p, u, ciphertext, recipients) # TODO sender_key
74+
return HPKE(p, u, ciphertext, recipients, recipient_key) # TODO sender_key
7475
raise ValueError(f"Unsupported or unknown alg(1): {alg}.")
7576

7677
@classmethod

cwt/recipient_algs/aes_key_wrap.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ def __init__(
1717
self,
1818
protected: Dict[int, Any],
1919
unprotected: Dict[int, Any],
20-
sender_key: COSEKeyInterface,
2120
ciphertext: bytes = b"",
2221
recipients: List[Any] = [],
22+
sender_key: Optional[COSEKeyInterface] = None,
2323
):
24+
if sender_key is None:
25+
raise ValueError("sender_key should be set.")
2426
if sender_key.alg not in [-3, -4, -5]:
2527
raise ValueError(f"Invalid alg in sender_key: {sender_key.alg}.")
2628
if 1 in protected and protected[1] != sender_key.alg:
@@ -33,12 +35,11 @@ def __init__(
3335
sender_key.key_ops,
3436
sender_key.key,
3537
)
36-
self._sender_key = sender_key
38+
self._sender_key: COSEKeyInterface = sender_key
3739

3840
def encode(
3941
self,
4042
plaintext: bytes = b"",
41-
recipient_key: Optional[COSEKeyInterface] = None,
4243
salt: Optional[bytes] = None,
4344
context: Optional[Union[List[Any], Dict[str, Any]]] = None,
4445
external_aad: bytes = b"",

cwt/recipient_algs/direct_hkdf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ def verify_key(
8585
def encode(
8686
self,
8787
plaintext: bytes = b"",
88-
recipient_key: Optional[COSEKeyInterface] = None,
8988
salt: Optional[bytes] = None,
9089
context: Optional[Union[List[Any], Dict[str, Any]]] = None,
9190
external_aad: bytes = b"",

0 commit comments

Comments
 (0)