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

Commit

Permalink
First version
Browse files Browse the repository at this point in the history
TODO: Figure out Python packing mess. Might use Bento.
  • Loading branch information
davedoesdev committed Sep 30, 2013
1 parent 24c8004 commit af596e0
Show file tree
Hide file tree
Showing 68 changed files with 6,527 additions and 10 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
@@ -0,0 +1,2 @@
[run]
branch = True
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
*.pyc
node_modules
19 changes: 19 additions & 0 deletions LICENCE
@@ -0,0 +1,19 @@
Copyright (c) 2013 David Halls <https://github.com/davedoesdev/>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
34 changes: 34 additions & 0 deletions Makefile
@@ -0,0 +1,34 @@

export PYTHONPATH=.
PYVOWS = ./test/pyvows.sh

all: lint test

docs: build_docs

build_docs:
cd docs && make html

lint:
pylint jwt test bench

test: run_test

run_test:
$(PYVOWS) test

coverage: run_coverage

run_coverage:
$(PYVOWS) --cover --cover-package jwt --cover-report coverage/coverage.xml test

bench: run_bench

run_bench:
for b in ./bench/*_bench.py; do $$b; done

bench_gfm:
for b in ./bench/*_bench.py; do $$b --gfm; done

dev_deps:
mkdir -p node_modules && npm install jsjws sinon
110 changes: 110 additions & 0 deletions README.md
@@ -0,0 +1,110 @@
# 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)

Module for generating and verifying JSON Web Tokens.

- Uses [jws](https://github.com/brianloveswords/python-jws) to do the heavy lifting.
- 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.
- Unit tests, including tests for interoperability with [node-jsjws](https://github.com/davedoesdev/node-jsjws).

Example:

```python
import jwt, Crypto.PublicKey.RSA as RSA, datetime
key = RSA.generate(2048)
payload = { 'foo': 'bar', 'wup': 90 };
token = jwt.generate_jwt(payload, key, 'PS256', datetime.timedelta(minutes=5))
header, claims = jwt.verify_jwt(token, key)
for k in payload: assert claims[k] == payload[k]
```

The API is described [here](docs/_build/html/index.html).

## Installation

```shell
pip install jwt
```

## Another Example

You can read and write keys from and to [PEM-format](http://www.openssl.org/docs/crypto/pem.html) strings:

```python
import jwt, Crypto.PublicKey.RSA as RSA, datetime
key = RSA.generate(2048)
priv_pem = key.exportKey()
pub_pem = key.publickey().exportKey()
payload = { 'foo': 'bar', 'wup': 90 };
priv_key = RSA.importKey(priv_pem)
pub_key = RSA.importKey(pub_pem)
token = jwt.generate_jwt(payload, priv_key, 'RS256', datetime.timedelta(minutes=5))
header, claims = jwt.verify_jwt(token, pub_key)
for k in payload: assert claims[k] == payload[k]
```

## Licence

[MIT](LICENCE)

## Tests

```shell
make test
```

## Lint

```shell
make lint
```

## Code Coverage

```shell
make coverage
```

[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).

Coveralls page is [here](https://coveralls.io/r/davedoesdev/python-jwt).

## Benchmarks

```shell
make bench
```

Here are some results on a laptop with an Intel Core i5-3210M 2.5Ghz CPU and 6Gb RAM running Ubuntu 13.04.

Generate Key|user (ns)|sys (ns)|real (ns)
:--|--:|--:|--:
RSA|152,700,000|300,000|152,906,095

Generate Token|user (ns)|sys (ns)|real (ns)
:--|--:|--:|--:
HS256|140,000|10,000|157,202
HS384|160,000|10,000|156,403
HS512|139,999|20,000|153,212
PS256|3,159,999|49,999|3,218,649
PS384|3,170,000|10,000|3,176,899
PS512|3,120,000|9,999|3,141,219
RS256|3,070,000|20,000|3,094,644
RS384|3,090,000|0|3,092,471
RS512|3,079,999|20,000|3,095,314

Load Key|user (ns)|sys (ns)|real (ns)
:--|--:|--:|--:
RSA|811,000|0|810,139

Verify Token|user (ns)|sys (ns)|real (ns)
:--|--:|--:|--:
HS256|140,000|0|129,947
HS384|130,000|0|130,161
HS512|119,999|0|128,850
PS256|780,000|10,000|775,609
PS384|759,999|0|752,933
PS512|739,999|0|738,118
RS256|700,000|0|719,365
RS384|719,999|0|721,524
RS512|730,000|0|719,706

1 change: 1 addition & 0 deletions bench/__init__.py
@@ -0,0 +1 @@
""" Keep pylint happy """
28 changes: 28 additions & 0 deletions bench/generate_key_bench.py
@@ -0,0 +1,28 @@
#!/usr/bin/env python

""" Benchmark generating an RSA key """

import Crypto.PublicKey.RSA as RSA
from unitbench import Benchmark
from bench.reporter import Reporter

class GenerateKeyBenchmark(Benchmark):
""" Generate key benchmark """

def input(self):
""" Name of benchmark """
return ["Generate Key"]

def repeats(self):
""" Iterations """
return 100

def bench_RSA(self):
""" Generate key """
RSA.generate(2048)

if __name__ == "__main__":
#pylint: disable=W0402
import string
string.capwords = lambda x: x
GenerateKeyBenchmark().run(reporter=Reporter())
41 changes: 41 additions & 0 deletions bench/generate_token_bench.py
@@ -0,0 +1,41 @@
#!/usr/bin/env python

""" Benchmark generating a JWT """

from datetime import timedelta
from unitbench import Benchmark
from test.fixtures import payload, priv_keys, priv_key, algs
from bench.reporter import Reporter
import jwt

class GenerateTokenBenchmark(Benchmark):
""" Generate JWT benchmark """

def input(self):
""" Nme of benchmark """
return ["Generate Token"]

def repeats(self):
""" Iterations """
return 1000

#pylint: disable=W0621
def make_bench_generate_token(alg):
""" Return function which will generate token for particular algorithm """
def f(_):
""" Generate token """
privk = priv_keys[alg].get('default', priv_key)
jwt.generate_jwt(payload, privk, alg, timedelta(seconds=5))
return f

for alg in algs:
name = 'bench_' + alg
f = make_bench_generate_token(alg)
f.__name__ = name
setattr(GenerateTokenBenchmark, name, f)

if __name__ == "__main__":
#pylint: disable=W0402
import string
string.capwords = lambda x: x
GenerateTokenBenchmark().run(reporter=Reporter())
29 changes: 29 additions & 0 deletions bench/load_key_bench.py
@@ -0,0 +1,29 @@
#!/usr/bin/env python

""" Benchmark loading an RSA key from a PEM string """

import Crypto.PublicKey.RSA as RSA
from unitbench import Benchmark
from test.fixtures import priv_pem
from bench.reporter import Reporter

class LoadKeyBenchmark(Benchmark):
""" Load key benchmark """

def input(self):
""" Name of benchmark """
return ["Load Key"]

def repeats(self):
""" Iterations """
return 10000

def bench_RSA(self):
""" Import key """
RSA.importKey(priv_pem)

if __name__ == "__main__":
#pylint: disable=W0402
import string
string.capwords = lambda x: x
LoadKeyBenchmark().run(reporter=Reporter())
38 changes: 38 additions & 0 deletions bench/reporter.py
@@ -0,0 +1,38 @@
""" Custom reporter to generate Github-flavoured markdown tables """

import sys
import unitbench
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--gfm', dest='gfm', action='store_true')
gfm = parser.parse_args().gfm

class Reporter(unitbench.Reporter):
""" Custom reporter """

def __init__(self, output_stream=sys.stdout):
self.stream = output_stream

def write_results(self, value, results):
if gfm:
self.stream.write("{0}|{1}|{2}|{3}\n".format(value, "user (ns)", "sys (ns)", "real (ns)"))
self.stream.write(":--|--:|--:|--:\n")
for r in results:
if (hasattr(r, "user_mean") and
hasattr(r, "system_mean") and hasattr(r, "wall_mean")):
self.stream.write("{0}|{1:,}|{2:,}|{3:,}\n".format(r.name,
long(r.user_mean * 10**9),
long(r.system_mean * 10**9),
long(r.wall_mean * 10**9)))
else:
self.stream.write("{0:<20}{1:>15}{2:>15}{3:>15}\n".format(value, "user (ns)", "sys (ns)", "real (ns)"))
self.stream.write("=" * 65 + "\n")
for r in results:
if (hasattr(r, "user_mean") and
hasattr(r, "system_mean") and hasattr(r, "wall_mean")):
self.stream.write("{0:<20} {1:>14,} {2:>14,} {3:>14,}\n".format(r.name,
long(r.user_mean * 10**9),
long(r.system_mean * 10**9),
long(r.wall_mean * 10**9)))
self.stream.write("\n")
43 changes: 43 additions & 0 deletions bench/verify_token_bench.py
@@ -0,0 +1,43 @@
#!/usr/bin/env python

""" Benchmark verifying a JWT """

from datetime import timedelta
from unitbench import Benchmark
from test.fixtures import payload, priv_keys, priv_key, pub_keys, pub_key, algs
from bench.reporter import Reporter
import jwt

class VerifyTokenBenchmark(Benchmark):
""" Verify JWT benchmark """

def input(self):
""" Name of benchmark """
return ["Verify Token"]

def repeats(self):
""" Iterations """
return 1000

#pylint: disable=W0621
def make_bench_verify_token(alg):
""" Return function which will generate token for particular algorith """
privk = priv_keys[alg].get('default', priv_key)
token = jwt.generate_jwt(payload, privk, alg, timedelta(days=1))
def f(_):
""" Verify token """
pubk = pub_keys[alg].get('default', pub_key)
jwt.verify_jwt(token, pubk)
return f

for alg in algs:
name = 'bench_' + alg
f = make_bench_verify_token(alg)
f.__name__ = name
setattr(VerifyTokenBenchmark, name, f)

if __name__ == "__main__":
#pylint: disable=W0402
import string
string.capwords = lambda x: x
VerifyTokenBenchmark().run(reporter=Reporter())

0 comments on commit af596e0

Please sign in to comment.