Skip to content
This repository was archived by the owner on Mar 6, 2026. It is now read-only.

Commit 9281ca0

Browse files
author
Jon Wayne Parrott
authored
Split crypt into a package to allow alternative implementations (#189)
1 parent 6e86c93 commit 9281ca0

7 files changed

Lines changed: 266 additions & 159 deletions

File tree

google/auth/crypt/__init__.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright 2016 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+
15+
"""Cryptography helpers for verifying and signing messages.
16+
17+
The simplest way to verify signatures is using :func:`verify_signature`::
18+
19+
cert = open('certs.pem').read()
20+
valid = crypt.verify_signature(message, signature, cert)
21+
22+
If you're going to verify many messages with the same certificate, you can use
23+
:class:`RSAVerifier`::
24+
25+
cert = open('certs.pem').read()
26+
verifier = crypt.RSAVerifier.from_string(cert)
27+
valid = verifier.verify(message, signature)
28+
29+
To sign messages use :class:`RSASigner` with a private key::
30+
31+
private_key = open('private_key.pem').read()
32+
signer = crypt.RSASigner(private_key)
33+
signature = signer.sign(message)
34+
"""
35+
36+
import six
37+
38+
from google.auth.crypt import base
39+
from google.auth.crypt import rsa
40+
41+
42+
__all__ = [
43+
'RSASigner',
44+
'RSAVerifier',
45+
'Signer',
46+
'Verifier',
47+
]
48+
49+
# Aliases to maintain the v1.0.0 interface, as the crypt module was split
50+
# into submodules.
51+
Signer = base.Signer
52+
Verifier = base.Verifier
53+
RSASigner = rsa.RSASigner
54+
RSAVerifier = rsa.RSAVerifier
55+
56+
57+
def verify_signature(message, signature, certs):
58+
"""Verify an RSA cryptographic signature.
59+
60+
Checks that the provided ``signature`` was generated from ``bytes`` using
61+
the private key associated with the ``cert``.
62+
63+
Args:
64+
message (Union[str, bytes]): The plaintext message.
65+
signature (Union[str, bytes]): The cryptographic signature to check.
66+
certs (Union[Sequence, str, bytes]): The certificate or certificates
67+
to use to check the signature.
68+
69+
Returns:
70+
bool: True if the signature is valid, otherwise False.
71+
"""
72+
if isinstance(certs, (six.text_type, six.binary_type)):
73+
certs = [certs]
74+
75+
for cert in certs:
76+
verifier = rsa.RSAVerifier.from_string(cert)
77+
if verifier.verify(message, signature):
78+
return True
79+
return False
Lines changed: 9 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,15 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""Cryptography helpers for verifying and signing messages.
15+
"""Pure-Python RSA cryptography implementation.
1616
1717
Uses the ``rsa``, ``pyasn1`` and ``pyasn1_modules`` packages
1818
to parse PEM files storing PKCS#1 or PKCS#8 keys as well as
1919
certificates. There is no support for p12 files.
20+
"""
2021

21-
The simplest way to verify signatures is using :func:`verify_signature`::
22-
23-
cert = open('certs.pem').read()
24-
valid = crypt.verify_signature(message, signature, cert)
25-
26-
If you're going to verify many messages with the same certificate, you can use
27-
:class:`RSAVerifier`::
28-
29-
cert = open('certs.pem').read()
30-
verifier = crypt.RSAVerifier.from_string(cert)
31-
valid = verifier.verify(message, signature)
32-
33-
34-
To sign messages use :class:`RSASigner` with a private key::
35-
36-
private_key = open('private_key.pem').read()
37-
signer = crypt.RSASigner(private_key)
38-
signature = signer.sign(message)
22+
from __future__ import absolute_import
3923

40-
"""
41-
import abc
4224
import io
4325
import json
4426

@@ -50,6 +32,7 @@
5032
import six
5133

5234
from google.auth import _helpers
35+
from google.auth.crypt import base
5336

5437
_POW2 = (128, 64, 32, 16, 8, 4, 2, 1)
5538
_CERTIFICATE_MARKER = b'-----BEGIN CERTIFICATE-----'
@@ -84,28 +67,7 @@ def _bit_list_to_bytes(bit_list):
8467
return bytes(byte_vals)
8568

8669

87-
@six.add_metaclass(abc.ABCMeta)
88-
class Verifier(object):
89-
"""Abstract base class for crytographic signature verifiers."""
90-
91-
@abc.abstractmethod
92-
def verify(self, message, signature):
93-
"""Verifies a message against a cryptographic signature.
94-
95-
Args:
96-
message (Union[str, bytes]): The message to verify.
97-
signature (Union[str, bytes]): The cryptography signature to check.
98-
99-
Returns:
100-
bool: True if message was signed by the private key associated
101-
with the public key that this object was constructed with.
102-
"""
103-
# pylint: disable=missing-raises-doc,redundant-returns-doc
104-
# (pylint doesn't recognize that this is abstract)
105-
raise NotImplementedError('Verify must be implemented')
106-
107-
108-
class RSAVerifier(Verifier):
70+
class RSAVerifier(base.Verifier):
10971
"""Verifies RSA cryptographic signatures using public keys.
11072
11173
Args:
@@ -116,7 +78,7 @@ class RSAVerifier(Verifier):
11678
def __init__(self, public_key):
11779
self._pubkey = public_key
11880

119-
@_helpers.copy_docstring(Verifier)
81+
@_helpers.copy_docstring(base.Verifier)
12082
def verify(self, message, signature):
12183
message = _helpers.to_bytes(message)
12284
try:
@@ -157,56 +119,7 @@ def from_string(cls, public_key):
157119
return cls(pubkey)
158120

159121

160-
def verify_signature(message, signature, certs):
161-
"""Verify an RSA cryptographic signature.
162-
163-
Checks that the provided ``signature`` was generated from ``bytes`` using
164-
the private key associated with the ``cert``.
165-
166-
Args:
167-
message (Union[str, bytes]): The plaintext message.
168-
signature (Union[str, bytes]): The cryptographic signature to check.
169-
certs (Union[Sequence, str, bytes]): The certificate or certificates
170-
to use to check the signature.
171-
172-
Returns:
173-
bool: True if the signature is valid, otherwise False.
174-
"""
175-
if isinstance(certs, (six.text_type, six.binary_type)):
176-
certs = [certs]
177-
178-
for cert in certs:
179-
verifier = RSAVerifier.from_string(cert)
180-
if verifier.verify(message, signature):
181-
return True
182-
return False
183-
184-
185-
@six.add_metaclass(abc.ABCMeta)
186-
class Signer(object):
187-
"""Abstract base class for cryptographic signers."""
188-
189-
@abc.abstractproperty
190-
def key_id(self):
191-
"""Optional[str]: The key ID used to identify this private key."""
192-
raise NotImplementedError('Key id must be implemented')
193-
194-
@abc.abstractmethod
195-
def sign(self, message):
196-
"""Signs a message.
197-
198-
Args:
199-
message (Union[str, bytes]): The message to be signed.
200-
201-
Returns:
202-
bytes: The signature of the message.
203-
"""
204-
# pylint: disable=missing-raises-doc,redundant-returns-doc
205-
# (pylint doesn't recognize that this is abstract)
206-
raise NotImplementedError('Sign must be implemented')
207-
208-
209-
class RSASigner(Signer):
122+
class RSASigner(base.Signer):
210123
"""Signs messages with an RSA private key.
211124
212125
Args:
@@ -221,11 +134,11 @@ def __init__(self, private_key, key_id=None):
221134
self._key_id = key_id
222135

223136
@property
224-
@_helpers.copy_docstring(Signer)
137+
@_helpers.copy_docstring(base.Signer)
225138
def key_id(self):
226139
return self._key_id
227140

228-
@_helpers.copy_docstring(Signer)
141+
@_helpers.copy_docstring(base.Signer)
229142
def sign(self, message):
230143
message = _helpers.to_bytes(message)
231144
return rsa.pkcs1.sign(message, self._key, 'SHA-256')

google/auth/crypt/base.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2016 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+
15+
"""Base classes for cryptographic signers and verifiers."""
16+
17+
import abc
18+
19+
import six
20+
21+
22+
@six.add_metaclass(abc.ABCMeta)
23+
class Verifier(object):
24+
"""Abstract base class for crytographic signature verifiers."""
25+
26+
@abc.abstractmethod
27+
def verify(self, message, signature):
28+
"""Verifies a message against a cryptographic signature.
29+
30+
Args:
31+
message (Union[str, bytes]): The message to verify.
32+
signature (Union[str, bytes]): The cryptography signature to check.
33+
34+
Returns:
35+
bool: True if message was signed by the private key associated
36+
with the public key that this object was constructed with.
37+
"""
38+
# pylint: disable=missing-raises-doc,redundant-returns-doc
39+
# (pylint doesn't recognize that this is abstract)
40+
raise NotImplementedError('Verify must be implemented')
41+
42+
43+
@six.add_metaclass(abc.ABCMeta)
44+
class Signer(object):
45+
"""Abstract base class for cryptographic signers."""
46+
47+
@abc.abstractproperty
48+
def key_id(self):
49+
"""Optional[str]: The key ID used to identify this private key."""
50+
raise NotImplementedError('Key id must be implemented')
51+
52+
@abc.abstractmethod
53+
def sign(self, message):
54+
"""Signs a message.
55+
56+
Args:
57+
message (Union[str, bytes]): The message to be signed.
58+
59+
Returns:
60+
bytes: The signature of the message.
61+
"""
62+
# pylint: disable=missing-raises-doc,redundant-returns-doc
63+
# (pylint doesn't recognize that this is abstract)
64+
raise NotImplementedError('Sign must be implemented')

google/auth/crypt/rsa.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
15+
"""RSA cryptography signer and verifier."""
16+
17+
from google.auth.crypt import _python_rsa
18+
19+
RSASigner = _python_rsa.RSASigner
20+
RSAVerifier = _python_rsa.RSAVerifier

tests/crypt/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)