Skip to content

Commit cf2c0a9

Browse files
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.
1 parent 9dcc516 commit cf2c0a9

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

google/auth/crypt/es256.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
"""ECDSA (ES256) verifier and signer that use the ``cryptography`` library.
1616
"""
1717

18+
from cryptography import utils
1819
import cryptography.exceptions
1920
from cryptography.hazmat import backends
2021
from cryptography.hazmat.primitives import hashes
2122
from cryptography.hazmat.primitives import serialization
2223
from cryptography.hazmat.primitives.asymmetric import ec
2324
from cryptography.hazmat.primitives.asymmetric import padding
25+
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
26+
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
2427
import cryptography.x509
2528
import pkg_resources
2629

@@ -58,9 +61,17 @@ def __init__(self, public_key):
5861

5962
@_helpers.copy_docstring(base.Verifier)
6063
def verify(self, message, signature):
64+
# First convert (r||s) raw signature to ASN1 encoded signature.
65+
sig_bytes = _helpers.to_bytes(signature)
66+
if len(sig_bytes) != 64:
67+
return False
68+
r = utils.int_from_bytes(sig_bytes[:32], byteorder="big")
69+
s = utils.int_from_bytes(sig_bytes[32:], byteorder="big")
70+
asn1_sig = encode_dss_signature(r, s)
71+
6172
message = _helpers.to_bytes(message)
6273
try:
63-
self._pubkey.verify(signature, message, ec.ECDSA(hashes.SHA256()))
74+
self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256()))
6475
return True
6576
except (ValueError, cryptography.exceptions.InvalidSignature):
6677
return False
@@ -118,7 +129,11 @@ def key_id(self):
118129
@_helpers.copy_docstring(base.Signer)
119130
def sign(self, message):
120131
message = _helpers.to_bytes(message)
121-
return self._key.sign(message, ec.ECDSA(hashes.SHA256()))
132+
asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256()))
133+
134+
# Convert ASN1 encoded signature to (r||s) raw signature.
135+
(r, s) = decode_dss_signature(asn1_signature)
136+
return utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32)
122137

123138
@classmethod
124139
def from_string(cls, key, key_id=None):

tests/crypt/test_es256.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import base64
1516
import json
1617
import os
1718

@@ -72,6 +73,17 @@ def test_verify_failure(self):
7273
bad_signature2 = b"a"
7374
assert not verifier.verify(b"foo", bad_signature2)
7475

76+
def test_verify_failure_with_wrong_raw_signature(self):
77+
to_sign = b"foo"
78+
79+
# This signature has a wrong "r" value in the "(r,s)" raw signature.
80+
wrong_signature = base64.urlsafe_b64decode(
81+
b"m7oaRxUDeYqjZ8qiMwo0PZLTMZWKJLFQREpqce1StMIa_yXQQ-C5WgeIRHW7OqlYSDL0XbUrj_uAw9i-QhfOJQ=="
82+
)
83+
84+
verifier = es256.ES256Verifier.from_string(PUBLIC_KEY_BYTES)
85+
assert not verifier.verify(to_sign, wrong_signature)
86+
7587
def test_from_string_pub_key(self):
7688
verifier = es256.ES256Verifier.from_string(PUBLIC_KEY_BYTES)
7789
assert isinstance(verifier, es256.ES256Verifier)

0 commit comments

Comments
 (0)