[Pkg-freeipa-devel] [Git][freeipa-team/python-jwcrypto][master] 13 commits: Post release bump to 0.8.dev1

Timo Aaltonen gitlab at salsa.debian.org
Tue Jan 5 09:51:23 GMT 2021



Timo Aaltonen pushed to branch master at FreeIPA packaging / python-jwcrypto


Commits:
495f926b by Simo Sorce at 2020-02-19T12:04:36-05:00
Post release bump to 0.8.dev1

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
39779e9c by Simo Sorce at 2020-02-19T17:57:39+00:00
Remove cap on sphinx version

Also use python3 for it by default

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
e2251d6e by inyong_lee at 2020-03-20T14:32:34+00:00
Add secp256k1 curve

- Add testcase for secp256k1

- Add allowed_algs support for JWS, JWT

- - - - -
f15312f8 by Swatantra Agrawal at 2020-04-13T10:35:36-04:00
Typo rectified
- - - - -
9e29ac7b by Simo Sorce at 2020-04-22T08:50:29-04:00
Fix okp key type import

Do not require a crv param, we have enough info on our own.
Fix Import of public key from PEM

Add test to check we can import public and private from PEM and that
signature and verification then works properly.

keys generated via:
$ openssl genpkey -algorithm ed25519 -outform PEM -out ed25519-privkey.pem
$ openssl pkey -pubout -in ed25519-privkey.pem -out ed25519-pubkey.pem

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
544cdf23 by Simo Sorce at 2020-04-23T08:38:02-04:00
Add method to export Keys ans Sets as dictionaries

This allows to avoid json_decoding again to manipulate the exported
data.

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
3e191d70 by Braden Pellett at 2020-07-24T12:55:48+00:00
Fix some documentation typos

- - - - -
3b7e8322 by Braden Pellett at 2020-07-24T12:56:32+00:00
Rename ambiguous variable

- - - - -
4e08b661 by Simo Sorce at 2020-08-17T15:48:18-04:00
Release 0.8

Signed-off-by: Simo Sorce <simo at redhat.com>

- - - - -
b500164b by Timo Aaltonen at 2021-01-05T11:44:30+02:00
Merge branch 'upstream'

- - - - -
f0fb53ec by Timo Aaltonen at 2021-01-05T11:45:02+02:00
bump the version

- - - - -
5b67f4f3 by Timo Aaltonen at 2021-01-05T11:45:28+02:00
bump policy

- - - - -
84bd2862 by Timo Aaltonen at 2021-01-05T11:49:40+02:00
releasing package python-jwcrypto version 0.8.0-1

- - - - -


15 changed files:

- README.md
- debian/changelog
- debian/control
- docs/source/conf.py
- docs/source/jwk.rst
- docs/source/jwt.rst
- jwcrypto/common.py
- jwcrypto/jwa.py
- jwcrypto/jwe.py
- jwcrypto/jwk.py
- jwcrypto/jws.py
- jwcrypto/jwt.py
- jwcrypto/tests.py
- setup.py
- tox.ini


Changes:

=====================================
README.md
=====================================
@@ -4,13 +4,13 @@ JWCrypto
 ========
 
 An implementation of the JOSE Working Group documents:
-RFC 7515 - JSON Web Signature (JWS)
-RFC 7516 - JSON Web Encryption (JWE)
-RFC 7517 - JSON Web Key (JWK)
-RFC 7518 - JSON Web Algorithms (JWA)
-RFC 7519 - JSON Web Token (JWT)
-RFC 7520 - Examples of Protecting Content Using JSON Object Signing and
-Encryption (JOSE)
+- RFC 7515 - JSON Web Signature (JWS)
+- RFC 7516 - JSON Web Encryption (JWE)
+- RFC 7517 - JSON Web Key (JWK)
+- RFC 7518 - JSON Web Algorithms (JWA)
+- RFC 7519 - JSON Web Token (JWT)
+- RFC 7520 - Examples of Protecting Content Using JSON Object Signing and
+  Encryption (JOSE)
 
 Documentation
 =============


=====================================
debian/changelog
=====================================
@@ -1,12 +1,13 @@
-python-jwcrypto (0.7.0-1) UNRELEASED; urgency=medium
+python-jwcrypto (0.8.0-1) unstable; urgency=medium
 
+  [ Debian Janitor ]
   * New upstream release.
   * Set debhelper-compat version in Build-Depends.
   * Set upstream metadata fields: Bug-Database, Repository, Repository-
     Browse.
-  * Update standards version to 4.4.1, no changes needed.
+  * Update standards version to 4.5.1, no changes needed.
 
- -- Debian Janitor <janitor at jelmer.uk>  Tue, 10 Dec 2019 23:06:55 +0000
+ -- Timo Aaltonen <tjaalton at debian.org>  Tue, 05 Jan 2021 11:49:19 +0200
 
 python-jwcrypto (0.6.0-2) unstable; urgency=medium
 


=====================================
debian/control
=====================================
@@ -10,7 +10,7 @@ Build-Depends:
  python3-cryptography,
  python3-nose,
  python3-setuptools,
-Standards-Version: 4.4.1
+Standards-Version: 4.5.1
 Homepage: https://github.com/latchset/jwcrypto
 Vcs-Git: https://salsa.debian.org/freeipa-team/python-jwcrypto.git
 Vcs-Browser: https://salsa.debian.org/freeipa-team/python-jwcrypto


=====================================
docs/source/conf.py
=====================================
@@ -53,9 +53,9 @@ copyright = u'2016-2018, JWCrypto Contributors'
 # built documents.
 #
 # The short X.Y version.
-version = '0.7'
+version = '0.8'
 # The full version, including alpha/beta/rc tags.
-release = '0.7'
+release = '0.8'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


=====================================
docs/source/jwk.rst
=====================================
@@ -3,7 +3,7 @@ JSON Web Key (JWK)
 
 The jwk Module implements the `JSON Web Key`_ standard.
 A JSON Web Key is represented by a JWK object, related utility classes and
-functions are availbale in this module too.
+functions are available in this module too.
 
 .. _JSON Web Key: http://tools.ietf.org/html/rfc7517
 
@@ -69,10 +69,10 @@ Export the key with::
     >>> key.export()
     '{"k":"X6TBlwY2so8EwKZ2TFXM7XHSgWBKQJhcspzYydp5Y-o","kty":"oct"}'
 
-Create a 2048bit RSA keypair::
+Create a 2048bit RSA key pair::
     >>> jwk.JWK.generate(kty='RSA', size=2048)
 
-Create a P-256 EC keypair and export the public key::
+Create a P-256 EC key pair and export the public key::
     >>> key = jwk.JWK.generate(kty='EC', crv='P-256')
     >>> key.export(private_key=False)
     '{"y":"VYlYwBfOTIICojCPfdUjnmkpN-g-lzZKxzjAoFmDRm8",


=====================================
docs/source/jwt.rst
=====================================
@@ -3,7 +3,7 @@ JSON Web Token (JWT)
 
 The jwt Module implements the `JSON Web Token`_ standard.
 A JSON Web Token is represented by a JWT object, related utility classes and
-functions are availbale in this module too.
+functions are available in this module too.
 
 .. _JSON Web Token: http://tools.ietf.org/html/rfc7519
 


=====================================
jwcrypto/common.py
=====================================
@@ -91,7 +91,7 @@ class InvalidJWEKeyType(JWException):
     """Invalid JWE Key Type.
 
     This exception is raised when the provided JWK Key does not match
-    the type required by the sepcified algorithm.
+    the type required by the specified algorithm.
     """
 
     def __init__(self, expected, obtained):
@@ -103,7 +103,7 @@ class InvalidJWEKeyLength(JWException):
     """Invalid JWE Key Length.
 
     This exception is raised when the provided JWK Key does not match
-    the length required by the sepcified algorithm.
+    the length required by the specified algorithm.
     """
 
     def __init__(self, expected, obtained):


=====================================
jwcrypto/jwa.py
=====================================
@@ -249,6 +249,18 @@ class _ES256(_RawEC, JWAAlgorithm):
         super(_ES256, self).__init__('P-256', hashes.SHA256())
 
 
+class _ES256K(_RawEC, JWAAlgorithm):
+
+    name = "ES256K"
+    description = "ECDSA using secp256k1 curve and SHA-256"
+    keysize = 256
+    algorithm_usage_location = 'alg'
+    algorithm_use = 'sig'
+
+    def __init__(self):
+        super(_ES256K, self).__init__('secp256k1', hashes.SHA256())
+
+
 class _ES384(_RawEC, JWAAlgorithm):
 
     name = "ES384"
@@ -1042,6 +1054,7 @@ class JWA(object):
         'RS384': _RS384,
         'RS512': _RS512,
         'ES256': _ES256,
+        'ES256K': _ES256K,
         'ES384': _ES384,
         'ES512': _ES512,
         'PS256': _PS256,


=====================================
jwcrypto/jwe.py
=====================================
@@ -284,13 +284,13 @@ class JWE(object):
                         "is set" % invalid)
             if 'protected' not in self.objects:
                 raise InvalidJWEOperation(
-                    "Can't use compat encoding without protected headers")
+                    "Can't use compact encoding without protected headers")
             else:
                 ph = json_decode(self.objects['protected'])
                 for required in 'alg', 'enc':
                     if required not in ph:
                         raise InvalidJWEOperation(
-                            "Can't use compat encoding, '%s' must be in the "
+                            "Can't use compact encoding, '%s' must be in the "
                             "protected header" % required)
             if 'recipients' in self.objects:
                 if len(self.objects['recipients']) != 1:
@@ -438,7 +438,7 @@ class JWE(object):
          If a key is provided a decryption step will be attempted after
          the object is successfully deserialized.
 
-        :raises InvalidJWEData: if the raw object is an invaid JWE token.
+        :raises InvalidJWEData: if the raw object is an invalid JWE token.
         :raises InvalidJWEOperation: if the decryption fails.
         """
 


=====================================
jwcrypto/jwk.py
=====================================
@@ -148,12 +148,14 @@ JWKParamsRegistry = {
     'x5t#S256': JWKParameter('X.509 Certificate SHA-256 Thumbprint',
                              True, None, None)
 }
-"""Regstry of valid key parameters"""
+"""Registry of valid key parameters"""
 
 # RFC 7518 - 7.6 , RFC 8037 - 5
+# secp256k1 - https://tools.ietf.org/html/draft-ietf-cose-webauthn-algorithms
 JWKEllipticCurveRegistry = {'P-256': 'P-256 curve',
                             'P-384': 'P-384 curve',
                             'P-521': 'P-521 curve',
+                            'secp256k1': 'SECG secp256k1 curve',
                             'Ed25519': 'Ed25519 signature algorithm key pairs',
                             'Ed448': 'Ed448 signature algorithm key pairs',
                             'X25519': 'X25519 function key pairs',
@@ -180,7 +182,8 @@ JWKOperationsRegistry = {'sign': 'Compute digital Signature or MAC',
 
 JWKpycaCurveMap = {'secp256r1': 'P-256',
                    'secp384r1': 'P-384',
-                   'secp521r1': 'P-521'}
+                   'secp521r1': 'P-521',
+                   'secp256k1': 'secp256k1'}
 
 
 class InvalidJWKType(JWException):
@@ -278,7 +281,7 @@ class JWK(object):
         always be provided and its value must be a valid one as defined
         by the 'IANA JSON Web Key Types registry' and specified in the
         :data:`JWKTypesRegistry` variable. The valid key parameters per
-        key type are defined in the :data:`JWKValuesregistry` variable.
+        key type are defined in the :data:`JWKValuesRegistry` variable.
 
         To generate a new random key call the class method generate() with
         the appropriate 'kty' parameter, and other parameters as needed (key
@@ -287,13 +290,13 @@ class JWK(object):
         Valid options per type, when generating new keys:
          * oct: size(int)
          * RSA: public_exponent(int), size(int)
-         * EC: crv(str) (one of P-256, P-384, P-521)
+         * EC: crv(str) (one of P-256, P-384, P-521, secp256k1)
          * OKP: crv(str) (one of Ed25519, Ed448, X25519, X448)
 
         Deprecated:
         Alternatively if the 'generate' parameter is provided, with a
         valid key type as value then a new key will be generated according
-        to the defaults or provided key strenght options (type specific).
+        to the defaults or provided key strength options (type specific).
 
         :raises InvalidJWKType: if the key type is invalid
         :raises InvalidJWKValue: if incorrect or inconsistent parameters
@@ -401,6 +404,8 @@ class JWK(object):
             return ec.SECP384R1()
         elif name == 'P-521':
             return ec.SECP521R1()
+        elif name == 'secp256k1':
+            return ec.SECP256K1()
         elif name in _OKP_CURVES_TABLE:
             return name
         else:
@@ -450,10 +455,16 @@ class JWK(object):
                                   'OKP key type' % params['crv'])
         self._import_pyca_pri_okp(key, **params)
 
+    def _okp_curve_from_pyca_key(self, key):
+        for name, val in iteritems(_OKP_CURVES_TABLE):
+            if isinstance(key, (val.pubkey, val.privkey)):
+                return name
+        raise InvalidJWKValue('Invalid OKP Key object %r' % key)
+
     def _import_pyca_pri_okp(self, key, **params):
         params.update(
             kty='OKP',
-            crv=params['crv'],
+            crv=self._okp_curve_from_pyca_key(key),
             d=base64url_encode(key.private_bytes(
                 serialization.Encoding.Raw,
                 serialization.PrivateFormat.Raw,
@@ -467,10 +478,10 @@ class JWK(object):
     def _import_pyca_pub_okp(self, key, **params):
         params.update(
             kty='OKP',
-            crv=params['crv'],
+            crv=self._okp_curve_from_pyca_key(key),
             x=base64url_encode(key.public_bytes(
                 serialization.Encoding.Raw,
-                serialization.PrivateFormat.Raw))
+                serialization.PublicFormat.Raw))
         )
         self.import_key(**params)
 
@@ -565,10 +576,10 @@ class JWK(object):
         obj.import_key(**jkey)
         return obj
 
-    def export(self, private_key=True):
+    def export(self, private_key=True, as_dict=False):
         """Exports the key in the standard JSON format.
         Exports the key regardless of type, if private_key is False
-        and the key is_symmetric an exceptionis raised.
+        and the key is_symmetric an exception is raised.
 
         :param private_key(bool): Whether to export the private key.
                                   Defaults to True.
@@ -576,16 +587,20 @@ class JWK(object):
         if private_key is True:
             # Use _export_all for backwards compatibility, as this
             # function allows to export symmetrict keys too
-            return self._export_all()
-        else:
-            return self.export_public()
+            return self._export_all(as_dict)
 
-    def export_public(self):
+        return self.export_public(as_dict)
+
+    def export_public(self, as_dict=False):
         """Exports the public key in the standard JSON format.
         It fails if one is not available like when this function
         is called on a symmetric key.
+
+        :param as_dict(bool): If set to True export as python dict not JSON
         """
         pub = self._public_params()
+        if as_dict is True:
+            return pub
         return json_encode(pub)
 
     def _public_params(self):
@@ -603,24 +618,28 @@ class JWK(object):
                 pub[param] = self._key[param]
         return pub
 
-    def _export_all(self):
+    def _export_all(self, as_dict=False):
         d = dict()
         d.update(self._params)
         d.update(self._key)
         d.update(self._unknown)
+        if as_dict is True:
+            return d
         return json_encode(d)
 
-    def export_private(self):
+    def export_private(self, as_dict=False):
         """Export the private key in the standard JSON format.
         It fails for a JWK that has only a public key or is symmetric.
+
+        :param as_dict(bool): If set to True export as python dict not JSON
         """
         if self.has_private:
-            return self._export_all()
+            return self._export_all(as_dict)
         raise InvalidJWKType("No private key available")
 
-    def export_symmetric(self):
+    def export_symmetric(self, as_dict=False):
         if self.is_symmetric:
-            return self._export_all()
+            return self._export_all(as_dict)
         raise InvalidJWKType("Not a symmetric key")
 
     def public(self):
@@ -767,12 +786,12 @@ class JWK(object):
             raise NotImplementedError
 
     def get_op_key(self, operation=None, arg=None):
-        """Get the key object associated to the requested opration.
+        """Get the key object associated to the requested operation.
         For example the public RSA key for the 'verify' operation or
         the private EC key for the 'decrypt' operation.
 
         :param operation: The requested operation.
-         The valid set of operations is availble in the
+         The valid set of operations is available in the
          :data:`JWKOperationsRegistry` registry.
         :param arg: an optional, context specific, argument
          For example a curve name.
@@ -936,7 +955,7 @@ class _JWKkeys(set):
 class JWKSet(dict):
     """A set of JWK objects.
 
-    Inherits from the standard 'dict' bultin type.
+    Inherits from the standard 'dict' builtin type.
     Creates a special key 'keys' that is of a type derived from 'set'
     The 'keys' attribute accepts only :class:`jwcrypto.jwk.JWK` elements.
     """
@@ -964,26 +983,31 @@ class JWKSet(dict):
     def add(self, elem):
         self['keys'].add(elem)
 
-    def export(self, private_keys=True):
-        """Exports a RFC 7517 keyset using the standard JSON format
+    def export(self, private_keys=True, as_dict=False):
+        """Exports a RFC 7517 key set.
+           Exports as json by default, or as dict if requested.
 
         :param private_key(bool): Whether to export private keys.
                                   Defaults to True.
+        :param as_dict(bool): Whether to return a dict instead of
+                              a JSON object
         """
         exp_dict = dict()
         for k, v in iteritems(self):
             if k == 'keys':
                 keys = list()
                 for jwk in v:
-                    keys.append(json_decode(jwk.export(private_keys)))
+                    keys.append(jwk.export(private_keys, as_dict=True))
                 v = keys
             exp_dict[k] = v
+        if as_dict is True:
+            return exp_dict
         return json_encode(exp_dict)
 
     def import_keyset(self, keyset):
-        """Imports a RFC 7517 keyset using the standard JSON format.
+        """Imports a RFC 7517 key set using the standard JSON format.
 
-        :param keyset: The RFC 7517 representation of a JOSE Keyset.
+        :param keyset: The RFC 7517 representation of a JOSE key set.
         """
         try:
             jwkset = json_decode(keyset)
@@ -1002,9 +1026,9 @@ class JWKSet(dict):
 
     @classmethod
     def from_json(cls, keyset):
-        """Creates a RFC 7517 keyset from the standard JSON format.
+        """Creates a RFC 7517 key set from the standard JSON format.
 
-        :param keyset: The RFC 7517 representation of a JOSE Keyset.
+        :param keyset: The RFC 7517 representation of a JOSE key set.
         """
         obj = cls()
         obj.import_keyset(keyset)


=====================================
jwcrypto/jws.py
=====================================
@@ -326,7 +326,7 @@ class JWS(object):
                 except Exception as e:  # pylint: disable=broad-except
                     self.verifylog.append('Failed: [%s]' % repr(e))
         else:
-            raise InvalidJWSSignature('No signatures availble')
+            raise InvalidJWSSignature('No signatures available')
 
         if not self.is_valid:
             raise InvalidJWSSignature('Verification failed for all '
@@ -373,7 +373,7 @@ class JWS(object):
         :param alg: The signing algorithm (optional). usually the algorithm
          is known as it is provided with the JOSE Headers of the token.
 
-        :raises InvalidJWSObject: if the raw object is an invaid JWS token.
+        :raises InvalidJWSObject: if the raw object is an invalid JWS token.
         :raises InvalidJWSSignature: if the verification fails.
         """
         self.objects = dict()
@@ -480,7 +480,9 @@ class JWS(object):
         if alg is None:
             raise ValueError('"alg" not specified')
 
-        c = JWSCore(alg, key, protected, self.objects['payload'])
+        c = JWSCore(
+            alg, key, protected, self.objects['payload'], self.allowed_algs
+        )
         sig = c.sign()
 
         o = dict()
@@ -516,7 +518,7 @@ class JWS(object):
          representation, otherwise generates a standard JSON format.
 
         :raises InvalidJWSOperation: if the object cannot serialized
-         with the compact representation and `compat` is True.
+         with the compact representation and `compact` is True.
         :raises InvalidJWSSignature: if no signature has been added
          to the object, or no valid signature can be found.
         """


=====================================
jwcrypto/jwt.py
=====================================
@@ -162,7 +162,7 @@ class JWT(object):
          the token. A (:class:`jwcrypto.jwk.JWKSet`) can also be used.
         :param algs: An optional list of allowed algorithms
         :param default_claims: An optional dict with default values for
-         registred claims. A None value for NumericDate type claims
+         registered claims. A None value for NumericDate type claims
          will cause generation according to system time. Only the values
          from RFC 7519 - 4.1 are evaluated.
         :param check_claims: An optional dict of claims that must be
@@ -171,7 +171,7 @@ class JWT(object):
 
         Note: either the header,claims or jwt,key parameters should be
         provided as a deserialization operation (which occurs if the jwt
-        is provided will wipe any header os claim provided by setting
+        is provided) will wipe any header or claim provided by setting
         those obtained from the deserialization of the jwt token.
 
         Note: if check_claims is not provided the 'exp' and 'nbf' claims
@@ -258,8 +258,8 @@ class JWT(object):
         return self._leeway
 
     @leeway.setter
-    def leeway(self, l):
-        self._leeway = int(l)
+    def leeway(self, lwy):
+        self._leeway = int(lwy)
 
     @property
     def validity(self):
@@ -418,12 +418,14 @@ class JWT(object):
 
         Creates a JWS token with the header as the JWS protected header and
         the claims as the payload. See (:class:`jwcrypto.jws.JWS`) for
-        details on the exceptions that may be reaised.
+        details on the exceptions that may be raised.
 
         :param key: A (:class:`jwcrypto.jwk.JWK`) key.
         """
 
         t = JWS(self.claims)
+        if self._algs:
+            t.allowed_algs = self._algs
         t.add_signature(key, protected=self.header)
         self.token = t
 
@@ -432,7 +434,7 @@ class JWT(object):
 
         Creates a JWE token with the header as the JWE protected header and
         the claims as the plaintext. See (:class:`jwcrypto.jwe.JWE`) for
-        details on the exceptions that may be reaised.
+        details on the exceptions that may be raised.
 
         :param key: A (:class:`jwcrypto.jwk.JWK`) key.
         """
@@ -511,7 +513,7 @@ class JWT(object):
         Note: the compact parameter is provided for general compatibility
         with the serialize() functions of :class:`jwcrypto.jws.JWS` and
         :class:`jwcrypto.jwe.JWE` so that these objects can all be used
-        interchangeably. However the only valid JWT representtion is the
+        interchangeably. However the only valid JWT representation is the
         compact representation.
         """
         return self.token.serialize(compact)


=====================================
jwcrypto/tests.py
=====================================
@@ -259,6 +259,39 @@ PrivateKeys_EdDsa = {
     ]
 }
 
+PublicKeys_secp256k1 = {
+    "keys": [
+        {
+            "kty": "EC",
+            "crv": "secp256k1",
+            "x": "Ss6na3mcci8Ud4lQrjaB_T40sfKApEcl2RLIWOJdjow",
+            "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8"
+        }
+    ]
+}
+
+PrivateKeys_secp256k1 = {
+    "keys": [
+        {
+            "kty": "EC",
+            "crv": "secp256k1",
+            "x": "Ss6na3mcci8Ud4lQrjaB_T40sfKApEcl2RLIWOJdjow",
+            "y": "7l9qIKtKPW6oEiOYBt7r22Sm0mtFJU-yBkkvMvpscd8",
+            "d": "GYhU2vrYGZrjLZn71Xniqm54Mi53xiYtaTLawzaf9dA"
+        },
+    ]
+}
+
+Ed25519PrivatePEM = b"""-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIEh4ImJiiZgSNg9J9I+Z5toHKh6LDO2MCbSYNZTkMXDU
+-----END PRIVATE KEY-----
+"""
+
+Ed25519PublicPEM = b"""-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAlsRcb1mVVIUcDjNqZU27N+iPXihH1EQDa/O3utHLtqc=
+-----END PUBLIC KEY-----
+"""
+
 
 class TestJWK(unittest.TestCase):
     def test_create_pubKeys(self):
@@ -319,6 +352,9 @@ class TestJWK(unittest.TestCase):
         # New param prevails
         key = jwk.JWK.generate(kty='EC', curve='P-256', crv='P-521')
         key.get_curve('P-521')
+        # New secp256k curve
+        key = jwk.JWK.generate(kty='EC', curve='secp256k1')
+        key.get_curve('secp256k1')
 
     def test_generate_OKP_keys(self):
         for crv in jwk.ImplementedOkpCurves:
@@ -364,7 +400,7 @@ class TestJWK(unittest.TestCase):
         ks3 = jwk.JWKSet.from_json(ks.export())
         self.assertEqual(len(ks), len(ks3))
 
-        # Test Keyset with mutiple keys
+        # Test key set with mutiple keys
         ksm = jwk.JWKSet.from_json(json_encode(PrivateKeys))
         num = 0
         for item in ksm:
@@ -422,6 +458,21 @@ class TestJWK(unittest.TestCase):
         self.assertFalse(pubkey.has_private)
         self.assertEqual(prikey.key_id, pubkey.key_id)
 
+    def test_export_as_dict(self):
+        key = jwk.JWK(**SymmetricKeys['keys'][1])
+        k = key.export_symmetric(as_dict=True)
+        self.assertEqual(k['kid'], SymmetricKeys['keys'][1]['kid'])
+        key = jwk.JWK.from_pem(PublicCert)
+        k = key.export_public(as_dict=True)
+        self.assertEqual(k['kid'], PublicCertThumbprint)
+        key = jwk.JWK.from_pem(RSAPrivatePEM, password=RSAPrivatePassword)
+        k = key.export_private(as_dict=True)
+        self.assertEqual(k['kid'],
+                         u'x31vrbZceU2qOPLtrUwPkLa3PNakMn9tOsq_ntFVrJc')
+        keyset = jwk.JWKSet.from_json(json_encode(PrivateKeys))
+        ks = keyset.export(as_dict=True)
+        self.assertTrue('keys' in ks)
+
     def test_public(self):
         key = jwk.JWK.from_pem(RSAPrivatePEM, password=RSAPrivatePassword)
         self.assertTrue(key.has_public)
@@ -451,6 +502,16 @@ class TestJWK(unittest.TestCase):
         for key in keylist:
             jwk.JWK(**key)
 
+    def test_create_pubKeys_secp256k1(self):
+        keylist = PublicKeys_secp256k1['keys']
+        for key in keylist:
+            jwk.JWK(**key)
+
+    def test_create_priKeys_secp256k1(self):
+        keylist = PrivateKeys_secp256k1['keys']
+        for key in keylist:
+            jwk.JWK(**key)
+
     def test_thumbprint_eddsa(self):
         for i in range(0, len(PublicKeys_EdDsa['keys'])):
             k = jwk.JWK(**PublicKeys_EdDsa['keys'][i])
@@ -458,6 +519,22 @@ class TestJWK(unittest.TestCase):
                 k.thumbprint(),
                 PublicKeys_EdDsa['thumbprints'][i])
 
+    def test_pem_okp(self):
+        payload = b'Imported private Ed25519'
+        prikey = jwk.JWK.from_pem(Ed25519PrivatePEM)
+        self.assertTrue(prikey.has_private)
+        self.assertTrue(prikey.has_public)
+        s = jws.JWS(payload)
+        s.add_signature(prikey, None, {'alg': 'EdDSA'}, None)
+        sig = s.serialize()
+        pubkey = jwk.JWK.from_pem(Ed25519PublicPEM)
+        self.assertTrue(pubkey.has_public)
+        self.assertFalse(pubkey.has_private)
+        c = jws.JWS()
+        c.deserialize(sig, pubkey, alg="EdDSA")
+        self.assertTrue(c.objects['valid'])
+        self.assertEqual(c.payload, payload)
+
 
 # RFC 7515 - A.1
 A1_protected = \
@@ -786,6 +863,19 @@ class TestJWS(unittest.TestCase):
             self.assertEqual(jws_verify.payload.decode('utf-8'),
                              curve_example['payload'])
 
+    def test_secp256k1_signing_and_verification(self):
+        key = jwk.JWK(**PrivateKeys_secp256k1['keys'][0])
+        payload = bytes(bytearray(A1_payload))
+        jws_test = jws.JWS(payload)
+        jws_test.allowed_algs = ['ES256K']
+        jws_test.add_signature(key, None, json_encode({"alg": "ES256K"}), None)
+        jws_test_serialization_compact = jws_test.serialize(compact=True)
+        jws_verify = jws.JWS()
+        jws_verify.allowed_algs = ['ES256K']
+        jws_verify.deserialize(jws_test_serialization_compact)
+        jws_verify.verify(key.public())
+        self.assertEqual(jws_verify.payload, payload)
+
 
 E_A1_plaintext = \
     [84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32,


=====================================
setup.py
=====================================
@@ -6,7 +6,7 @@ from setuptools import setup
 
 setup(
     name = 'jwcrypto',
-    version = '0.7',
+    version = '0.8',
     license = 'LGPLv3+',
     maintainer = 'JWCrypto Project Contributors',
     maintainer_email = 'simo at redhat.com',
@@ -19,12 +19,13 @@ setup(
         'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
         'Intended Audience :: Developers',
         'Topic :: Security',
         'Topic :: Software Development :: Libraries :: Python Modules'
     ],
     data_files = [('share/doc/jwcrypto', ['LICENSE', 'README.md'])],
     install_requires = [
-        'cryptography >= 1.5',
+        'cryptography >= 2.3',
     ],
 )


=====================================
tox.ini
=====================================
@@ -50,10 +50,10 @@ commands =
     markdown_py README.md -f {toxworkdir}/README.md.html
 
 [testenv:sphinx]
-basepython = python2.7
+basepython = python3
 changedir = docs/source
 deps =
-    sphinx < 1.3.0
+    sphinx
 commands =
     sphinx-build -v -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
 



View it on GitLab: https://salsa.debian.org/freeipa-team/python-jwcrypto/-/compare/904c7902fff8514f1f42161c8e1fa23d11e75c76...84bd2862b9e5ce2e9631d5bed4c70c4b911b8531

-- 
View it on GitLab: https://salsa.debian.org/freeipa-team/python-jwcrypto/-/compare/904c7902fff8514f1f42161c8e1fa23d11e75c76...84bd2862b9e5ce2e9631d5bed4c70c4b911b8531
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-freeipa-devel/attachments/20210105/3449a2f8/attachment-0001.html>


More information about the Pkg-freeipa-devel mailing list