Skip to content

Commit 9bb121b

Browse files
authored
Merge branch 'main' into 142-add-extensibility-to-referenced-token-status_list-object
2 parents 4e46c0b + 2013da8 commit 9bb121b

File tree

5 files changed

+101
-47
lines changed

5 files changed

+101
-47
lines changed

draft-ietf-oauth-status-list.md

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ author:
2121
email: paul.bastian@posteo.de
2222
-
2323
fullname: Christian Bormann
24-
email: chris.bormann@gmx.de
24+
organization: Robert Bosch GmbH
25+
email: christiancarl.bormann@bosch.com
2526

2627
normative:
2728
RFC1950: RFC1950
@@ -38,6 +39,7 @@ normative:
3839
RFC9052: RFC9052
3940
RFC9110: RFC9110
4041
RFC9111: RFC9111
42+
RFC9596: RFC9596
4143
IANA.MediaTypes:
4244
author:
4345
org: "IANA"
@@ -58,7 +60,6 @@ normative:
5860
org: "IANA"
5961
title: "CBOR Web Token (CWT) Claims"
6062
target: "https://www.iana.org/assignments/cwt/cwt.xhtml"
61-
CWT.typ: I-D.ietf-cose-typ-header-parameter
6263

6364
informative:
6465
RFC6749: RFC6749
@@ -220,13 +221,13 @@ This section defines the structure for a CBOR-encoded Status List:
220221
* `bits`: REQUIRED. Unsigned int (Major Type 0) that contains the number of bits per Referenced Token in the Status List. The allowed values for `bits` are 1, 2, 4 and 8.
221222
* `lst`: REQUIRED. Byte string (Major Type 2) that contains the Status List as specified in [](#status-list-json).
222223

223-
The following example illustrates the CBOR representation of the Status List:
224+
The following example illustrates the CBOR representation of the Status List in Hex:
224225

225226
~~~~~~~~~~
226227
{::include ./examples/status_list_encoding_cbor}
227228
~~~~~~~~~~
228229

229-
The following is the CBOR diagnostic output of the example above:
230+
The following is the CBOR Annotated Hex output of the example above:
230231

231232
~~~~~~~~~~
232233
{::include ./examples/status_list_encoding_cbor_diag}
@@ -277,7 +278,7 @@ The Status List Token MUST be encoded as a "CBOR Web Token (CWT)" according to {
277278

278279
The following content applies to the CWT protected header:
279280

280-
* `16` TBD (type): REQUIRED. The type of the CWT MUST be `statuslist+cwt` as defined in {{CWT.typ}}.
281+
* `16` (type): REQUIRED. The type of the CWT MUST be `statuslist+cwt` as defined in {{RFC9596}}.
281282

282283
The following content applies to the CWT Claims Set:
283284

@@ -298,13 +299,13 @@ The following additional rules apply:
298299

299300
4. Application of additional restrictions and policy are at the discretion of the verifying party.
300301

301-
The following is a non-normative example for a Status List Token in CWT format (not including the type header yet):
302+
The following is a non-normative example for a Status List Token in CWT format in Hex:
302303

303304
~~~~~~~~~~
304305
{::include ./examples/status_list_cwt}
305306
~~~~~~~~~~
306307

307-
The following is the CBOR diagnostic output of the example above:
308+
The following is the CBOR Annotated Hex output of the example above:
308309

309310
~~~~~~~~~~
310311
{::include ./examples/status_list_cwt_diag}
@@ -364,31 +365,18 @@ The following content applies to the CWT Claims Set:
364365

365366
Application of additional restrictions and policy are at the discretion of the verifying party.
366367

367-
The following is a non-normative example for a decoded payload of a Referenced Token:
368+
The following is a non-normative example of a Referenced Token in CWT format in Hex:
368369

369-
~~~ ascii-art
370+
~~~~~~~~~~
371+
{::include ./examples/referenced_token_cwt}
372+
~~~~~~~~~~
373+
374+
The following is the CBOR Annotated Hex output of the example above:
375+
376+
~~~~~~~~~~
377+
{::include ./examples/referenced_token_cwt_diag}
378+
~~~~~~~~~~
370379

371-
18(
372-
[
373-
/ protected / << {
374-
/ alg / 1: -7 / ES256 /
375-
} >>,
376-
/ unprotected / {
377-
/ kid / 4: h'3132' / '13' /
378-
},
379-
/ payload / << {
380-
/ iss / 1: "https://example.com",
381-
/ status / 65535: {
382-
"status_list": {
383-
"idx": 0,
384-
"uri": "https://example.com/statuslists/1"
385-
}
386-
}
387-
} >>,
388-
/ signature / h'...'
389-
]
390-
)
391-
~~~
392380

393381
## Referenced Token in other COSE/CBOR Format {#referenced-token-cose}
394382

@@ -814,6 +802,7 @@ for their valuable contributions, discussions and feedback to this specification
814802
-03
815803

816804
* relax requirements for status_list claims to contain other parameters
805+
* change cwt referenced token example to hex and annotated hex
817806
* require TLS only for fetching Status List, not for Status List Token
818807
* remove the undefined phrase Status List endpoint
819808
* remove http caching in favor of the new ttl claim

src/main.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from status_list import StatusList
22
from status_token import StatusListToken
3-
from datetime import datetime, timedelta
3+
from referenced_token import CWT
4+
from datetime import datetime, timedelta, timezone
45
import os
56
import util
67

78
key = util.EXAMPLE_KEY
8-
iat = datetime.utcfromtimestamp(1686920170)
9+
iat = datetime.fromtimestamp(1686920170, timezone.utc)
910
exp = iat + timedelta(days=7000)
1011
ttl = timedelta(hours=12)
1112
folder = "./examples/"
@@ -53,25 +54,23 @@ def statusListEncoding1Bit():
5354
status_list = exampleStatusList1Bit()
5455
encoded = status_list.encodeAsJSON()
5556
text = "byte_array = [{}, {}] \nencoded:\n{}".format(
56-
hex(status_list.list[0]),
57-
hex(status_list.list[1]),
58-
util.printObject(encoded)
57+
hex(status_list.list[0]), hex(status_list.list[1]), util.printObject(encoded)
5958
)
6059
util.outputFile(folder + "status_list_encoding_json", text)
6160

61+
6262
def statusListEncoding1BitCBOR():
6363
status_list = exampleStatusList1Bit()
6464
encoded = status_list.encodeAsCBOR()
6565
hex_encoded = encoded.hex()
6666
text = "byte_array = [{}, {}] \nencoded:\n{}".format(
67-
hex(status_list.list[0]),
68-
hex(status_list.list[1]),
69-
util.printText(hex_encoded)
67+
hex(status_list.list[0]), hex(status_list.list[1]), util.printText(hex_encoded)
7068
)
7169
util.outputFile(folder + "status_list_encoding_cbor", text)
7270
diag = util.printCBORDiagnostics(encoded)
7371
util.outputFile(folder + "status_list_encoding_cbor_diag", diag)
7472

73+
7574
def statusListEncoding2Bit():
7675
status_list = exampleStatusList2Bit()
7776
encoded = status_list.encodeAsJSON()
@@ -83,6 +82,7 @@ def statusListEncoding2Bit():
8382
)
8483
util.outputFile(folder + "status_list_encoding2_json", text)
8584

85+
8686
def statusListEncoding2BitCBOR():
8787
status_list = exampleStatusList2Bit()
8888
encoded = status_list.encodeAsCBOR()
@@ -97,6 +97,7 @@ def statusListEncoding2BitCBOR():
9797
diag = util.printCBORDiagnostics(encoded)
9898
util.outputFile(folder + "status_list_encoding2_cbor_diag", diag)
9999

100+
100101
def statusListJWT():
101102
status_list = exampleStatusList1Bit()
102103
jwt = StatusListToken(
@@ -109,6 +110,7 @@ def statusListJWT():
109110
text = util.formatToken(status_jwt, key)
110111
util.outputFile(folder + "status_list_jwt", text)
111112

113+
112114
def statusListCWT():
113115
status_list = exampleStatusList1Bit()
114116
cwt = StatusListToken(
@@ -121,7 +123,27 @@ def statusListCWT():
121123
status_cwt = cwt.buildCWT(iat=iat, exp=exp, ttl=ttl)
122124
hex_encoded = status_cwt.hex()
123125
util.outputFile(folder + "status_list_cwt", util.printText(hex_encoded))
124-
util.outputFile(folder + "status_list_cwt_diag", util.printCBORDiagnostics(status_cwt))
126+
util.outputFile(
127+
folder + "status_list_cwt_diag", util.printCBORDiagnostics(status_cwt)
128+
)
129+
130+
131+
def referencedTokenCWT():
132+
encoded = CWT(
133+
iat=iat,
134+
exp=exp,
135+
sub="12345",
136+
iss="https://example.com",
137+
jwk=key,
138+
status_url="https://example.com/statuslists/1",
139+
status_idx=0,
140+
)
141+
hex_encoded = encoded.hex()
142+
util.outputFile(folder + "referenced_token_cwt", util.printText(hex_encoded))
143+
util.outputFile(
144+
folder + "referenced_token_cwt_diag", util.printCBORDiagnostics(encoded)
145+
)
146+
125147

126148
if __name__ == "__main__":
127149
if not os.path.exists(folder):
@@ -132,3 +154,4 @@ def statusListCWT():
132154
statusListEncoding1BitCBOR()
133155
statusListEncoding2BitCBOR()
134156
statusListCWT()
157+
referencedTokenCWT()

src/referenced_token.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from cbor2 import dumps
2+
from cwt import COSE, COSEHeaders, COSEKey, CWTClaims, COSEAlgs
3+
from datetime import datetime
4+
from jwcrypto import jwk
5+
6+
7+
def CWT(
8+
jwk: jwk.JWK,
9+
iat: datetime,
10+
sub: str,
11+
iss: str,
12+
status_url: str,
13+
status_idx: int,
14+
exp: datetime = None,
15+
):
16+
claims = {}
17+
claims[CWTClaims.SUB] = sub
18+
claims[CWTClaims.ISS] = iss
19+
claims[CWTClaims.IAT] = int(iat.timestamp())
20+
if exp is not None:
21+
claims[CWTClaims.EXP] = int(exp.timestamp())
22+
23+
claims[65535] = {
24+
"status_list": {
25+
"idx": status_idx,
26+
"uri": status_url,
27+
}
28+
}
29+
30+
protected_header = {}
31+
unprotected_header = {}
32+
33+
if jwk.key_id:
34+
unprotected_header[COSEHeaders.KID] = jwk.key_id.encode("utf-8")
35+
protected_header[COSEHeaders.ALG] = COSEAlgs.ES256
36+
37+
key = COSEKey.from_jwk(jwk)
38+
39+
sender = COSE.new()
40+
encoded = sender.encode(
41+
dumps(claims), key, protected=protected_header, unprotected=unprotected_header
42+
)
43+
44+
return encoded

src/status_list.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ def get(self, pos: int) -> int:
6262
rest = pos % self.divisor
6363
floored = pos // self.divisor
6464
shift = rest * self.bits
65-
return (
66-
self.list[floored] & (((1 << self.bits) - 1) << shift)
67-
) >> shift
65+
return (self.list[floored] & (((1 << self.bits) - 1) << shift)) >> shift
6866

6967
def __str__(self):
7068
val = ""

src/status_token.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def buildJWT(
8282
ttl: timedelta = None,
8383
optional_claims: Dict = None,
8484
optional_header: Dict = None,
85-
compact=True
85+
compact=True,
8686
) -> str:
8787
# build claims
8888
if optional_claims is not None:
@@ -111,15 +111,15 @@ def buildJWT(
111111
token = jwt.JWT(header=header, claims=claims)
112112
token.make_signed_token(self._key)
113113
return token.serialize(compact=compact)
114-
114+
115115
def buildCWT(
116116
self,
117117
iat: datetime = datetime.utcnow(),
118118
exp: datetime = None,
119119
ttl: timedelta = None,
120120
optional_claims: Dict = None,
121121
optional_protected_header: Dict = None,
122-
optional_unprotected_header: Dict = None
122+
optional_unprotected_header: Dict = None,
123123
) -> bytes:
124124
# build claims
125125
if optional_claims is not None:
@@ -147,7 +147,7 @@ def buildCWT(
147147
unprotected_header = {}
148148

149149
if self._key.key_id:
150-
unprotected_header[COSEHeaders.KID] = self._key.key_id.encode('utf-8')
150+
unprotected_header[COSEHeaders.KID] = self._key.key_id.encode("utf-8")
151151
protected_header[COSEHeaders.ALG] = self._alg
152152
protected_header[16] = STATUS_LIST_TYP_CWT
153153

@@ -159,7 +159,7 @@ def buildCWT(
159159
dumps(claims),
160160
key,
161161
protected=protected_header,
162-
unprotected=unprotected_header
162+
unprotected=unprotected_header,
163163
)
164164

165165
return encoded

0 commit comments

Comments
 (0)