From 7c659fed65f08f2b52f0320c99a456679749e3f3 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 5 Oct 2012 09:30:50 +0900 Subject: use keyring to store user password using a quite lame cryptedfile by the moment until dbus bug makes gnome-keyring usable again or we come up with the encrypted database solution. we might want to explore the option of using this python-keyring with the different native backends for win and macosx. for now: we generate a random secret that we store in the qsettings file. so, the whole thing is just to avoid plaintext stuff. for this, we could have done rot13, haha. --- src/leap/crypto/__init__.py | 0 src/leap/crypto/leapkeyring.py | 63 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/leap/crypto/__init__.py create mode 100644 src/leap/crypto/leapkeyring.py (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/__init__.py b/src/leap/crypto/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/crypto/leapkeyring.py b/src/leap/crypto/leapkeyring.py new file mode 100644 index 00000000..394142db --- /dev/null +++ b/src/leap/crypto/leapkeyring.py @@ -0,0 +1,63 @@ +import os + +import keyring + +############# +# Disclaimer +############# +# This currently is not a keyring, it's more like a joke. +# No, seriously. +# We're affected by this **bug** + +# https://bitbucket.org/kang/python-keyring-lib/issue/65/dbusexception-method-opensession-with + +# so using the gnome keyring does not seem feasible right now. +# I thought this was the next best option to store secrets in plain sight. + +# in the future we should move to use the gnome/kde/macosx/win keyrings. + + +class LeapCryptedFileKeyring(keyring.backend.CryptedFileKeyring): + + filename = os.path.expanduser("~/.config/leap/.secrets") + + def __init__(self, seed=None): + self.seed = seed + + def _get_new_password(self): + # XXX every time this method is called, + # $deity kills a kitten. + return "secret%s" % self.seed + + def _init_file(self): + self.keyring_key = self._get_new_password() + self.set_password('keyring_setting', 'pass_ref', 'pass_ref_value') + + def _unlock(self): + self.keyring_key = self._get_new_password() + print 'keyring key ', self.keyring_key + try: + ref_pw = self.get_password( + 'keyring_setting', + 'pass_ref') + print 'ref pw ', ref_pw + assert ref_pw == "pass_ref_value" + except AssertionError: + self._lock() + raise ValueError('Incorrect password') + + +def leap_set_password(key, value, seed="xxx"): + keyring.set_keyring(LeapCryptedFileKeyring(seed=seed)) + keyring.set_password('leap', key, value) + + +def leap_get_password(key, seed="xxx"): + keyring.set_keyring(LeapCryptedFileKeyring(seed=seed)) + return keyring.get_password('leap', key) + + +if __name__ == "__main__": + leap_set_password('test', 'bar') + passwd = leap_get_password('test') + assert passwd == 'bar' -- cgit v1.2.3 From 0576427f697fa631d8f5cc2fc596c60bce38dda3 Mon Sep 17 00:00:00 2001 From: kali Date: Sat, 6 Oct 2012 01:51:51 +0900 Subject: pep8 --- src/leap/crypto/leapkeyring.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/leapkeyring.py b/src/leap/crypto/leapkeyring.py index 394142db..bb0ca147 100644 --- a/src/leap/crypto/leapkeyring.py +++ b/src/leap/crypto/leapkeyring.py @@ -9,7 +9,8 @@ import keyring # No, seriously. # We're affected by this **bug** -# https://bitbucket.org/kang/python-keyring-lib/issue/65/dbusexception-method-opensession-with +# https://bitbucket.org/kang/python-keyring-lib/ +# issue/65/dbusexception-method-opensession-with # so using the gnome keyring does not seem feasible right now. # I thought this was the next best option to store secrets in plain sight. -- cgit v1.2.3 From dd446094737ec8274f8c5c3d59ab1466e2ebacc0 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 11 Oct 2012 03:56:46 +0900 Subject: make file_path explicit --- src/leap/crypto/leapkeyring.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/leapkeyring.py b/src/leap/crypto/leapkeyring.py index bb0ca147..bceadc75 100644 --- a/src/leap/crypto/leapkeyring.py +++ b/src/leap/crypto/leapkeyring.py @@ -1,7 +1,7 @@ -import os - import keyring +from leap.base.config import get_config_file + ############# # Disclaimer ############# @@ -20,7 +20,11 @@ import keyring class LeapCryptedFileKeyring(keyring.backend.CryptedFileKeyring): - filename = os.path.expanduser("~/.config/leap/.secrets") + filename = ".secrets" + + @property + def file_path(self): + return get_config_file(self.filename) def __init__(self, seed=None): self.seed = seed -- cgit v1.2.3 From e1dbfc454180a77ebb38ecae6244ac4abe6d0ac5 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 18 Oct 2012 09:30:53 +0900 Subject: catch cert verification errors and ask user for trust with a little helper function using gnutls --- src/leap/crypto/certs.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/leap/crypto/certs.py (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py new file mode 100644 index 00000000..aa1fc9e9 --- /dev/null +++ b/src/leap/crypto/certs.py @@ -0,0 +1,31 @@ +import ctypes +import socket + +import gnutls.connection +import gnutls.library + + +def get_https_cert_fingerprint(domain): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cred = gnutls.connection.X509Credentials() + + session = gnutls.connection.ClientSession(sock, cred) + session.connect((domain, 443)) + session.handshake() + cert = session.peer_certificate + + _buffer = ctypes.create_string_buffer(20) + buffer_length = ctypes.c_size_t(20) + + gnutls.library.functions.gnutls_x509_crt_get_fingerprint( + cert._c_object, gnutls.library.constants.GNUTLS_DIG_SHA1, # 3 + ctypes.byref(_buffer), ctypes.byref(buffer_length)) + + # deinit + #server_cert._X509Certificate__deinit(server_cert._c_object) + # needed? is segfaulting + + fpr = ctypes.string_at(_buffer, buffer_length.value) + hex_fpr = u":".join(u"%02X" % ord(char) for char in fpr) + + return hex_fpr -- cgit v1.2.3 From 7fa82fb4744ee5cc2c859c75cfd05cc3304c9282 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 08:17:49 +0900 Subject: add more digest functions separate get_cert and get_fingerprint functions added separator --- src/leap/crypto/certs.py | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py index aa1fc9e9..ac9bd357 100644 --- a/src/leap/crypto/certs.py +++ b/src/leap/crypto/certs.py @@ -5,7 +5,10 @@ import gnutls.connection import gnutls.library -def get_https_cert_fingerprint(domain): +def get_https_cert_from_domain(domain): + """ + @param domain: a domain name to get a certificate from. + """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) cred = gnutls.connection.X509Credentials() @@ -13,12 +16,36 @@ def get_https_cert_fingerprint(domain): session.connect((domain, 443)) session.handshake() cert = session.peer_certificate - - _buffer = ctypes.create_string_buffer(20) - buffer_length = ctypes.c_size_t(20) + return cert + + +def get_https_cert_fingerprint(domain, hash_type="SHA256", sep=":"): + """ + @param domain: a domain name to get a fingerprint from + @type domain: str + @param hash_type: the hash function to be used in the fingerprint. + must be one of SHA1, SHA224, SHA256, SHA384, SHA512 + @type hash_type: str + @rparam: hex_fpr, a hexadecimal representation of a bytestring + containing the fingerprint. + @rtype: string + """ + cert = get_https_cert_from_domain(domain) + + _buffer = ctypes.create_string_buffer(64) + buffer_length = ctypes.c_size_t(64) + + SUPPORTED_DIGEST_FUN = ("SHA1", "SHA224", "SHA256", "SHA384", "SHA512") + if hash_type in SUPPORTED_DIGEST_FUN: + digestfunction = getattr( + gnutls.library.constants, + "GNUTLS_DIG_%s" % hash_type) + else: + # XXX improperlyconfigured or something + raise Exception("digest function not supported") gnutls.library.functions.gnutls_x509_crt_get_fingerprint( - cert._c_object, gnutls.library.constants.GNUTLS_DIG_SHA1, # 3 + cert._c_object, digestfunction, ctypes.byref(_buffer), ctypes.byref(buffer_length)) # deinit @@ -26,6 +53,9 @@ def get_https_cert_fingerprint(domain): # needed? is segfaulting fpr = ctypes.string_at(_buffer, buffer_length.value) - hex_fpr = u":".join(u"%02X" % ord(char) for char in fpr) + hex_fpr = sep.join(u"%02X" % ord(char) for char in fpr) return hex_fpr + +#if __name__ == "__main__": + #print get_https_cert_fingerprint('springbok') -- cgit v1.2.3 From 634030e5bba3fe7c2ea3632fff252a60b471487a Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 19 Oct 2012 09:05:14 +0900 Subject: ca cert fingerprint check + api cert verification --- src/leap/crypto/certs.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py index ac9bd357..8908865d 100644 --- a/src/leap/crypto/certs.py +++ b/src/leap/crypto/certs.py @@ -2,6 +2,7 @@ import ctypes import socket import gnutls.connection +import gnutls.crypto import gnutls.library @@ -19,10 +20,19 @@ def get_https_cert_from_domain(domain): return cert -def get_https_cert_fingerprint(domain, hash_type="SHA256", sep=":"): +def get_cert_from_file(filepath): + with open(filepath) as f: + cert = gnutls.crypto.X509Certificate(f.read()) + return cert + + +def get_cert_fingerprint(domain=None, filepath=None, + hash_type="SHA256", sep=":"): """ @param domain: a domain name to get a fingerprint from @type domain: str + @param filepath: path to a file containing a PEM file + @type filepath: str @param hash_type: the hash function to be used in the fingerprint. must be one of SHA1, SHA224, SHA256, SHA384, SHA512 @type hash_type: str @@ -30,7 +40,10 @@ def get_https_cert_fingerprint(domain, hash_type="SHA256", sep=":"): containing the fingerprint. @rtype: string """ - cert = get_https_cert_from_domain(domain) + if domain: + cert = get_https_cert_from_domain(domain) + if filepath: + cert = get_cert_from_file(filepath) _buffer = ctypes.create_string_buffer(64) buffer_length = ctypes.c_size_t(64) @@ -56,6 +69,3 @@ def get_https_cert_fingerprint(domain, hash_type="SHA256", sep=":"): hex_fpr = sep.join(u"%02X" % ord(char) for char in fpr) return hex_fpr - -#if __name__ == "__main__": - #print get_https_cert_fingerprint('springbok') -- cgit v1.2.3 From 0590991d7777de473a7df21ed32e1fa7caa9cf4b Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 26 Oct 2012 00:12:08 +0900 Subject: user credentials saved on login/signup branches. cert request is using magick decorator that retrieves the certificates using srp. --- src/leap/crypto/leapkeyring.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/leapkeyring.py b/src/leap/crypto/leapkeyring.py index bceadc75..d4be7bf9 100644 --- a/src/leap/crypto/leapkeyring.py +++ b/src/leap/crypto/leapkeyring.py @@ -59,6 +59,7 @@ def leap_set_password(key, value, seed="xxx"): def leap_get_password(key, seed="xxx"): keyring.set_keyring(LeapCryptedFileKeyring(seed=seed)) + #import ipdb;ipdb.set_trace() return keyring.get_password('leap', key) -- cgit v1.2.3 From 490cde9c33039c2c5b16d929d6f8bb8e8f06f430 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 5 Dec 2012 23:50:08 +0900 Subject: tests for firstrun/wizard --- src/leap/crypto/leapkeyring.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/leapkeyring.py b/src/leap/crypto/leapkeyring.py index d4be7bf9..c241d0bc 100644 --- a/src/leap/crypto/leapkeyring.py +++ b/src/leap/crypto/leapkeyring.py @@ -53,6 +53,7 @@ class LeapCryptedFileKeyring(keyring.backend.CryptedFileKeyring): def leap_set_password(key, value, seed="xxx"): + key, value = map(unicode, (key, value)) keyring.set_keyring(LeapCryptedFileKeyring(seed=seed)) keyring.set_password('leap', key, value) -- cgit v1.2.3 From 914a07aaf8ef52b2eaf88f1bf01fb6f72adcac5a Mon Sep 17 00:00:00 2001 From: kali Date: Sat, 15 Dec 2012 02:25:12 +0900 Subject: use gnutls to parse pemfiles --- src/leap/crypto/certs.py | 42 ++++++++++++++++++++++++++++++++++--- src/leap/crypto/tests/__init__.py | 0 src/leap/crypto/tests/test_certs.py | 11 ++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/leap/crypto/tests/__init__.py create mode 100644 src/leap/crypto/tests/test_certs.py (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py index 8908865d..45d7326d 100644 --- a/src/leap/crypto/certs.py +++ b/src/leap/crypto/certs.py @@ -1,10 +1,14 @@ import ctypes +from StringIO import StringIO +import re import socket import gnutls.connection import gnutls.crypto import gnutls.library +from leap.util.misc import null_check + def get_https_cert_from_domain(domain): """ @@ -20,12 +24,44 @@ def get_https_cert_from_domain(domain): return cert -def get_cert_from_file(filepath): - with open(filepath) as f: - cert = gnutls.crypto.X509Certificate(f.read()) +def get_cert_from_file(_file): + getcert = lambda f: gnutls.crypto.X509Certificate(f.read()) + if isinstance(_file, str): + with open(_file) as f: + cert = getcert(f) + else: + cert = getcert(_file) return cert +def get_pkey_from_file(_file): + getkey = lambda f: gnutls.crypto.X509PrivateKey(f.read()) + if isinstance(_file, str): + with open(_file) as f: + key = getkey(f) + else: + key = getkey(_file) + return key + + +def can_load_cert_and_pkey(string): + try: + f = StringIO(string) + cert = get_cert_from_file(f) + + f = StringIO(string) + key = get_pkey_from_file(f) + + null_check(cert, 'certificate') + null_check(key, 'private key') + except: + # XXX catch GNUTLSError + raise + return False + else: + return True + + def get_cert_fingerprint(domain=None, filepath=None, hash_type="SHA256", sep=":"): """ diff --git a/src/leap/crypto/tests/__init__.py b/src/leap/crypto/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/crypto/tests/test_certs.py b/src/leap/crypto/tests/test_certs.py new file mode 100644 index 00000000..4d167c51 --- /dev/null +++ b/src/leap/crypto/tests/test_certs.py @@ -0,0 +1,11 @@ +import unittest + + +class CertTestCase(unittest.TestCase): + + def test_load_client_and_pkey(self): + self.fail('not implemented') + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 20f779b644a551bf56cb735868c55cd50d7c3610 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 18 Dec 2012 21:07:06 +0900 Subject: catch gnutls error while validating pemfile --- src/leap/crypto/certs.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py index 45d7326d..78f49fb0 100644 --- a/src/leap/crypto/certs.py +++ b/src/leap/crypto/certs.py @@ -1,6 +1,5 @@ import ctypes from StringIO import StringIO -import re import socket import gnutls.connection @@ -10,6 +9,10 @@ import gnutls.library from leap.util.misc import null_check +class BadCertError(Exception): + """raised for malformed certs""" + + def get_https_cert_from_domain(domain): """ @param domain: a domain name to get a certificate from. @@ -55,9 +58,8 @@ def can_load_cert_and_pkey(string): null_check(cert, 'certificate') null_check(key, 'private key') except: - # XXX catch GNUTLSError - raise - return False + # XXX catch GNUTLSError? + raise BadCertError else: return True -- cgit v1.2.3 From e98c3cc5fad75bea038dc67238e5ce85d701b1e1 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Dec 2012 02:50:52 +0900 Subject: fix broken tests --- src/leap/crypto/tests/test_certs.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/tests/test_certs.py b/src/leap/crypto/tests/test_certs.py index 4d167c51..e476b630 100644 --- a/src/leap/crypto/tests/test_certs.py +++ b/src/leap/crypto/tests/test_certs.py @@ -1,10 +1,21 @@ import unittest +from leap.testing.https_server import where +from leap.crypto import certs + class CertTestCase(unittest.TestCase): - def test_load_client_and_pkey(self): - self.fail('not implemented') + def test_can_load_client_and_pkey(self): + with open(where('leaptestscert.pem')) as cf: + cs = cf.read() + with open(where('leaptestskey.pem')) as kf: + ks = kf.read() + certs.can_load_cert_and_pkey(cs + ks) + + with self.assertRaises(certs.BadCertError): + # screw header + certs.can_load_cert_and_pkey(cs.replace("BEGIN", "BEGINN") + ks) if __name__ == "__main__": -- cgit v1.2.3 From 68af5b2f807ac8acd9525d46d37cfd2a28a06b47 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 16 Jan 2013 23:33:46 +0900 Subject: fix ctypes dependency discovery for libgnutls --- src/leap/crypto/__init__.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/__init__.py b/src/leap/crypto/__init__.py index e69de29b..8df3fc6c 100644 --- a/src/leap/crypto/__init__.py +++ b/src/leap/crypto/__init__.py @@ -0,0 +1,26 @@ +""" +DEBUG! ----------- gnutls lib: libgnutls.26.dylib +DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls.26.dylib +DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls.26.dylib +DEBUG! ----------- gnutls lib: libgnutls-extra.26.dylib +DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls-extra.26.dylib +DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls-extra.26.dylib +""" +import sys + +# hackaround pyinstaller ctypes dependencies discovery +# See: +# http://www.pyinstaller.org/wiki/Features/CtypesDependencySupport#SolutioninPyInstaller +# gnutls.library.load_library is using a style of dep loading +# unsupported by pyinstaller. So we force these imports here. + +if sys.platform == "darwin": + from ctypes import CDLL + try: + CDLL("libgnutls.26.dylib") + except OSError: + pass + try: + CDLL("libgnutls-extra.26.dylib") + except OSError: + pass -- cgit v1.2.3 From 39430cf8c1f9b7118586b1a1f232168fb2d5730b Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 07:36:51 +0900 Subject: switch dependency to pyopenssl --- src/leap/crypto/certs.py | 86 ++++++++++++++---------------- src/leap/crypto/certs_gnutls.py | 112 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 48 deletions(-) create mode 100644 src/leap/crypto/certs_gnutls.py (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py index 78f49fb0..c2835878 100644 --- a/src/leap/crypto/certs.py +++ b/src/leap/crypto/certs.py @@ -1,44 +1,53 @@ -import ctypes +import logging +import os from StringIO import StringIO -import socket +import ssl -import gnutls.connection -import gnutls.crypto -import gnutls.library +from OpenSSL import crypto from leap.util.misc import null_check +logger = logging.getLogger(__name__) + class BadCertError(Exception): - """raised for malformed certs""" + """ + raised for malformed certs + """ -def get_https_cert_from_domain(domain): +class NoCertError(Exception): """ - @param domain: a domain name to get a certificate from. + raised for cert not found in given path """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - cred = gnutls.connection.X509Credentials() - session = gnutls.connection.ClientSession(sock, cred) - session.connect((domain, 443)) - session.handshake() - cert = session.peer_certificate - return cert + +def get_https_cert_from_domain(domain, port=443): + """ + @param domain: a domain name to get a certificate from. + """ + cert = ssl.get_server_certificate((domain, port)) + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + return x509 def get_cert_from_file(_file): - getcert = lambda f: gnutls.crypto.X509Certificate(f.read()) + null_check(_file, "pem file") if isinstance(_file, str): + if not os.path.isfile(_file): + raise NoCertError with open(_file) as f: - cert = getcert(f) + cert = f.read() else: - cert = getcert(_file) - return cert + cert = _file.read() + x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert) + return x509 def get_pkey_from_file(_file): - getkey = lambda f: gnutls.crypto.X509PrivateKey(f.read()) + getkey = lambda f: crypto.load_privatekey( + crypto.FILETYPE_PEM, f.read()) + if isinstance(_file, str): with open(_file) as f: key = getkey(f) @@ -48,6 +57,10 @@ def get_pkey_from_file(_file): def can_load_cert_and_pkey(string): + """ + loads certificate and private key from + a buffer + """ try: f = StringIO(string) cert = get_cert_from_file(f) @@ -57,14 +70,14 @@ def can_load_cert_and_pkey(string): null_check(cert, 'certificate') null_check(key, 'private key') - except: - # XXX catch GNUTLSError? + except Exception as exc: + logger.error(type(exc), exc.message) raise BadCertError else: return True -def get_cert_fingerprint(domain=None, filepath=None, +def get_cert_fingerprint(domain=None, port=443, filepath=None, hash_type="SHA256", sep=":"): """ @param domain: a domain name to get a fingerprint from @@ -79,31 +92,8 @@ def get_cert_fingerprint(domain=None, filepath=None, @rtype: string """ if domain: - cert = get_https_cert_from_domain(domain) + cert = get_https_cert_from_domain(domain, port=port) if filepath: cert = get_cert_from_file(filepath) - - _buffer = ctypes.create_string_buffer(64) - buffer_length = ctypes.c_size_t(64) - - SUPPORTED_DIGEST_FUN = ("SHA1", "SHA224", "SHA256", "SHA384", "SHA512") - if hash_type in SUPPORTED_DIGEST_FUN: - digestfunction = getattr( - gnutls.library.constants, - "GNUTLS_DIG_%s" % hash_type) - else: - # XXX improperlyconfigured or something - raise Exception("digest function not supported") - - gnutls.library.functions.gnutls_x509_crt_get_fingerprint( - cert._c_object, digestfunction, - ctypes.byref(_buffer), ctypes.byref(buffer_length)) - - # deinit - #server_cert._X509Certificate__deinit(server_cert._c_object) - # needed? is segfaulting - - fpr = ctypes.string_at(_buffer, buffer_length.value) - hex_fpr = sep.join(u"%02X" % ord(char) for char in fpr) - + hex_fpr = cert.digest(hash_type) return hex_fpr diff --git a/src/leap/crypto/certs_gnutls.py b/src/leap/crypto/certs_gnutls.py new file mode 100644 index 00000000..20c0e043 --- /dev/null +++ b/src/leap/crypto/certs_gnutls.py @@ -0,0 +1,112 @@ +''' +We're using PyOpenSSL now + +import ctypes +from StringIO import StringIO +import socket + +import gnutls.connection +import gnutls.crypto +import gnutls.library + +from leap.util.misc import null_check + + +class BadCertError(Exception): + """raised for malformed certs""" + + +def get_https_cert_from_domain(domain): + """ + @param domain: a domain name to get a certificate from. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cred = gnutls.connection.X509Credentials() + + session = gnutls.connection.ClientSession(sock, cred) + session.connect((domain, 443)) + session.handshake() + cert = session.peer_certificate + return cert + + +def get_cert_from_file(_file): + getcert = lambda f: gnutls.crypto.X509Certificate(f.read()) + if isinstance(_file, str): + with open(_file) as f: + cert = getcert(f) + else: + cert = getcert(_file) + return cert + + +def get_pkey_from_file(_file): + getkey = lambda f: gnutls.crypto.X509PrivateKey(f.read()) + if isinstance(_file, str): + with open(_file) as f: + key = getkey(f) + else: + key = getkey(_file) + return key + + +def can_load_cert_and_pkey(string): + try: + f = StringIO(string) + cert = get_cert_from_file(f) + + f = StringIO(string) + key = get_pkey_from_file(f) + + null_check(cert, 'certificate') + null_check(key, 'private key') + except: + # XXX catch GNUTLSError? + raise BadCertError + else: + return True + +def get_cert_fingerprint(domain=None, filepath=None, + hash_type="SHA256", sep=":"): + """ + @param domain: a domain name to get a fingerprint from + @type domain: str + @param filepath: path to a file containing a PEM file + @type filepath: str + @param hash_type: the hash function to be used in the fingerprint. + must be one of SHA1, SHA224, SHA256, SHA384, SHA512 + @type hash_type: str + @rparam: hex_fpr, a hexadecimal representation of a bytestring + containing the fingerprint. + @rtype: string + """ + if domain: + cert = get_https_cert_from_domain(domain) + if filepath: + cert = get_cert_from_file(filepath) + + _buffer = ctypes.create_string_buffer(64) + buffer_length = ctypes.c_size_t(64) + + SUPPORTED_DIGEST_FUN = ("SHA1", "SHA224", "SHA256", "SHA384", "SHA512") + if hash_type in SUPPORTED_DIGEST_FUN: + digestfunction = getattr( + gnutls.library.constants, + "GNUTLS_DIG_%s" % hash_type) + else: + # XXX improperlyconfigured or something + raise Exception("digest function not supported") + + gnutls.library.functions.gnutls_x509_crt_get_fingerprint( + cert._c_object, digestfunction, + ctypes.byref(_buffer), ctypes.byref(buffer_length)) + + # deinit + #server_cert._X509Certificate__deinit(server_cert._c_object) + # needed? is segfaulting + + fpr = ctypes.string_at(_buffer, buffer_length.value) + hex_fpr = sep.join(u"%02X" % ord(char) for char in fpr) + + return hex_fpr +''' -- cgit v1.2.3 From 9cdc193c587631986e579c1ba37a8b982be01238 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 18:47:41 +0900 Subject: all tests green again plus: * added soledad test requirements * removed soledad from run_tests run (+1K tests failing) * added option to run All tests to run_tests script * pep8 cleanup --- src/leap/crypto/__init__.py | 26 -------------------------- 1 file changed, 26 deletions(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/__init__.py b/src/leap/crypto/__init__.py index 8df3fc6c..e69de29b 100644 --- a/src/leap/crypto/__init__.py +++ b/src/leap/crypto/__init__.py @@ -1,26 +0,0 @@ -""" -DEBUG! ----------- gnutls lib: libgnutls.26.dylib -DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls.26.dylib -DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls.26.dylib -DEBUG! ----------- gnutls lib: libgnutls-extra.26.dylib -DEBUG! ----------- gnutls lib: /usr/local/lib/libgnutls-extra.26.dylib -DEBUG! ----------- gnutls lib: /opt/local/lib/libgnutls-extra.26.dylib -""" -import sys - -# hackaround pyinstaller ctypes dependencies discovery -# See: -# http://www.pyinstaller.org/wiki/Features/CtypesDependencySupport#SolutioninPyInstaller -# gnutls.library.load_library is using a style of dep loading -# unsupported by pyinstaller. So we force these imports here. - -if sys.platform == "darwin": - from ctypes import CDLL - try: - CDLL("libgnutls.26.dylib") - except OSError: - pass - try: - CDLL("libgnutls-extra.26.dylib") - except OSError: - pass -- cgit v1.2.3 From 19da34c598ce6db172c1e1a8978bf031fc6db89b Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 24 Jan 2013 20:07:06 +0900 Subject: check cert time_boundaries uses pyOpenSSL I had missed this one while deprecating gnutls --- src/leap/crypto/certs.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/leap/crypto') diff --git a/src/leap/crypto/certs.py b/src/leap/crypto/certs.py index c2835878..cbb5725a 100644 --- a/src/leap/crypto/certs.py +++ b/src/leap/crypto/certs.py @@ -2,7 +2,9 @@ import logging import os from StringIO import StringIO import ssl +import time +from dateutil.parser import parse from OpenSSL import crypto from leap.util.misc import null_check @@ -33,7 +35,7 @@ def get_https_cert_from_domain(domain, port=443): def get_cert_from_file(_file): null_check(_file, "pem file") - if isinstance(_file, str): + if isinstance(_file, (str, unicode)): if not os.path.isfile(_file): raise NoCertError with open(_file) as f: @@ -97,3 +99,14 @@ def get_cert_fingerprint(domain=None, port=443, filepath=None, cert = get_cert_from_file(filepath) hex_fpr = cert.digest(hash_type) return hex_fpr + + +def get_time_boundaries(certfile): + cert = get_cert_from_file(certfile) + null_check(cert, 'certificate') + + fromts, tots = (cert.get_notBefore(), cert.get_notAfter()) + from_, to_ = map( + lambda ts: time.gmtime(time.mktime(parse(ts).timetuple())), + (fromts, tots)) + return from_, to_ -- cgit v1.2.3