es256.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. # Copyright 2017 Google Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """ECDSA (ES256) verifier and signer that use the ``cryptography`` library.
  15. """
  16. from cryptography import utils # type: ignore
  17. import cryptography.exceptions
  18. from cryptography.hazmat import backends
  19. from cryptography.hazmat.primitives import hashes
  20. from cryptography.hazmat.primitives import serialization
  21. from cryptography.hazmat.primitives.asymmetric import ec
  22. from cryptography.hazmat.primitives.asymmetric import padding
  23. from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
  24. from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
  25. import cryptography.x509
  26. from google.auth import _helpers
  27. from google.auth.crypt import base
  28. _CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
  29. _BACKEND = backends.default_backend()
  30. _PADDING = padding.PKCS1v15()
  31. class ES256Verifier(base.Verifier):
  32. """Verifies ECDSA cryptographic signatures using public keys.
  33. Args:
  34. public_key (
  35. cryptography.hazmat.primitives.asymmetric.ec.ECDSAPublicKey):
  36. The public key used to verify signatures.
  37. """
  38. def __init__(self, public_key):
  39. self._pubkey = public_key
  40. @_helpers.copy_docstring(base.Verifier)
  41. def verify(self, message, signature):
  42. # First convert (r||s) raw signature to ASN1 encoded signature.
  43. sig_bytes = _helpers.to_bytes(signature)
  44. if len(sig_bytes) != 64:
  45. return False
  46. r = (
  47. int.from_bytes(sig_bytes[:32], byteorder="big")
  48. if _helpers.is_python_3()
  49. else utils.int_from_bytes(sig_bytes[:32], byteorder="big")
  50. )
  51. s = (
  52. int.from_bytes(sig_bytes[32:], byteorder="big")
  53. if _helpers.is_python_3()
  54. else utils.int_from_bytes(sig_bytes[32:], byteorder="big")
  55. )
  56. asn1_sig = encode_dss_signature(r, s)
  57. message = _helpers.to_bytes(message)
  58. try:
  59. self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256()))
  60. return True
  61. except (ValueError, cryptography.exceptions.InvalidSignature):
  62. return False
  63. @classmethod
  64. def from_string(cls, public_key):
  65. """Construct an Verifier instance from a public key or public
  66. certificate string.
  67. Args:
  68. public_key (Union[str, bytes]): The public key in PEM format or the
  69. x509 public key certificate.
  70. Returns:
  71. Verifier: The constructed verifier.
  72. Raises:
  73. ValueError: If the public key can't be parsed.
  74. """
  75. public_key_data = _helpers.to_bytes(public_key)
  76. if _CERTIFICATE_MARKER in public_key_data:
  77. cert = cryptography.x509.load_pem_x509_certificate(
  78. public_key_data, _BACKEND
  79. )
  80. pubkey = cert.public_key()
  81. else:
  82. pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
  83. return cls(pubkey)
  84. class ES256Signer(base.Signer, base.FromServiceAccountMixin):
  85. """Signs messages with an ECDSA private key.
  86. Args:
  87. private_key (
  88. cryptography.hazmat.primitives.asymmetric.ec.ECDSAPrivateKey):
  89. The private key to sign with.
  90. key_id (str): Optional key ID used to identify this private key. This
  91. can be useful to associate the private key with its associated
  92. public key or certificate.
  93. """
  94. def __init__(self, private_key, key_id=None):
  95. self._key = private_key
  96. self._key_id = key_id
  97. @property # type: ignore
  98. @_helpers.copy_docstring(base.Signer)
  99. def key_id(self):
  100. return self._key_id
  101. @_helpers.copy_docstring(base.Signer)
  102. def sign(self, message):
  103. message = _helpers.to_bytes(message)
  104. asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256()))
  105. # Convert ASN1 encoded signature to (r||s) raw signature.
  106. (r, s) = decode_dss_signature(asn1_signature)
  107. return (
  108. (r.to_bytes(32, byteorder="big") + s.to_bytes(32, byteorder="big"))
  109. if _helpers.is_python_3()
  110. else (utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32))
  111. )
  112. @classmethod
  113. def from_string(cls, key, key_id=None):
  114. """Construct a RSASigner from a private key in PEM format.
  115. Args:
  116. key (Union[bytes, str]): Private key in PEM format.
  117. key_id (str): An optional key id used to identify the private key.
  118. Returns:
  119. google.auth.crypt._cryptography_rsa.RSASigner: The
  120. constructed signer.
  121. Raises:
  122. ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
  123. UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
  124. into a UTF-8 ``str``.
  125. ValueError: If ``cryptography`` "Could not deserialize key data."
  126. """
  127. key = _helpers.to_bytes(key)
  128. private_key = serialization.load_pem_private_key(
  129. key, password=None, backend=_BACKEND
  130. )
  131. return cls(private_key, key_id=key_id)
  132. def __getstate__(self):
  133. """Pickle helper that serializes the _key attribute."""
  134. state = self.__dict__.copy()
  135. state["_key"] = self._key.private_bytes(
  136. encoding=serialization.Encoding.PEM,
  137. format=serialization.PrivateFormat.PKCS8,
  138. encryption_algorithm=serialization.NoEncryption(),
  139. )
  140. return state
  141. def __setstate__(self, state):
  142. """Pickle helper that deserializes the _key attribute."""
  143. state["_key"] = serialization.load_pem_private_key(state["_key"], None)
  144. self.__dict__.update(state)