Skip to content
This repository was archived by the owner on Nov 13, 2023. It is now read-only.

Commit af596e0

Browse files
committed
First version
TODO: Figure out Python packing mess. Might use Bento.
1 parent 24c8004 commit af596e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+6527
-10
lines changed

.coveragerc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[run]
2+
branch = True

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
*.pyc
2+
node_modules

LICENCE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2013 David Halls <https://github.com/davedoesdev/>
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

Makefile

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
export PYTHONPATH=.
3+
PYVOWS = ./test/pyvows.sh
4+
5+
all: lint test
6+
7+
docs: build_docs
8+
9+
build_docs:
10+
cd docs && make html
11+
12+
lint:
13+
pylint jwt test bench
14+
15+
test: run_test
16+
17+
run_test:
18+
$(PYVOWS) test
19+
20+
coverage: run_coverage
21+
22+
run_coverage:
23+
$(PYVOWS) --cover --cover-package jwt --cover-report coverage/coverage.xml test
24+
25+
bench: run_bench
26+
27+
run_bench:
28+
for b in ./bench/*_bench.py; do $$b; done
29+
30+
bench_gfm:
31+
for b in ./bench/*_bench.py; do $$b --gfm; done
32+
33+
dev_deps:
34+
mkdir -p node_modules && npm install jsjws sinon

README.md

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# python-jwt&nbsp;&nbsp;&nbsp;[![Build Status](https://travis-ci.org/davedoesdev/python-jwt.png)](https://travis-ci.org/davedoesdev/python-jwt) [![Coverage Status](https://coveralls.io/repos/davedoesdev/python-jwt/badge.png?branch=master)](https://coveralls.io/r/davedoesdev/python-jwt?branch=master) [![PyPI version](https://badge.fury.io/py/jwt.png)](http://badge.fury.io/py/jwt)
2+
3+
Module for generating and verifying JSON Web Tokens.
4+
5+
- Uses [jws](https://github.com/brianloveswords/python-jws) to do the heavy lifting.
6+
- Supports [__RS256__, __RS384__, __RS512__](http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-14#section-3.3), [__PS256__, __PS384__, __PS512__](http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-14#section-3.5), [__HS256__, __HS384__ and __HS512__](http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-14#section-3.2) signature algorithms.
7+
- Unit tests, including tests for interoperability with [node-jsjws](https://github.com/davedoesdev/node-jsjws).
8+
9+
Example:
10+
11+
```python
12+
import jwt, Crypto.PublicKey.RSA as RSA, datetime
13+
key = RSA.generate(2048)
14+
payload = { 'foo': 'bar', 'wup': 90 };
15+
token = jwt.generate_jwt(payload, key, 'PS256', datetime.timedelta(minutes=5))
16+
header, claims = jwt.verify_jwt(token, key)
17+
for k in payload: assert claims[k] == payload[k]
18+
```
19+
20+
The API is described [here](docs/_build/html/index.html).
21+
22+
## Installation
23+
24+
```shell
25+
pip install jwt
26+
```
27+
28+
## Another Example
29+
30+
You can read and write keys from and to [PEM-format](http://www.openssl.org/docs/crypto/pem.html) strings:
31+
32+
```python
33+
import jwt, Crypto.PublicKey.RSA as RSA, datetime
34+
key = RSA.generate(2048)
35+
priv_pem = key.exportKey()
36+
pub_pem = key.publickey().exportKey()
37+
payload = { 'foo': 'bar', 'wup': 90 };
38+
priv_key = RSA.importKey(priv_pem)
39+
pub_key = RSA.importKey(pub_pem)
40+
token = jwt.generate_jwt(payload, priv_key, 'RS256', datetime.timedelta(minutes=5))
41+
header, claims = jwt.verify_jwt(token, pub_key)
42+
for k in payload: assert claims[k] == payload[k]
43+
```
44+
45+
## Licence
46+
47+
[MIT](LICENCE)
48+
49+
## Tests
50+
51+
```shell
52+
make test
53+
```
54+
55+
## Lint
56+
57+
```shell
58+
make lint
59+
```
60+
61+
## Code Coverage
62+
63+
```shell
64+
make coverage
65+
```
66+
67+
[coverage.py](http://nedbatchelder.com/code/coverage/) results are available [here](http://htmlpreview.github.io/?https://github.com/davedoesdev/python-jwt/blob/master/coverage/html/index.html).
68+
69+
Coveralls page is [here](https://coveralls.io/r/davedoesdev/python-jwt).
70+
71+
## Benchmarks
72+
73+
```shell
74+
make bench
75+
```
76+
77+
Here are some results on a laptop with an Intel Core i5-3210M 2.5Ghz CPU and 6Gb RAM running Ubuntu 13.04.
78+
79+
Generate Key|user (ns)|sys (ns)|real (ns)
80+
:--|--:|--:|--:
81+
RSA|152,700,000|300,000|152,906,095
82+
83+
Generate Token|user (ns)|sys (ns)|real (ns)
84+
:--|--:|--:|--:
85+
HS256|140,000|10,000|157,202
86+
HS384|160,000|10,000|156,403
87+
HS512|139,999|20,000|153,212
88+
PS256|3,159,999|49,999|3,218,649
89+
PS384|3,170,000|10,000|3,176,899
90+
PS512|3,120,000|9,999|3,141,219
91+
RS256|3,070,000|20,000|3,094,644
92+
RS384|3,090,000|0|3,092,471
93+
RS512|3,079,999|20,000|3,095,314
94+
95+
Load Key|user (ns)|sys (ns)|real (ns)
96+
:--|--:|--:|--:
97+
RSA|811,000|0|810,139
98+
99+
Verify Token|user (ns)|sys (ns)|real (ns)
100+
:--|--:|--:|--:
101+
HS256|140,000|0|129,947
102+
HS384|130,000|0|130,161
103+
HS512|119,999|0|128,850
104+
PS256|780,000|10,000|775,609
105+
PS384|759,999|0|752,933
106+
PS512|739,999|0|738,118
107+
RS256|700,000|0|719,365
108+
RS384|719,999|0|721,524
109+
RS512|730,000|0|719,706
110+

bench/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
""" Keep pylint happy """

bench/generate_key_bench.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python
2+
3+
""" Benchmark generating an RSA key """
4+
5+
import Crypto.PublicKey.RSA as RSA
6+
from unitbench import Benchmark
7+
from bench.reporter import Reporter
8+
9+
class GenerateKeyBenchmark(Benchmark):
10+
""" Generate key benchmark """
11+
12+
def input(self):
13+
""" Name of benchmark """
14+
return ["Generate Key"]
15+
16+
def repeats(self):
17+
""" Iterations """
18+
return 100
19+
20+
def bench_RSA(self):
21+
""" Generate key """
22+
RSA.generate(2048)
23+
24+
if __name__ == "__main__":
25+
#pylint: disable=W0402
26+
import string
27+
string.capwords = lambda x: x
28+
GenerateKeyBenchmark().run(reporter=Reporter())

bench/generate_token_bench.py

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env python
2+
3+
""" Benchmark generating a JWT """
4+
5+
from datetime import timedelta
6+
from unitbench import Benchmark
7+
from test.fixtures import payload, priv_keys, priv_key, algs
8+
from bench.reporter import Reporter
9+
import jwt
10+
11+
class GenerateTokenBenchmark(Benchmark):
12+
""" Generate JWT benchmark """
13+
14+
def input(self):
15+
""" Nme of benchmark """
16+
return ["Generate Token"]
17+
18+
def repeats(self):
19+
""" Iterations """
20+
return 1000
21+
22+
#pylint: disable=W0621
23+
def make_bench_generate_token(alg):
24+
""" Return function which will generate token for particular algorithm """
25+
def f(_):
26+
""" Generate token """
27+
privk = priv_keys[alg].get('default', priv_key)
28+
jwt.generate_jwt(payload, privk, alg, timedelta(seconds=5))
29+
return f
30+
31+
for alg in algs:
32+
name = 'bench_' + alg
33+
f = make_bench_generate_token(alg)
34+
f.__name__ = name
35+
setattr(GenerateTokenBenchmark, name, f)
36+
37+
if __name__ == "__main__":
38+
#pylint: disable=W0402
39+
import string
40+
string.capwords = lambda x: x
41+
GenerateTokenBenchmark().run(reporter=Reporter())

bench/load_key_bench.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env python
2+
3+
""" Benchmark loading an RSA key from a PEM string """
4+
5+
import Crypto.PublicKey.RSA as RSA
6+
from unitbench import Benchmark
7+
from test.fixtures import priv_pem
8+
from bench.reporter import Reporter
9+
10+
class LoadKeyBenchmark(Benchmark):
11+
""" Load key benchmark """
12+
13+
def input(self):
14+
""" Name of benchmark """
15+
return ["Load Key"]
16+
17+
def repeats(self):
18+
""" Iterations """
19+
return 10000
20+
21+
def bench_RSA(self):
22+
""" Import key """
23+
RSA.importKey(priv_pem)
24+
25+
if __name__ == "__main__":
26+
#pylint: disable=W0402
27+
import string
28+
string.capwords = lambda x: x
29+
LoadKeyBenchmark().run(reporter=Reporter())

bench/reporter.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
""" Custom reporter to generate Github-flavoured markdown tables """
2+
3+
import sys
4+
import unitbench
5+
import argparse
6+
7+
parser = argparse.ArgumentParser()
8+
parser.add_argument('--gfm', dest='gfm', action='store_true')
9+
gfm = parser.parse_args().gfm
10+
11+
class Reporter(unitbench.Reporter):
12+
""" Custom reporter """
13+
14+
def __init__(self, output_stream=sys.stdout):
15+
self.stream = output_stream
16+
17+
def write_results(self, value, results):
18+
if gfm:
19+
self.stream.write("{0}|{1}|{2}|{3}\n".format(value, "user (ns)", "sys (ns)", "real (ns)"))
20+
self.stream.write(":--|--:|--:|--:\n")
21+
for r in results:
22+
if (hasattr(r, "user_mean") and
23+
hasattr(r, "system_mean") and hasattr(r, "wall_mean")):
24+
self.stream.write("{0}|{1:,}|{2:,}|{3:,}\n".format(r.name,
25+
long(r.user_mean * 10**9),
26+
long(r.system_mean * 10**9),
27+
long(r.wall_mean * 10**9)))
28+
else:
29+
self.stream.write("{0:<20}{1:>15}{2:>15}{3:>15}\n".format(value, "user (ns)", "sys (ns)", "real (ns)"))
30+
self.stream.write("=" * 65 + "\n")
31+
for r in results:
32+
if (hasattr(r, "user_mean") and
33+
hasattr(r, "system_mean") and hasattr(r, "wall_mean")):
34+
self.stream.write("{0:<20} {1:>14,} {2:>14,} {3:>14,}\n".format(r.name,
35+
long(r.user_mean * 10**9),
36+
long(r.system_mean * 10**9),
37+
long(r.wall_mean * 10**9)))
38+
self.stream.write("\n")

bench/verify_token_bench.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env python
2+
3+
""" Benchmark verifying a JWT """
4+
5+
from datetime import timedelta
6+
from unitbench import Benchmark
7+
from test.fixtures import payload, priv_keys, priv_key, pub_keys, pub_key, algs
8+
from bench.reporter import Reporter
9+
import jwt
10+
11+
class VerifyTokenBenchmark(Benchmark):
12+
""" Verify JWT benchmark """
13+
14+
def input(self):
15+
""" Name of benchmark """
16+
return ["Verify Token"]
17+
18+
def repeats(self):
19+
""" Iterations """
20+
return 1000
21+
22+
#pylint: disable=W0621
23+
def make_bench_verify_token(alg):
24+
""" Return function which will generate token for particular algorith """
25+
privk = priv_keys[alg].get('default', priv_key)
26+
token = jwt.generate_jwt(payload, privk, alg, timedelta(days=1))
27+
def f(_):
28+
""" Verify token """
29+
pubk = pub_keys[alg].get('default', pub_key)
30+
jwt.verify_jwt(token, pubk)
31+
return f
32+
33+
for alg in algs:
34+
name = 'bench_' + alg
35+
f = make_bench_verify_token(alg)
36+
f.__name__ = name
37+
setattr(VerifyTokenBenchmark, name, f)
38+
39+
if __name__ == "__main__":
40+
#pylint: disable=W0402
41+
import string
42+
string.capwords = lambda x: x
43+
VerifyTokenBenchmark().run(reporter=Reporter())

0 commit comments

Comments
 (0)