Hello community,

here is the log from the commit of package python-PyJWT for openSUSE:Factory 
checked in at 2014-11-14 09:19:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-PyJWT (Old)
 and      /work/SRC/openSUSE:Factory/.python-PyJWT.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-PyJWT"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-PyJWT/python-PyJWT.changes        
2014-09-03 20:50:01.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-PyJWT.new/python-PyJWT.changes   
2014-11-14 09:19:19.000000000 +0100
@@ -1,0 +2,5 @@
+Thu Nov  6 09:08:37 UTC 2014 - [email protected]
+
+- Update to 0.3.0
+
+-------------------------------------------------------------------

Old:
----
  PyJWT-0.2.1.tar.gz

New:
----
  PyJWT-0.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-PyJWT.spec ++++++
--- /var/tmp/diff_new_pack.S4Qbam/_old  2014-11-14 09:19:19.000000000 +0100
+++ /var/tmp/diff_new_pack.S4Qbam/_new  2014-11-14 09:19:19.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           python-PyJWT
-Version:        0.2.1
+Version:        0.3.0
 Release:        0
 Url:            https://github.com/progrium/pyjwt
 Summary:        JSON Web Token implementation in Python
@@ -27,6 +27,7 @@
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  python-devel
 BuildRequires:  python-setuptools
+Requires:       python-ecdsa
 Requires:       python-pycrypto
 %if 0%{?suse_version} && 0%{?suse_version} <= 1110
 %{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}

++++++ PyJWT-0.2.1.tar.gz -> PyJWT-0.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/PKG-INFO new/PyJWT-0.3.0/PKG-INFO
--- old/PyJWT-0.2.1/PKG-INFO    2014-04-28 19:10:49.000000000 +0200
+++ new/PyJWT-0.3.0/PKG-INFO    2014-10-22 07:37:36.000000000 +0200
@@ -1,46 +1,64 @@
 Metadata-Version: 1.1
 Name: PyJWT
-Version: 0.2.1
+Version: 0.3.0
 Summary: JSON Web Token implementation in Python
 Home-page: http://github.com/progrium/pyjwt
 Author: Jeff Lindsay
 Author-email: [email protected]
 License: MIT
-Description: PyJWT [![Build 
Status](https://travis-ci.org/progrium/pyjwt.png?branch=master)](https://travis-ci.org/progrium/pyjwt)
-        =====
+Description: # PyJWT [![Build 
Status](https://travis-ci.org/progrium/pyjwt.png?branch=master)](https://travis-ci.org/progrium/pyjwt)
+        
         A Python implementation of [JSON Web Token draft 
01](http://self-issued.info/docs/draft-jones-json-web-token-01.html).
         
-        Installing
-        ----------
+        ## Installing
+        
+        ```
+        $ pip install PyJWT
+        ```
+        
+        **A Note on Dependencies**:
+        
+        The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
+        using any of those algorithms, you'll need to install it as well.
         
-            sudo easy_install PyJWT
+        ```
+        $ pip install PyCrypto
+        ```
         
-        **Note**: The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you 
plan on
-        using any of those algorithms you'll need to install it as well.
+        The Elliptic Curve Digital Signature algorithms depend on 
Python-ECDSA. If
+        you plan on using any of those algorithms, you'll need to install it 
as well.
         
-            sudo easy_install PyCrypto
+        ```
+        $ pip install ecdsa
+        ```
         
-        Usage
-        -----
+        ## Usage
         
-            import jwt
-            jwt.encode({"some": "payload"}, "secret")
+        ```python
+        import jwt
+        jwt.encode({'some': 'payload'}, 'secret')
+        ```
         
         Additional headers may also be specified.
         
-            jwt.encode({"some": "payload"}, "secret", headers={"kid": 
"230498151c214b788dd97f22b85410a5"})
+        ```python
+        jwt.encode({'some': 'payload'}, 'secret', headers={'kid': 
'230498151c214b788dd97f22b85410a5'})
+        ```
         
         Note the resulting JWT will not be encrypted, but verifiable with a 
secret key.
         
-            jwt.decode("someJWTstring", "secret")
+        ```python
+        jwt.decode('someJWTstring', 'secret')
+        ```
         
         If the secret is wrong, it will raise a `jwt.DecodeError` telling you 
as such.
         You can still get the payload by setting the `verify` argument to 
`False`.
         
-            jwt.decode("someJWTstring", verify=False)
+        ```python
+        jwt.decode('someJWTstring', verify=False)
+        ```
         
-        Algorithms
-        ----------
+        ## Algorithms
         
         The JWT spec supports several algorithms for cryptographic signing. 
This library
         currently supports:
@@ -48,35 +66,46 @@
         * HS256 - HMAC using SHA-256 hash algorithm (default)
         * HS384 - HMAC using SHA-384 hash algorithm
         * HS512 - HMAC using SHA-512 hash algorithm
+        * ES256 - ECDSA signature algorithm using SHA-256 hash algorithm
+        * ES384 - ECDSA signature algorithm using SHA-384 hash algorithm
+        * ES512 - ECDSA signature algorithm using SHA-512 hash algorithm
         * RS256 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash 
algorithm
         * RS384 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-384 hash 
algorithm
         * RS512 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-512 hash 
algorithm
         
         Change the algorithm with by setting it in encode:
         
-            jwt.encode({"some": "payload"}, "secret", "HS512")
+        ```python
+        jwt.encode({'some': 'payload'}, 'secret', 'HS512')
+        ```
         
         When using the RSASSA-PKCS1-v1_5 algorithms, the `key` argument in both
         `jwt.encode()` and `jwt.decode()` (`"secret"` in the examples) is 
expected to
-        be an RSA private key as imported with 
`Crypto.PublicKey.RSA.importKey()`.
+        be an RSA public or private key as imported with 
`Crypto.PublicKey.RSA.importKey()`.
         
-        Tests
-        -----
+        When using the ECDSA algorithms, the `key` argument is expected to
+        be an Elliptic Curve private key as imported with 
`ecdsa.SigningKey.from_pem()`,
+        or a public key as imported with `ecdsa.VerifyingKey.from_pem()`.
+        
+        ## Tests
         
         You can run tests from the project root after cloning with:
         
-            python tests/test_jwt.py
+        ```
+        $ python tests/test_jwt.py
+        ```
         
-        Support of reserved claim names
-        -------------------------------
+        ## Support of reserved claim names
         
         JSON Web Token defines some reserved claim names and defines how they 
should be
         used. PyJWT supports these reserved claim names:
         
          - "exp" (Expiration Time) Claim
+         - "nbf" (Not Before Time) Claim
+         - "iss" (Issuer) Claim
+         - "aud" (Audience) Claim
         
-        Expiration Time Claim
-        =====================
+        ### Expiration Time Claim
         
         From [draft 01 of the JWT 
spec](http://self-issued.info/docs/draft-jones-json-web-token-01.html#ReservedClaimName):
         
@@ -91,18 +120,23 @@
         You can pass the expiration time as a UTC UNIX timestamp (an int) or 
as a
         datetime, which will be converted into an int. For example:
         
-            jwt.encode({"exp": 1371720939}, "secret")
+        ```python
+        jwt.encode({'exp': 1371720939}, 'secret')
         
-            jwt.encode({"exp": datetime.utcnow()}, "secret")
+        jwt.encode({'exp': datetime.utcnow()}, 'secret')
+        ```
         
         Expiration time is automatically verified in `jwt.decode()` and raises
         `jwt.ExpiredSignature` if the expiration time is in the past:
         
-            import jwt
-            try:
-                jwt.decode('JWT_STRING', "secret")
-            except jwt.ExpiredSignature:
-                # Signature has expired
+        ```python
+        import jwt
+        
+        try:
+            jwt.decode('JWT_STRING', 'secret')
+        except jwt.ExpiredSignature:
+            # Signature has expired
+        ```
         
         Expiration time will be compared to the current UTC time (as given by
         `timegm(datetime.utcnow().utctimetuple())`), so be sure to use a UTC 
timestamp
@@ -116,16 +150,69 @@
         after creation but you know that sometimes you will process it after 
30 seconds,
         you can set a leeway of 10 seconds in order to have some margin:
         
-            import jwt, time
-            jwt_payload = jwt.encode({'exp': datetime.utcnow() + 
datetime.timedelta(seconds=30)}, 'secret')
-            time.sleep(32)
-            # Jwt payload is now expired
-            # But with some leeway, it will still validate
-            jwt.decode(jwt_payload, 'secret', leeway=10)
+        ```python
+        import datetime
+        import time
+        import jwt
+        
+        jwt_payload = jwt.encode({
+            'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)
+        }, 'secret')
+        
+        time.sleep(32)
+        
+        # JWT payload is now expired
+        # But with some leeway, it will still validate
+        jwt.decode(jwt_payload, 'secret', leeway=10)
+        ```
+        
+        ### Not Before Time Claim
+        
+        > The nbf (not before) claim identifies the time before which the JWT 
MUST NOT be accepted for processing. The processing of the nbf claim requires 
that the current date/time MUST be after or equal to the not-before date/time 
listed in the nbf claim. Implementers MAY provide for some small leeway, 
usually no more than a few minutes, to account for clock skew. Its value MUST 
be a number containing a NumericDate value. Use of this claim is OPTIONAL.
+        
+        The `nbf` claim works similarly to the `exp` claim above.
+        
+        ```python
+        jwt.encode({'nbf': 1371720939}, 'secret')
+        
+        jwt.encode({'nbf': datetime.utcnow()}, 'secret')
+        ```
+        
+        ### Issuer Claim
+        
+        > The iss (issuer) claim identifies the principal that issued the JWT. 
The processing of this claim is generally application specific. The iss value 
is a case-sensitive string containing a StringOrURI value. Use of this claim is 
OPTIONAL.
+        
+        ```python
+        import jwt
+        
+        
+        payload = {
+            'some': 'payload',
+            'iss': 'urn:foo'
+        }
+        
+        token = jwt.encode(payload, 'secret')
+        decoded = jwt.decode(token, 'secret', issuer='urn:foo')
+        ```
+        
+        ### Audience Claim
+        
+        > The aud (audience) claim identifies the recipients that the JWT is 
intended for. Each principal intended to process the JWT MUST identify itself 
with a value in the audience claim. If the principal processing the claim does 
not identify itself with a value in the aud claim when this claim is present, 
then the JWT MUST be rejected. In the general case, the aud value is an array 
of case-sensitive strings, each containing a StringOrURI value. In the special 
case when the JWT has one audience, the aud value MAY be a single 
case-sensitive string containing a StringOrURI value. The interpretation of 
audience values is generally application specific. Use of this claim is 
OPTIONAL.
+        
+        ```python
+        import jwt
+        
+        
+        payload = {
+            'some': 'payload',
+            'aud': 'urn:foo'
+        }
         
+        token = jwt.encode(payload, 'secret')
+        decoded = jwt.decode(token, 'secret', audience='urn:foo')
+        ```
         
-        License
-        -------
+        ## License
         
         MIT
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/PyJWT.egg-info/PKG-INFO 
new/PyJWT-0.3.0/PyJWT.egg-info/PKG-INFO
--- old/PyJWT-0.2.1/PyJWT.egg-info/PKG-INFO     2014-04-28 19:10:48.000000000 
+0200
+++ new/PyJWT-0.3.0/PyJWT.egg-info/PKG-INFO     2014-10-22 07:37:35.000000000 
+0200
@@ -1,46 +1,64 @@
 Metadata-Version: 1.1
 Name: PyJWT
-Version: 0.2.1
+Version: 0.3.0
 Summary: JSON Web Token implementation in Python
 Home-page: http://github.com/progrium/pyjwt
 Author: Jeff Lindsay
 Author-email: [email protected]
 License: MIT
-Description: PyJWT [![Build 
Status](https://travis-ci.org/progrium/pyjwt.png?branch=master)](https://travis-ci.org/progrium/pyjwt)
-        =====
+Description: # PyJWT [![Build 
Status](https://travis-ci.org/progrium/pyjwt.png?branch=master)](https://travis-ci.org/progrium/pyjwt)
+        
         A Python implementation of [JSON Web Token draft 
01](http://self-issued.info/docs/draft-jones-json-web-token-01.html).
         
-        Installing
-        ----------
+        ## Installing
+        
+        ```
+        $ pip install PyJWT
+        ```
+        
+        **A Note on Dependencies**:
+        
+        The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
+        using any of those algorithms, you'll need to install it as well.
         
-            sudo easy_install PyJWT
+        ```
+        $ pip install PyCrypto
+        ```
         
-        **Note**: The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you 
plan on
-        using any of those algorithms you'll need to install it as well.
+        The Elliptic Curve Digital Signature algorithms depend on 
Python-ECDSA. If
+        you plan on using any of those algorithms, you'll need to install it 
as well.
         
-            sudo easy_install PyCrypto
+        ```
+        $ pip install ecdsa
+        ```
         
-        Usage
-        -----
+        ## Usage
         
-            import jwt
-            jwt.encode({"some": "payload"}, "secret")
+        ```python
+        import jwt
+        jwt.encode({'some': 'payload'}, 'secret')
+        ```
         
         Additional headers may also be specified.
         
-            jwt.encode({"some": "payload"}, "secret", headers={"kid": 
"230498151c214b788dd97f22b85410a5"})
+        ```python
+        jwt.encode({'some': 'payload'}, 'secret', headers={'kid': 
'230498151c214b788dd97f22b85410a5'})
+        ```
         
         Note the resulting JWT will not be encrypted, but verifiable with a 
secret key.
         
-            jwt.decode("someJWTstring", "secret")
+        ```python
+        jwt.decode('someJWTstring', 'secret')
+        ```
         
         If the secret is wrong, it will raise a `jwt.DecodeError` telling you 
as such.
         You can still get the payload by setting the `verify` argument to 
`False`.
         
-            jwt.decode("someJWTstring", verify=False)
+        ```python
+        jwt.decode('someJWTstring', verify=False)
+        ```
         
-        Algorithms
-        ----------
+        ## Algorithms
         
         The JWT spec supports several algorithms for cryptographic signing. 
This library
         currently supports:
@@ -48,35 +66,46 @@
         * HS256 - HMAC using SHA-256 hash algorithm (default)
         * HS384 - HMAC using SHA-384 hash algorithm
         * HS512 - HMAC using SHA-512 hash algorithm
+        * ES256 - ECDSA signature algorithm using SHA-256 hash algorithm
+        * ES384 - ECDSA signature algorithm using SHA-384 hash algorithm
+        * ES512 - ECDSA signature algorithm using SHA-512 hash algorithm
         * RS256 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash 
algorithm
         * RS384 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-384 hash 
algorithm
         * RS512 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-512 hash 
algorithm
         
         Change the algorithm with by setting it in encode:
         
-            jwt.encode({"some": "payload"}, "secret", "HS512")
+        ```python
+        jwt.encode({'some': 'payload'}, 'secret', 'HS512')
+        ```
         
         When using the RSASSA-PKCS1-v1_5 algorithms, the `key` argument in both
         `jwt.encode()` and `jwt.decode()` (`"secret"` in the examples) is 
expected to
-        be an RSA private key as imported with 
`Crypto.PublicKey.RSA.importKey()`.
+        be an RSA public or private key as imported with 
`Crypto.PublicKey.RSA.importKey()`.
         
-        Tests
-        -----
+        When using the ECDSA algorithms, the `key` argument is expected to
+        be an Elliptic Curve private key as imported with 
`ecdsa.SigningKey.from_pem()`,
+        or a public key as imported with `ecdsa.VerifyingKey.from_pem()`.
+        
+        ## Tests
         
         You can run tests from the project root after cloning with:
         
-            python tests/test_jwt.py
+        ```
+        $ python tests/test_jwt.py
+        ```
         
-        Support of reserved claim names
-        -------------------------------
+        ## Support of reserved claim names
         
         JSON Web Token defines some reserved claim names and defines how they 
should be
         used. PyJWT supports these reserved claim names:
         
          - "exp" (Expiration Time) Claim
+         - "nbf" (Not Before Time) Claim
+         - "iss" (Issuer) Claim
+         - "aud" (Audience) Claim
         
-        Expiration Time Claim
-        =====================
+        ### Expiration Time Claim
         
         From [draft 01 of the JWT 
spec](http://self-issued.info/docs/draft-jones-json-web-token-01.html#ReservedClaimName):
         
@@ -91,18 +120,23 @@
         You can pass the expiration time as a UTC UNIX timestamp (an int) or 
as a
         datetime, which will be converted into an int. For example:
         
-            jwt.encode({"exp": 1371720939}, "secret")
+        ```python
+        jwt.encode({'exp': 1371720939}, 'secret')
         
-            jwt.encode({"exp": datetime.utcnow()}, "secret")
+        jwt.encode({'exp': datetime.utcnow()}, 'secret')
+        ```
         
         Expiration time is automatically verified in `jwt.decode()` and raises
         `jwt.ExpiredSignature` if the expiration time is in the past:
         
-            import jwt
-            try:
-                jwt.decode('JWT_STRING', "secret")
-            except jwt.ExpiredSignature:
-                # Signature has expired
+        ```python
+        import jwt
+        
+        try:
+            jwt.decode('JWT_STRING', 'secret')
+        except jwt.ExpiredSignature:
+            # Signature has expired
+        ```
         
         Expiration time will be compared to the current UTC time (as given by
         `timegm(datetime.utcnow().utctimetuple())`), so be sure to use a UTC 
timestamp
@@ -116,16 +150,69 @@
         after creation but you know that sometimes you will process it after 
30 seconds,
         you can set a leeway of 10 seconds in order to have some margin:
         
-            import jwt, time
-            jwt_payload = jwt.encode({'exp': datetime.utcnow() + 
datetime.timedelta(seconds=30)}, 'secret')
-            time.sleep(32)
-            # Jwt payload is now expired
-            # But with some leeway, it will still validate
-            jwt.decode(jwt_payload, 'secret', leeway=10)
+        ```python
+        import datetime
+        import time
+        import jwt
+        
+        jwt_payload = jwt.encode({
+            'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)
+        }, 'secret')
+        
+        time.sleep(32)
+        
+        # JWT payload is now expired
+        # But with some leeway, it will still validate
+        jwt.decode(jwt_payload, 'secret', leeway=10)
+        ```
+        
+        ### Not Before Time Claim
+        
+        > The nbf (not before) claim identifies the time before which the JWT 
MUST NOT be accepted for processing. The processing of the nbf claim requires 
that the current date/time MUST be after or equal to the not-before date/time 
listed in the nbf claim. Implementers MAY provide for some small leeway, 
usually no more than a few minutes, to account for clock skew. Its value MUST 
be a number containing a NumericDate value. Use of this claim is OPTIONAL.
+        
+        The `nbf` claim works similarly to the `exp` claim above.
+        
+        ```python
+        jwt.encode({'nbf': 1371720939}, 'secret')
+        
+        jwt.encode({'nbf': datetime.utcnow()}, 'secret')
+        ```
+        
+        ### Issuer Claim
+        
+        > The iss (issuer) claim identifies the principal that issued the JWT. 
The processing of this claim is generally application specific. The iss value 
is a case-sensitive string containing a StringOrURI value. Use of this claim is 
OPTIONAL.
+        
+        ```python
+        import jwt
+        
+        
+        payload = {
+            'some': 'payload',
+            'iss': 'urn:foo'
+        }
+        
+        token = jwt.encode(payload, 'secret')
+        decoded = jwt.decode(token, 'secret', issuer='urn:foo')
+        ```
+        
+        ### Audience Claim
+        
+        > The aud (audience) claim identifies the recipients that the JWT is 
intended for. Each principal intended to process the JWT MUST identify itself 
with a value in the audience claim. If the principal processing the claim does 
not identify itself with a value in the aud claim when this claim is present, 
then the JWT MUST be rejected. In the general case, the aud value is an array 
of case-sensitive strings, each containing a StringOrURI value. In the special 
case when the JWT has one audience, the aud value MAY be a single 
case-sensitive string containing a StringOrURI value. The interpretation of 
audience values is generally application specific. Use of this claim is 
OPTIONAL.
+        
+        ```python
+        import jwt
+        
+        
+        payload = {
+            'some': 'payload',
+            'aud': 'urn:foo'
+        }
         
+        token = jwt.encode(payload, 'secret')
+        decoded = jwt.decode(token, 'secret', audience='urn:foo')
+        ```
         
-        License
-        -------
+        ## License
         
         MIT
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/PyJWT.egg-info/SOURCES (Joses-iMac's 
conflicted copy 2014-10-17).txt new/PyJWT-0.3.0/PyJWT.egg-info/SOURCES 
(Joses-iMac's conflicted copy 2014-10-17).txt
--- old/PyJWT-0.2.1/PyJWT.egg-info/SOURCES (Joses-iMac's conflicted copy 
2014-10-17).txt        1970-01-01 01:00:00.000000000 +0100
+++ new/PyJWT-0.3.0/PyJWT.egg-info/SOURCES (Joses-iMac's conflicted copy 
2014-10-17).txt        2014-10-15 01:07:45.000000000 +0200
@@ -0,0 +1,10 @@
+MANIFEST.in
+README.md
+setup.cfg
+setup.py
+PyJWT.egg-info/PKG-INFO
+PyJWT.egg-info/SOURCES.txt
+PyJWT.egg-info/dependency_links.txt
+PyJWT.egg-info/top_level.txt
+bin/jwt
+jwt/__init__.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/PyJWT.egg-info/SOURCES.txt 
new/PyJWT-0.3.0/PyJWT.egg-info/SOURCES.txt
--- old/PyJWT-0.2.1/PyJWT.egg-info/SOURCES.txt  2014-04-28 19:10:49.000000000 
+0200
+++ new/PyJWT-0.3.0/PyJWT.egg-info/SOURCES.txt  2014-10-22 07:37:36.000000000 
+0200
@@ -1,7 +1,9 @@
 MANIFEST.in
 README.md
+setup.cfg
 setup.py
 PyJWT.egg-info/PKG-INFO
+PyJWT.egg-info/SOURCES (Joses-iMac's conflicted copy 2014-10-17).txt
 PyJWT.egg-info/SOURCES.txt
 PyJWT.egg-info/dependency_links.txt
 PyJWT.egg-info/top_level.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/README.md new/PyJWT-0.3.0/README.md
--- old/PyJWT-0.2.1/README.md   2014-04-28 19:10:40.000000000 +0200
+++ new/PyJWT-0.3.0/README.md   2014-10-22 07:21:34.000000000 +0200
@@ -1,38 +1,56 @@
-PyJWT [![Build 
Status](https://travis-ci.org/progrium/pyjwt.png?branch=master)](https://travis-ci.org/progrium/pyjwt)
-=====
+# PyJWT [![Build 
Status](https://travis-ci.org/progrium/pyjwt.png?branch=master)](https://travis-ci.org/progrium/pyjwt)
+
 A Python implementation of [JSON Web Token draft 
01](http://self-issued.info/docs/draft-jones-json-web-token-01.html).
 
-Installing
-----------
+## Installing
+
+```
+$ pip install PyJWT
+```
+
+**A Note on Dependencies**:
+
+The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
+using any of those algorithms, you'll need to install it as well.
 
-    sudo easy_install PyJWT
+```
+$ pip install PyCrypto
+```
 
-**Note**: The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
-using any of those algorithms you'll need to install it as well.
+The Elliptic Curve Digital Signature algorithms depend on Python-ECDSA. If
+you plan on using any of those algorithms, you'll need to install it as well.
 
-    sudo easy_install PyCrypto
+```
+$ pip install ecdsa
+```
 
-Usage
------
+## Usage
 
-    import jwt
-    jwt.encode({"some": "payload"}, "secret")
+```python
+import jwt
+jwt.encode({'some': 'payload'}, 'secret')
+```
 
 Additional headers may also be specified.
 
-    jwt.encode({"some": "payload"}, "secret", headers={"kid": 
"230498151c214b788dd97f22b85410a5"})
+```python
+jwt.encode({'some': 'payload'}, 'secret', headers={'kid': 
'230498151c214b788dd97f22b85410a5'})
+```
 
 Note the resulting JWT will not be encrypted, but verifiable with a secret key.
 
-    jwt.decode("someJWTstring", "secret")
+```python
+jwt.decode('someJWTstring', 'secret')
+```
 
 If the secret is wrong, it will raise a `jwt.DecodeError` telling you as such.
 You can still get the payload by setting the `verify` argument to `False`.
 
-    jwt.decode("someJWTstring", verify=False)
+```python
+jwt.decode('someJWTstring', verify=False)
+```
 
-Algorithms
-----------
+## Algorithms
 
 The JWT spec supports several algorithms for cryptographic signing. This 
library
 currently supports:
@@ -40,35 +58,46 @@
 * HS256 - HMAC using SHA-256 hash algorithm (default)
 * HS384 - HMAC using SHA-384 hash algorithm
 * HS512 - HMAC using SHA-512 hash algorithm
+* ES256 - ECDSA signature algorithm using SHA-256 hash algorithm
+* ES384 - ECDSA signature algorithm using SHA-384 hash algorithm
+* ES512 - ECDSA signature algorithm using SHA-512 hash algorithm
 * RS256 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash algorithm
 * RS384 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-384 hash algorithm
 * RS512 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-512 hash algorithm
 
 Change the algorithm with by setting it in encode:
 
-    jwt.encode({"some": "payload"}, "secret", "HS512")
+```python
+jwt.encode({'some': 'payload'}, 'secret', 'HS512')
+```
 
 When using the RSASSA-PKCS1-v1_5 algorithms, the `key` argument in both
 `jwt.encode()` and `jwt.decode()` (`"secret"` in the examples) is expected to
-be an RSA private key as imported with `Crypto.PublicKey.RSA.importKey()`.
+be an RSA public or private key as imported with 
`Crypto.PublicKey.RSA.importKey()`.
 
-Tests
------
+When using the ECDSA algorithms, the `key` argument is expected to
+be an Elliptic Curve private key as imported with 
`ecdsa.SigningKey.from_pem()`,
+or a public key as imported with `ecdsa.VerifyingKey.from_pem()`.
+
+## Tests
 
 You can run tests from the project root after cloning with:
 
-    python tests/test_jwt.py
+```
+$ python tests/test_jwt.py
+```
 
-Support of reserved claim names
--------------------------------
+## Support of reserved claim names
 
 JSON Web Token defines some reserved claim names and defines how they should be
 used. PyJWT supports these reserved claim names:
 
  - "exp" (Expiration Time) Claim
+ - "nbf" (Not Before Time) Claim
+ - "iss" (Issuer) Claim
+ - "aud" (Audience) Claim
 
-Expiration Time Claim
-=====================
+### Expiration Time Claim
 
 From [draft 01 of the JWT 
spec](http://self-issued.info/docs/draft-jones-json-web-token-01.html#ReservedClaimName):
 
@@ -83,18 +112,23 @@
 You can pass the expiration time as a UTC UNIX timestamp (an int) or as a
 datetime, which will be converted into an int. For example:
 
-    jwt.encode({"exp": 1371720939}, "secret")
+```python
+jwt.encode({'exp': 1371720939}, 'secret')
 
-    jwt.encode({"exp": datetime.utcnow()}, "secret")
+jwt.encode({'exp': datetime.utcnow()}, 'secret')
+```
 
 Expiration time is automatically verified in `jwt.decode()` and raises
 `jwt.ExpiredSignature` if the expiration time is in the past:
 
-    import jwt
-    try:
-        jwt.decode('JWT_STRING', "secret")
-    except jwt.ExpiredSignature:
-        # Signature has expired
+```python
+import jwt
+
+try:
+    jwt.decode('JWT_STRING', 'secret')
+except jwt.ExpiredSignature:
+    # Signature has expired
+```
 
 Expiration time will be compared to the current UTC time (as given by
 `timegm(datetime.utcnow().utctimetuple())`), so be sure to use a UTC timestamp
@@ -108,15 +142,68 @@
 after creation but you know that sometimes you will process it after 30 
seconds,
 you can set a leeway of 10 seconds in order to have some margin:
 
-    import jwt, time
-    jwt_payload = jwt.encode({'exp': datetime.utcnow() + 
datetime.timedelta(seconds=30)}, 'secret')
-    time.sleep(32)
-    # Jwt payload is now expired
-    # But with some leeway, it will still validate
-    jwt.decode(jwt_payload, 'secret', leeway=10)
+```python
+import datetime
+import time
+import jwt
+
+jwt_payload = jwt.encode({
+    'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=30)
+}, 'secret')
+
+time.sleep(32)
+
+# JWT payload is now expired
+# But with some leeway, it will still validate
+jwt.decode(jwt_payload, 'secret', leeway=10)
+```
+
+### Not Before Time Claim
+
+> The nbf (not before) claim identifies the time before which the JWT MUST NOT 
be accepted for processing. The processing of the nbf claim requires that the 
current date/time MUST be after or equal to the not-before date/time listed in 
the nbf claim. Implementers MAY provide for some small leeway, usually no more 
than a few minutes, to account for clock skew. Its value MUST be a number 
containing a NumericDate value. Use of this claim is OPTIONAL.
+
+The `nbf` claim works similarly to the `exp` claim above.
+
+```python
+jwt.encode({'nbf': 1371720939}, 'secret')
+
+jwt.encode({'nbf': datetime.utcnow()}, 'secret')
+```
+
+### Issuer Claim
+
+> The iss (issuer) claim identifies the principal that issued the JWT. The 
processing of this claim is generally application specific. The iss value is a 
case-sensitive string containing a StringOrURI value. Use of this claim is 
OPTIONAL.
+
+```python
+import jwt
+
+
+payload = {
+    'some': 'payload',
+    'iss': 'urn:foo'
+}
+
+token = jwt.encode(payload, 'secret')
+decoded = jwt.decode(token, 'secret', issuer='urn:foo')
+```
+
+### Audience Claim
+
+> The aud (audience) claim identifies the recipients that the JWT is intended 
for. Each principal intended to process the JWT MUST identify itself with a 
value in the audience claim. If the principal processing the claim does not 
identify itself with a value in the aud claim when this claim is present, then 
the JWT MUST be rejected. In the general case, the aud value is an array of 
case-sensitive strings, each containing a StringOrURI value. In the special 
case when the JWT has one audience, the aud value MAY be a single 
case-sensitive string containing a StringOrURI value. The interpretation of 
audience values is generally application specific. Use of this claim is 
OPTIONAL.
+
+```python
+import jwt
+
+
+payload = {
+    'some': 'payload',
+    'aud': 'urn:foo'
+}
 
+token = jwt.encode(payload, 'secret')
+decoded = jwt.decode(token, 'secret', audience='urn:foo')
+```
 
-License
--------
+## License
 
 MIT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/jwt/__init__.py 
new/PyJWT-0.3.0/jwt/__init__.py
--- old/PyJWT-0.2.1/jwt/__init__.py     2014-04-28 19:10:40.000000000 +0200
+++ new/PyJWT-0.3.0/jwt/__init__.py     2014-10-22 07:37:04.000000000 +0200
@@ -19,14 +19,16 @@
 except ImportError:
     import simplejson as json
 
-__all__ = ['encode', 'decode', 'DecodeError']
-
 
 if sys.version_info >= (3, 0, 0):
     unicode = str
     basestring = str
 
 
+__version__ = '0.3.0'
+__all__ = ['encode', 'decode', 'DecodeError']
+
+
 class DecodeError(Exception):
     pass
 
@@ -35,7 +37,16 @@
     pass
 
 
+class InvalidAudience(Exception):
+    pass
+
+
+class InvalidIssuer(Exception):
+    pass
+
+
 signing_methods = {
+    'none': lambda msg, key: b'',
     'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
     'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
     'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
@@ -47,15 +58,18 @@
     'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
 }
 
+
 def prepare_HS_key(key):
-    if isinstance(key, basestring):
-        if isinstance(key, unicode):
-            key = key.encode('utf-8')
-    else:
-        raise TypeError("Expecting a string-formatted key.")
+    if not isinstance(key, basestring) and not isinstance(key, bytes):
+        raise TypeError('Expecting a string- or bytes-formatted key.')
+
+    if isinstance(key, unicode):
+        key = key.encode('utf-8')
+
     return key
 
 prepare_key_methods = {
+    'none': lambda key: None,
     'HS256': prepare_HS_key,
     'HS384': prepare_HS_key,
     'HS512': prepare_HS_key
@@ -81,14 +95,17 @@
     })
 
     def prepare_RS_key(key):
+        if isinstance(key, RSA._RSAobj):
+            return key
+
         if isinstance(key, basestring):
             if isinstance(key, unicode):
                 key = key.encode('utf-8')
+
             key = RSA.importKey(key)
-        elif isinstance(key, RSA._RSAobj):
-            pass
         else:
-            raise TypeError("Expecting a PEM- or RSA-formatted key.")
+            raise TypeError('Expecting a PEM- or RSA-formatted key.')
+
         return key
 
     prepare_key_methods.update({
@@ -100,6 +117,57 @@
 except ImportError:
     pass
 
+try:
+    import ecdsa
+    from Crypto.Hash import SHA256
+    from Crypto.Hash import SHA384
+    from Crypto.Hash import SHA512
+
+    signing_methods.update({
+        'ES256': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha256, 
sigencode=ecdsa.util.sigencode_der),
+        'ES384': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha384, 
sigencode=ecdsa.util.sigencode_der),
+        'ES512': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha512, 
sigencode=ecdsa.util.sigencode_der),
+    })
+
+    verify_methods.update({
+        'ES256': lambda msg, key, sig: key.verify(sig, msg, 
hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der),
+        'ES384': lambda msg, key, sig: key.verify(sig, msg, 
hashfunc=hashlib.sha384, sigdecode=ecdsa.util.sigdecode_der),
+        'ES512': lambda msg, key, sig: key.verify(sig, msg, 
hashfunc=hashlib.sha512, sigdecode=ecdsa.util.sigdecode_der),
+    })
+
+    def prepare_ES_key(key):
+        if isinstance(key, ecdsa.SigningKey) or \
+           isinstance(key, ecdsa.VerifyingKey):
+            return key
+
+        if isinstance(key, basestring):
+            if isinstance(key, unicode):
+                key = key.encode('utf-8')
+
+            # Attempt to load key. We don't know if it's
+            # a Signing Key or a Verifying Key, so we try
+            # the Verifying Key first.
+            try:
+                key = ecdsa.VerifyingKey.from_pem(key)
+            except ecdsa.der.UnexpectedDER:
+                try:
+                    key = ecdsa.SigningKey.from_pem(key)
+                except:
+                    raise
+        else:
+            raise TypeError('Expecting a PEM-formatted key.')
+
+        return key
+
+    prepare_key_methods.update({
+        'ES256': prepare_ES_key,
+        'ES384': prepare_ES_key,
+        'ES512': prepare_ES_key
+    })
+
+except ImportError:
+    pass
+
 
 def constant_time_compare(val1, val2):
     """
@@ -109,20 +177,26 @@
     """
     if len(val1) != len(val2):
         return False
+
     result = 0
-    if sys.version_info >= (3, 0, 0):  # bytes are numbers
+
+    if sys.version_info >= (3, 0, 0):
+        # Bytes are numbers
         for x, y in zip(val1, val2):
             result |= x ^ y
     else:
         for x, y in zip(val1, val2):
             result |= ord(x) ^ ord(y)
+
     return result == 0
 
 
 def base64url_decode(input):
     rem = len(input) % 4
+
     if rem > 0:
         input += b'=' * (4 - rem)
+
     return base64.urlsafe_b64decode(input)
 
 
@@ -136,28 +210,34 @@
         header_data = base64url_decode(header_segment).decode('utf-8')
         return json.loads(header_data)
     except (ValueError, TypeError):
-        raise DecodeError("Invalid header encoding")
+        raise DecodeError('Invalid header encoding')
 
 
 def encode(payload, key, algorithm='HS256', headers=None):
     segments = []
 
+    if algorithm is None:
+        algorithm = 'none'
+
     # Check that we get a mapping
     if not isinstance(payload, Mapping):
-        raise TypeError("Expecting a mapping object, as json web token only"
-                        "support json objects.")
+        raise TypeError('Expecting a mapping object, as json web token only'
+                        'support json objects.')
 
     # Header
-    header = {"typ": "JWT", "alg": algorithm}
+    header = {'typ': 'JWT', 'alg': algorithm}
     if headers:
         header.update(headers)
+
     json_header = json.dumps(header, separators=(',', ':')).encode('utf-8')
     segments.append(base64url_encode(json_header))
 
     # Payload
-    for time_claim in ['exp', 'iat', 'nbf']:    # convert datetime to a 
intDate value in known time-format claims
+    for time_claim in ['exp', 'iat', 'nbf']:
+        # Convert datetime to a intDate value in known time-format claims
         if isinstance(payload.get(time_claim), datetime):
             payload[time_claim] = timegm(payload[time_claim].utctimetuple())
+
     json_payload = json.dumps(payload, separators=(',', ':')).encode('utf-8')
     segments.append(base64url_encode(json_payload))
 
@@ -167,17 +247,22 @@
         key = prepare_key_methods[algorithm](key)
         signature = signing_methods[algorithm](signing_input, key)
     except KeyError:
-        raise NotImplementedError("Algorithm not supported")
+        raise NotImplementedError('Algorithm not supported')
+
     segments.append(base64url_encode(signature))
+
     return b'.'.join(segments)
 
 
-def decode(jwt, key='', verify=True, verify_expiration=True, leeway=0):
+def decode(jwt, key='', verify=True, **kwargs):
     payload, signing_input, header, signature = load(jwt)
 
     if verify:
+        verify_expiration = kwargs.pop('verify_expiration', True)
+        leeway = kwargs.pop('leeway', 0)
+
         verify_signature(payload, signing_input, header, signature, key,
-                verify_expiration, leeway)
+                         verify_expiration, leeway, **kwargs)
 
     return payload
 
@@ -189,49 +274,76 @@
         signing_input, crypto_segment = jwt.rsplit(b'.', 1)
         header_segment, payload_segment = signing_input.split(b'.', 1)
     except ValueError:
-        raise DecodeError("Not enough segments")
+        raise DecodeError('Not enough segments')
 
     try:
         header_data = base64url_decode(header_segment)
     except (TypeError, binascii.Error):
-        raise DecodeError("Invalid header padding")
+        raise DecodeError('Invalid header padding')
     try:
         header = json.loads(header_data.decode('utf-8'))
     except ValueError as e:
-        raise DecodeError("Invalid header string: %s" % e)
+        raise DecodeError('Invalid header string: %s' % e)
 
     try:
         payload_data = base64url_decode(payload_segment)
     except (TypeError, binascii.Error):
-        raise DecodeError("Invalid payload padding")
+        raise DecodeError('Invalid payload padding')
     try:
         payload = json.loads(payload_data.decode('utf-8'))
     except ValueError as e:
-        raise DecodeError("Invalid payload string: %s" % e)
+        raise DecodeError('Invalid payload string: %s' % e)
 
     try:
         signature = base64url_decode(crypto_segment)
     except (TypeError, binascii.Error):
-        raise DecodeError("Invalid crypto padding")
+        raise DecodeError('Invalid crypto padding')
 
     return (payload, signing_input, header, signature)
 
 
 def verify_signature(payload, signing_input, header, signature, key='',
-            verify_expiration=True, leeway=0):
+                     verify_expiration=True, leeway=0, **kwargs):
     try:
-        key = prepare_key_methods[header['alg']](key)
-        if header['alg'].startswith('HS'):
-            expected = verify_methods[header['alg']](signing_input, key)
+        algorithm = header['alg'].upper()
+        key = prepare_key_methods[algorithm](key)
+
+        if algorithm.startswith('HS'):
+            expected = verify_methods[algorithm](signing_input, key)
+
             if not constant_time_compare(signature, expected):
-                raise DecodeError("Signature verification failed")
+                raise DecodeError('Signature verification failed')
         else:
-            if not verify_methods[header['alg']](signing_input, key, 
signature):
-                raise DecodeError("Signature verification failed")
+            if not verify_methods[algorithm](signing_input, key, signature):
+                raise DecodeError('Signature verification failed')
     except KeyError:
-        raise DecodeError("Algorithm not supported")
+        raise DecodeError('Algorithm not supported')
+
+    if 'nbf' in payload and verify_expiration:
+        utc_timestamp = timegm(datetime.utcnow().utctimetuple())
+
+        if payload['nbf'] > (utc_timestamp + leeway):
+            raise ExpiredSignature('Signature not yet valid')
 
     if 'exp' in payload and verify_expiration:
         utc_timestamp = timegm(datetime.utcnow().utctimetuple())
+
         if payload['exp'] < (utc_timestamp - leeway):
-            raise ExpiredSignature("Signature has expired")
+            raise ExpiredSignature('Signature has expired')
+
+    audience = kwargs.get('audience')
+
+    if audience:
+        if isinstance(audience, list):
+            audiences = audience
+        else:
+            audiences = [audience]
+
+        if payload.get('aud') not in audiences:
+            raise InvalidAudience('Invalid audience')
+
+    issuer = kwargs.get('issuer')
+
+    if issuer:
+        if payload.get('iss') != issuer:
+            raise InvalidIssuer('Invalid issuer')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/setup.cfg new/PyJWT-0.3.0/setup.cfg
--- old/PyJWT-0.2.1/setup.cfg   2014-04-28 19:10:49.000000000 +0200
+++ new/PyJWT-0.3.0/setup.cfg   2014-10-22 07:37:36.000000000 +0200
@@ -1,3 +1,6 @@
+[wheel]
+universal = 1
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/PyJWT-0.2.1/setup.py new/PyJWT-0.3.0/setup.py
--- old/PyJWT-0.2.1/setup.py    2014-04-28 19:10:40.000000000 +0200
+++ new/PyJWT-0.3.0/setup.py    2014-10-16 06:02:37.000000000 +0200
@@ -1,32 +1,54 @@
 #!/usr/bin/env python
 import os
+import sys
+import re
 from setuptools import setup
 
 
+def get_version(package):
+    """
+    Return package version as listed in `__version__` in `init.py`.
+    """
+    init_py = open(os.path.join(package, '__init__.py')).read()
+    return re.search("__version__ = ['\"]([^'\"]+)['\"]", init_py).group(1)
+
+
+version = get_version('jwt')
+
 with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme:
     long_description = readme.read()
 
 
+if sys.argv[-1] == 'publish':
+    os.system("python setup.py sdist upload")
+    os.system("python setup.py bdist_wheel upload")
+    print("You probably want to also tag the version now:")
+    print("  git tag -a %s -m 'version %s'" % (version, version))
+    print("  git push --tags")
+    sys.exit()
+
+
 setup(
-    name="PyJWT",
-    version="0.2.1",
-    author="Jeff Lindsay",
-    author_email="[email protected]",
-    description="JSON Web Token implementation in Python",
-    license="MIT",
-    keywords="jwt json web token security signing",
-    url="http://github.com/progrium/pyjwt";,
+    name='PyJWT',
+    version=version,
+    author='Jeff Lindsay',
+    author_email='[email protected]',
+    description='JSON Web Token implementation in Python',
+    license='MIT',
+    keywords='jwt json web token security signing',
+    url='http://github.com/progrium/pyjwt',
     packages=['jwt'],
     scripts=['bin/jwt'],
     long_description=long_description,
     classifiers=[
-        "Development Status :: 3 - Alpha",
-        "License :: OSI Approved :: MIT License",
-        "Programming Language :: Python",
-        "Programming Language :: Python :: 2.6",
-        "Programming Language :: Python :: 2.7",
-        "Programming Language :: Python :: 3.2",
-        "Programming Language :: Python :: 3.3",
-        "Topic :: Utilities",
+        'Development Status :: 3 - Alpha',
+        'License :: OSI Approved :: MIT License',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: 3.3',
+        'Topic :: Utilities',
     ],
-    test_suite='tests.test_jwt')
+    test_suite='tests.test_jwt'
+)

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to