From abb4673fd8d7e36aa8edb26107116eec589ab07d Mon Sep 17 00:00:00 2001 From: James Kasten Date: Tue, 26 Jun 2012 20:01:52 -0400 Subject: [PATCH] Adding sni_challenge verification --- ca/sni_challenge/make.sh | 9 +++ ca/sni_challenge/sni_support.c | 19 +++++++ ca/sni_challenge/sni_support.h | 13 +++++ ca/sni_challenge/sni_support.i | 13 +++++ ca/sni_challenge/verify_sni_challenge.py | 72 ++++++++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 ca/sni_challenge/make.sh create mode 100644 ca/sni_challenge/sni_support.c create mode 100644 ca/sni_challenge/sni_support.h create mode 100644 ca/sni_challenge/sni_support.i create mode 100644 ca/sni_challenge/verify_sni_challenge.py diff --git a/ca/sni_challenge/make.sh b/ca/sni_challenge/make.sh new file mode 100644 index 000000000..00468964a --- /dev/null +++ b/ca/sni_challenge/make.sh @@ -0,0 +1,9 @@ +#!/bin/bash +#Quick script to compile/load sni_support +#Will change to something more appropriate in the future + +#Modify python path + +swig -python sni_support.i +gcc -fpic -I/home/james/virtualenvs/chocolate/include/python2.7 -c sni_support_wrap.c sni_support.c +gcc -shared sni_support_wrap.o sni_support.o -o _sni_support.so diff --git a/ca/sni_challenge/sni_support.c b/ca/sni_challenge/sni_support.c new file mode 100644 index 000000000..76a11f4fb --- /dev/null +++ b/ca/sni_challenge/sni_support.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include "sni_support.h" + +void set_sni_ext(SSL *ctx, char *servername) { + SSL_set_tlsext_host_name(ctx, servername); +} + +int get_nid(X509_EXTENSION *ext) { + return OBJ_obj2nid(X509_EXTENSION_get_object(ext)); +} + +binary_data_t get_unknown_value(X509_EXTENSION *ext) { + binary_data_t result; + result.size = ext->value->length; + result.data = (unsigned char *)ext->value->data; + return result; +} diff --git a/ca/sni_challenge/sni_support.h b/ca/sni_challenge/sni_support.h new file mode 100644 index 000000000..dc92657f6 --- /dev/null +++ b/ca/sni_challenge/sni_support.h @@ -0,0 +1,13 @@ +#ifndef SNI_SUPPORT_H +#define SNI_SUPPORT_H + +typedef struct binary_data { + int size; + unsigned char* data; +} binary_data_t; + +void set_sni_ext(SSL *ctx, char *servername); +int get_nid(X509_EXTENSION *ext); +binary_data_t get_unknown_value(X509_EXTENSION *ext); + +#endif diff --git a/ca/sni_challenge/sni_support.i b/ca/sni_challenge/sni_support.i new file mode 100644 index 000000000..21d9708a2 --- /dev/null +++ b/ca/sni_challenge/sni_support.i @@ -0,0 +1,13 @@ +%module sni_support +%{ + #include + #include + #include "sni_support.h" +%} + +%typemap(out) binary_data_t { + $result = PyString_FromStringAndSize($1.data,$1.size); +} + +%include "sni_support.h" + diff --git a/ca/sni_challenge/verify_sni_challenge.py b/ca/sni_challenge/verify_sni_challenge.py new file mode 100644 index 000000000..b04248028 --- /dev/null +++ b/ca/sni_challenge/verify_sni_challenge.py @@ -0,0 +1,72 @@ +import M2Crypto +import sni_support +import hmac +import hashlib + +S_SIZE = 20 + +def check(one, two, three, four, five): + print "done" + return 0 + +def byteToHex(byteStr): + return ''.join(["%02X" % ord(x) for x in byteStr]).strip() + +def check_challenge_value(ext_value, sharedSecret): + s = ext_value[0:S_SIZE] + mac = ext_value[S_SIZE:] + expected_mac = hmac.new(sharedSecret, str(s), hashlib.sha256).digest() + + #print "s: ", byteToHex(s) + #print "mac: ", byteToHex(mac) + #print "expected_mac: ", byteToHex(expected_mac) + #print type(mac) + #print type(expected_mac) + + if mac == expected_mac: + return True + return False + +def verify_challenge(address, sharedSecret, encryptedValue): + sni_name = byteToHex(encryptedValue[0]) + ".com" + + context = M2Crypto.SSL.Context() + context.set_allow_unknown_ca(True) + context.set_verify(M2Crypto.SSL.verify_none, 4) + + conn = M2Crypto.SSL.Connection(context) + sni_support.set_sni_ext(conn.ssl, sni_name) + conn.connect((address, 443)) + + cert_chain = conn.get_peer_cert_chain() + + #Ensure certificate chain form is correct + if len(cert_chain) != 1: + return False, "Incorrect number of certificates in chain" + + for i in range(0,cert_chain[0].get_ext_count()): + ext = cert_chain[0].get_ext_at(i) + + if sni_support.get_nid(ext.x509_ext) == 0: + + valid = check_challenge_value(sni_support.get_unknown_value(ext.x509_ext), sharedSecret) + if valid: + return True, "Challenge completed successfully" + else: + return False, "Certificate extension does not check out" + + +def main(): + #Testing the example sni_challenge + from Crypto.PublicKey import RSA + + testkey = RSA.importKey(open("/home/james/Documents/apache_choc/testing.key").read()) + + #the second parameter is ignored + #https://www.dlitz.net/software/pycrypto/api/current/ + encryptedValue = testkey.encrypt('0x12345678', 0) + valid, response = verify_challenge("127.0.0.1", '0x12345678', encryptedValue) + print response + +if __name__ == "__main__": + main()