Skip to content
This repository has been archived by the owner on Nov 5, 2019. It is now read-only.

Commit

Permalink
Deferring OpenSSL import until usage.
Browse files Browse the repository at this point in the history
This is to speed up import times. In OpenSSL 0.14 the import takes
0.5 seconds due to cffi on-demand build of extensions in the
cryptography library.

For classes and functions which are conditionally defined based on
the existence of OpenSSL.crypto, we check that the module
exists (but don't import it) using imp.find_module.
  • Loading branch information
dhermes committed Apr 8, 2015
1 parent 0a6241c commit 9dc6ba2
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 5 deletions.
12 changes: 11 additions & 1 deletion oauth2client/crypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
"""Crypto-related routines for oauth2client."""

import base64
import imp
import json
import logging
import os
import sys
import time

Expand All @@ -37,7 +39,10 @@ class AppIdentityError(Exception):


try:
from OpenSSL import crypto
_, package_dir, _ = imp.find_module('OpenSSL')
if not os.path.isfile(os.path.join(package_dir, 'crypto.py')):
raise ImportError('No module named OpenSSL')
del package_dir

class OpenSSLVerifier(object):
"""Verifies the signature on a message."""
Expand All @@ -61,6 +66,7 @@ def verify(self, message, signature):
True if message was signed by the private key associated with the public
key that this object was constructed with.
"""
from OpenSSL import crypto
try:
if isinstance(message, six.text_type):
message = message.encode('utf-8')
Expand All @@ -84,6 +90,7 @@ def from_string(key_pem, is_x509_cert):
Raises:
OpenSSL.crypto.Error if the key_pem can't be parsed.
"""
from OpenSSL import crypto
if is_x509_cert:
pubkey = crypto.load_certificate(crypto.FILETYPE_PEM, key_pem)
else:
Expand Down Expand Up @@ -111,6 +118,7 @@ def sign(self, message):
Returns:
string, The signature of the message for the given key.
"""
from OpenSSL import crypto
if isinstance(message, six.text_type):
message = message.encode('utf-8')
return crypto.sign(self._key, message, 'sha256')
Expand All @@ -129,6 +137,7 @@ def from_string(key, password=b'notasecret'):
Raises:
OpenSSL.crypto.Error if the key can't be parsed.
"""
from OpenSSL import crypto
parsed_pem_key = _parse_pem_key(key)
if parsed_pem_key:
pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, parsed_pem_key)
Expand All @@ -149,6 +158,7 @@ def pkcs12_key_as_pem(private_key_text, private_key_password):
Returns:
String. PEM contents of ``private_key_text``.
"""
from OpenSSL import crypto
decoded_body = base64.b64decode(private_key_text)
if isinstance(private_key_password, six.string_types):
private_key_password = private_key_password.encode('ascii')
Expand Down
10 changes: 6 additions & 4 deletions tests/test_crypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,19 @@ def test_succeeds(self):
self.assertTrue(pem_contents in [pkcs12_key_as_pem, alternate_pem])

def test_without_openssl(self):
openssl_mod = sys.modules['OpenSSL']
import os
path_isfile = os.path.isfile
try:
sys.modules['OpenSSL'] = None
os.path.isfile = lambda value: False
reload(crypt)
self.assertRaises(NotImplementedError, crypt.pkcs12_key_as_pem,
'FOO', 'BAR')
finally:
sys.modules['OpenSSL'] = openssl_mod
os.path.isfile = path_isfile
reload(crypt)

def test_with_nonsense_key(self):
from OpenSSL import crypto
credentials = self._make_signed_jwt_creds(private_key=b'NOT_A_KEY')
self.assertRaises(crypt.crypto.Error, crypt.pkcs12_key_as_pem,
self.assertRaises(crypto.Error, crypt.pkcs12_key_as_pem,
credentials.private_key, credentials.private_key_password)

0 comments on commit 9dc6ba2

Please sign in to comment.