Skip to content

Commit dda3730

Browse files
feat: add esdsa p-384 support
1 parent 446c8e7 commit dda3730

File tree

4 files changed

+59
-12
lines changed

4 files changed

+59
-12
lines changed

example.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from google.oauth2 import gdch_credentials
2+
import google.auth.crypt
3+
4+
5+
path = "/usr/local/google/home/sijunliu/wks/creds/gdch_384.json"
6+
cred = gdch_credentials.ServiceAccountCredentials.from_service_account_file(path)
7+
8+
message = "abc"
9+
10+
# Sign the message
11+
signature = cred._signer.sign(message)
12+
13+
# Verify the signature
14+
verifier = google.auth.crypt.EsVerifier(cred._signer._key.public_key())
15+
print(verifier.verify(message, signature))

google/auth/_service_account_info.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def from_dict(data, require=None, use_rsa_signer=True):
5656
if use_rsa_signer:
5757
signer = crypt.RSASigner.from_service_account_info(data)
5858
else:
59-
signer = crypt.ES256Signer.from_service_account_info(data)
59+
signer = crypt.EsSigner.from_service_account_info(data)
6060

6161
return signer
6262

google/auth/crypt/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747

4848
if es256 is not None: # pragma: NO COVER
4949
__all__ = [
50+
"EsSigner",
5051
"ES256Signer",
52+
"EsVerifier",
5153
"ES256Verifier",
5254
"RSASigner",
5355
"RSAVerifier",
@@ -68,6 +70,8 @@
6870
if es256 is not None: # pragma: NO COVER
6971
ES256Signer = es256.ES256Signer
7072
ES256Verifier = es256.ES256Verifier
73+
EsSigner = es256.EsSigner
74+
EsVerifier = es256.EsVerifier
7175

7276

7377
def verify_signature(message, signature, certs, verifier_cls=rsa.RSAVerifier):

google/auth/crypt/es256.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
_PADDING = padding.PKCS1v15()
3636

3737

38-
class ES256Verifier(base.Verifier):
38+
class EsVerifier(base.Verifier):
3939
"""Verifies ECDSA cryptographic signatures using public keys.
4040
4141
Args:
@@ -46,28 +46,38 @@ class ES256Verifier(base.Verifier):
4646

4747
def __init__(self, public_key):
4848
self._pubkey = public_key
49+
# ECDSA raw signature has (r||s) format where r,s are two
50+
# integers of size 32 bytes for P-256 curve and 48 bytes
51+
# for P-384 curve. For P-256 curve, we use SHA256 hash algo,
52+
# and for P-384 curve we use SHA384 algo.
53+
if isinstance(public_key.curve, ec.SECP384R1):
54+
self._r_s_size = 48
55+
self._sha_algo = hashes.SHA384()
56+
else:
57+
self._r_s_size = 32
58+
self._sha_algo = hashes.SHA256()
4959

5060
@_helpers.copy_docstring(base.Verifier)
5161
def verify(self, message, signature):
5262
# First convert (r||s) raw signature to ASN1 encoded signature.
5363
sig_bytes = _helpers.to_bytes(signature)
54-
if len(sig_bytes) != 64:
64+
if len(sig_bytes) != self._r_s_size * 2:
5565
return False
5666
r = (
57-
int.from_bytes(sig_bytes[:32], byteorder="big")
67+
int.from_bytes(sig_bytes[:self._r_s_size], byteorder="big")
5868
if _helpers.is_python_3()
59-
else utils.int_from_bytes(sig_bytes[:32], byteorder="big")
69+
else utils.int_from_bytes(sig_bytes[:self._r_s_size], byteorder="big")
6070
)
6171
s = (
62-
int.from_bytes(sig_bytes[32:], byteorder="big")
72+
int.from_bytes(sig_bytes[self._r_s_size:], byteorder="big")
6373
if _helpers.is_python_3()
64-
else utils.int_from_bytes(sig_bytes[32:], byteorder="big")
74+
else utils.int_from_bytes(sig_bytes[self._r_s_size:], byteorder="big")
6575
)
6676
asn1_sig = encode_dss_signature(r, s)
6777

6878
message = _helpers.to_bytes(message)
6979
try:
70-
self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256()))
80+
self._pubkey.verify(asn1_sig, message, ec.ECDSA(self._sha_algo))
7181
return True
7282
except (ValueError, cryptography.exceptions.InvalidSignature):
7383
return False
@@ -101,7 +111,11 @@ def from_string(cls, public_key):
101111
return cls(pubkey)
102112

103113

104-
class ES256Signer(base.Signer, base.FromServiceAccountMixin):
114+
class ES256Verifier(EsVerifier):
115+
pass
116+
117+
118+
class EsSigner(base.Signer, base.FromServiceAccountMixin):
105119
"""Signs messages with an ECDSA private key.
106120
107121
Args:
@@ -116,6 +130,16 @@ class ES256Signer(base.Signer, base.FromServiceAccountMixin):
116130
def __init__(self, private_key, key_id=None):
117131
self._key = private_key
118132
self._key_id = key_id
133+
# ECDSA raw signature has (r||s) format where r,s are two
134+
# integers of size 32 bytes for P-256 curve and 48 bytes
135+
# for P-384 curve. For P-256 curve, we use SHA256 hash algo,
136+
# and for P-384 curve we use SHA384 algo.
137+
if isinstance(private_key.curve, ec.SECP384R1):
138+
self._r_s_size = 48
139+
self._sha_algo = hashes.SHA384()
140+
else:
141+
self._r_s_size = 32
142+
self._sha_algo = hashes.SHA256()
119143

120144
@property # type: ignore
121145
@_helpers.copy_docstring(base.Signer)
@@ -125,14 +149,14 @@ def key_id(self):
125149
@_helpers.copy_docstring(base.Signer)
126150
def sign(self, message):
127151
message = _helpers.to_bytes(message)
128-
asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256()))
152+
asn1_signature = self._key.sign(message, ec.ECDSA(self._sha_algo))
129153

130154
# Convert ASN1 encoded signature to (r||s) raw signature.
131155
(r, s) = decode_dss_signature(asn1_signature)
132156
return (
133-
(r.to_bytes(32, byteorder="big") + s.to_bytes(32, byteorder="big"))
157+
(r.to_bytes(self._r_s_size, byteorder="big") + s.to_bytes(self._r_s_size, byteorder="big"))
134158
if _helpers.is_python_3()
135-
else (utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32))
159+
else (utils.int_to_bytes(r, self._r_s_size) + utils.int_to_bytes(s, self._r_s_size))
136160
)
137161

138162
@classmethod
@@ -173,3 +197,7 @@ def __setstate__(self, state):
173197
"""Pickle helper that deserializes the _key attribute."""
174198
state["_key"] = serialization.load_pem_private_key(state["_key"], None)
175199
self.__dict__.update(state)
200+
201+
202+
class ES256Signer(EsSigner):
203+
pass

0 commit comments

Comments
 (0)