From cf2c0a90701ce42f47df71281ae9cdf212c28e0e Mon Sep 17 00:00:00 2001 From: arithmetic1728 <58957152+arithmetic1728@users.noreply.github.com> Date: Tue, 21 Apr 2020 14:33:20 -0700 Subject: [PATCH 1/2] fix: support es256 raw format signature (#490) es256 signature in id_token has raw format, however, cryptography library verification/signing only works for asn1 encoded format. Therefore in verification/signing process, we need to convert between the ans1 encoded format and the raw format. --- google/auth/crypt/es256.py | 19 +++++++++++++++++-- tests/crypt/test_es256.py | 12 ++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/google/auth/crypt/es256.py b/google/auth/crypt/es256.py index 5bfd57fb8..6955efcc5 100644 --- a/google/auth/crypt/es256.py +++ b/google/auth/crypt/es256.py @@ -15,12 +15,15 @@ """ECDSA (ES256) verifier and signer that use the ``cryptography`` library. """ +from cryptography import utils import cryptography.exceptions from cryptography.hazmat import backends from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature +from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature import cryptography.x509 import pkg_resources @@ -58,9 +61,17 @@ def __init__(self, public_key): @_helpers.copy_docstring(base.Verifier) def verify(self, message, signature): + # First convert (r||s) raw signature to ASN1 encoded signature. + sig_bytes = _helpers.to_bytes(signature) + if len(sig_bytes) != 64: + return False + r = utils.int_from_bytes(sig_bytes[:32], byteorder="big") + s = utils.int_from_bytes(sig_bytes[32:], byteorder="big") + asn1_sig = encode_dss_signature(r, s) + message = _helpers.to_bytes(message) try: - self._pubkey.verify(signature, message, ec.ECDSA(hashes.SHA256())) + self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256())) return True except (ValueError, cryptography.exceptions.InvalidSignature): return False @@ -118,7 +129,11 @@ def key_id(self): @_helpers.copy_docstring(base.Signer) def sign(self, message): message = _helpers.to_bytes(message) - return self._key.sign(message, ec.ECDSA(hashes.SHA256())) + asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256())) + + # Convert ASN1 encoded signature to (r||s) raw signature. + (r, s) = decode_dss_signature(asn1_signature) + return utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32) @classmethod def from_string(cls, key, key_id=None): diff --git a/tests/crypt/test_es256.py b/tests/crypt/test_es256.py index 087ce6e23..5bb9050cd 100644 --- a/tests/crypt/test_es256.py +++ b/tests/crypt/test_es256.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import base64 import json import os @@ -72,6 +73,17 @@ def test_verify_failure(self): bad_signature2 = b"a" assert not verifier.verify(b"foo", bad_signature2) + def test_verify_failure_with_wrong_raw_signature(self): + to_sign = b"foo" + + # This signature has a wrong "r" value in the "(r,s)" raw signature. + wrong_signature = base64.urlsafe_b64decode( + b"m7oaRxUDeYqjZ8qiMwo0PZLTMZWKJLFQREpqce1StMIa_yXQQ-C5WgeIRHW7OqlYSDL0XbUrj_uAw9i-QhfOJQ==" + ) + + verifier = es256.ES256Verifier.from_string(PUBLIC_KEY_BYTES) + assert not verifier.verify(to_sign, wrong_signature) + def test_from_string_pub_key(self): verifier = es256.ES256Verifier.from_string(PUBLIC_KEY_BYTES) assert isinstance(verifier, es256.ES256Verifier) From ac74905330a1fdd0663bbe1d6ee8dca7e66f180f Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2020 14:50:57 -0700 Subject: [PATCH 2/2] chore: release 1.14.1 (#491) * updated CHANGELOG.md [ci skip] * updated setup.cfg [ci skip] * updated setup.py [ci skip] Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ setup.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 268077fdf..a47c7e67e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ [1]: https://pypi.org/project/google-auth/#history +### [1.14.1](https://www.github.com/googleapis/google-auth-library-python/compare/v1.14.0...v1.14.1) (2020-04-21) + + +### Bug Fixes + +* support es256 raw format signature ([#490](https://www.github.com/googleapis/google-auth-library-python/issues/490)) ([cf2c0a9](https://www.github.com/googleapis/google-auth-library-python/commit/cf2c0a90701ce42f47df71281ae9cdf212c28e0e)) + ## [1.14.0](https://www.github.com/googleapis/google-auth-library-python/compare/v1.13.1...v1.14.0) (2020-04-13) diff --git a/setup.py b/setup.py index 2acef446b..a0d279ffc 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ with io.open("README.rst", "r") as fh: long_description = fh.read() -version = "1.14.0" +version = "1.14.1" setup( name="google-auth",