]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blobdiff - ctypescrypto/x509.py
Python 3 support for modules pbkdf2 pkey ec x509
[oss/ctypescrypto.git] / ctypescrypto / x509.py
index 40263cffea5d1e26528bb989231a92808bfc86a2..35ab1fee27faeb47b199e62385df1239828f02f1 100644 (file)
@@ -8,14 +8,14 @@ such as CMS, OCSP and timestamps.
 
 
 
-from ctypes import c_void_p, c_long, c_int, POINTER, c_char_p, Structure, cast
+from ctypes import c_void_p, c_long, c_ulong, c_int, POINTER, c_char_p, Structure, cast
 from ctypescrypto.bio import Membio
 from ctypescrypto.pkey import PKey
 from ctypescrypto.oid import Oid
 from ctypescrypto.exception import LibCryptoError
-from ctypescrypto import libcrypto
+from ctypescrypto import libcrypto, pyver, chartype, inttype, bintype
 from datetime import datetime
-
+import sys
 try:
     from pytz import utc
 except ImportError:
@@ -38,43 +38,85 @@ except ImportError:
 
 __all__ = ['X509', 'X509Error', 'X509Name', 'X509Store', 'StackOfX509']
 
-class _validity(Structure):
-    """ ctypes representation of X509_VAL structure
-        needed to access certificate validity period, because openssl
-        doesn't provide fuctions for it - only macros
-    """
-    _fields_ = [('notBefore', c_void_p), ('notAfter', c_void_p)]
-
-class _cinf(Structure):
-    """ ctypes representtion of X509_CINF structure
-        neede to access certificate data, which are accessable only
-        via macros
-    """
-    _fields_ = [('version', c_void_p),
-                ('serialNumber', c_void_p),
-                ('sign_alg', c_void_p),
-                ('issuer', c_void_p),
-                ('validity', POINTER(_validity)),
-                ('subject', c_void_p),
-                ('pubkey', c_void_p),
-                ('issuerUID', c_void_p),
-                ('subjectUID', c_void_p),
-                ('extensions', c_void_p),
-               ]
-
-class _x509(Structure):
-    """
-    ctypes represntation of X509 structure needed
-    to access certificate data which are accesable only via
-    macros, not functions
-    """
-    _fields_ = [('cert_info', POINTER(_cinf)),
-                ('sig_alg', c_void_p),
-                ('signature', c_void_p),
-                # There are a lot of parsed extension fields there
-               ]
-_px509 = POINTER(_x509)
-
+if hasattr(libcrypto,"X509_get_version"):
+    
+# If it is OpenSSL 1.1 or above, use accessor functions
+    _X509_get_version = libcrypto.X509_get_version
+    _X509_get_version.restype = c_long
+    _X509_get_version.argtypes = (c_void_p,)
+
+    _X509_get_notBefore=libcrypto.X509_getm_notBefore
+    _X509_get_notBefore.restype = c_void_p
+    _X509_get_notBefore.argtypes = (c_void_p,)
+
+    _X509_get_notAfter=libcrypto.X509_getm_notAfter
+    _X509_get_notAfter.restype = c_void_p
+    _X509_get_notAfter.argtypes = (c_void_p,)
+else:
+    # Otherwise declare X509 structure internals and define deep poke
+    # functions
+    class _validity(Structure):
+        """ ctypes representation of X509_VAL structure
+            needed to access certificate validity period, because openssl
+            doesn't provide fuctions for it - only macros
+        """
+        _fields_ = [('notBefore', c_void_p), ('notAfter', c_void_p)]
+
+    class _cinf(Structure):
+        """ ctypes representtion of X509_CINF structure
+            neede to access certificate data, which are accessable only
+            via macros
+        """
+        _fields_ = [('version', c_void_p),
+                    ('serialNumber', c_void_p),
+                    ('sign_alg', c_void_p),
+                    ('issuer', c_void_p),
+                    ('validity', POINTER(_validity)),
+                    ('subject', c_void_p),
+                    ('pubkey', c_void_p),
+                    ('issuerUID', c_void_p),
+                    ('subjectUID', c_void_p),
+                    ('extensions', c_void_p),
+                ]
+
+    class _x509(Structure):
+        """
+        ctypes represntation of X509 structure needed
+        to access certificate data which are accesable only via
+        macros, not functions
+        """
+        _fields_ = [('cert_info', POINTER(_cinf)),
+                    ('sig_alg', c_void_p),
+                    ('signature', c_void_p),
+                    # There are a lot of parsed extension fields there
+                ]
+    _px509 = POINTER(_x509)
+    def _X509_get_version(ptr):
+        asn1int = cast(ptr, _px509)[0].cert_info[0].version  
+        return libcrypto.ASN1_INTEGER_get(asn1int)
+
+    def _X509_get_notBefore(ptr):   
+        # (x)->cert_info->validity->notBefore
+        return cast(ptr, _px509)[0].cert_info[0].validity[0].notBefore
+    def _X509_get_notAfter(ptr):
+        return cast(ptr, _px509)[0].cert_info[0].validity[0].notAfter
+
+if hasattr(libcrypto,'sk_num'):
+    sk_num = libcrypto.sk_num
+    sk_set = libcrypto.sk_set
+    sk_value = libcrypto.sk_value
+    sk_delete = libcrypto.sk_delete
+    sk_new_null = libcrypto.sk_new_null
+    sk_pop_free = libcrypto.sk_pop_free
+    sk_push = libcrypto.sk_push
+else:
+    sk_num = libcrypto.OPENSSL_sk_num
+    sk_set = libcrypto.OPENSSL_sk_set
+    sk_value = libcrypto.OPENSSL_sk_value
+    sk_delete = libcrypto.OPENSSL_sk_delete
+    sk_new_null = libcrypto.OPENSSL_sk_new_null
+    sk_pop_free = libcrypto.OPENSSL_sk_pop_free
+    sk_push = libcrypto.OPENSSL_sk_push
 class X509Error(LibCryptoError):
     """
     Exception, generated when some openssl function fail
@@ -118,7 +160,7 @@ class X509Name(object):
         """
         if self.need_free:
             libcrypto.X509_NAME_free(self.ptr)
-    def __str__(self):
+    def __bytes__(self):
         """
         Produces an ascii representation of the name, escaping all
         symbols > 0x80.  Probably it is not what you want, unless
@@ -127,7 +169,7 @@ class X509Name(object):
         bio = Membio()
         libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0,
                                      self.PRINT_FLAG | self.ESC_MSB)
-        return str(bio)
+        return bio.__bytes__()
 
     def __unicode__(self):
         """
@@ -135,7 +177,13 @@ class X509Name(object):
         """
         bio = Membio()
         libcrypto.X509_NAME_print_ex(bio.bio, self.ptr, 0, self.PRINT_FLAG)
-        return unicode(bio)
+        return bio.__unicode__()
+    if pyver == 2:
+        __str__ = __bytes__
+    else:
+        __str__ = __unicode__
+            
+
     def __len__(self):
         """
         return number of components in the name
@@ -148,6 +196,10 @@ class X509Name(object):
         return libcrypto.X509_NAME_cmp(self.ptr, other.ptr)
     def __eq__(self, other):
         return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) == 0
+    def __gt__(self, other):
+        return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) > 0
+    def __lt__(self, other):
+        return libcrypto.X509_NAME_cmp(self.ptr, other.ptr) < 0
 
     def __getitem__(self, key):
         if isinstance(key, Oid):
@@ -159,8 +211,8 @@ class X509Name(object):
             value = libcrypto.X509_NAME_ENTRY_get_data(entry)
             bio = Membio()
             libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
-            return unicode(bio)
-        elif isinstance(key, (int, long)):
+            return chartype(bio)
+        elif isinstance(key, inttype):
             # Return OID, string tuple
             entry = libcrypto.X509_NAME_get_entry(self.ptr, key)
             if entry is None:
@@ -169,7 +221,7 @@ class X509Name(object):
             value = libcrypto.X509_NAME_ENTRY_get_data(entry)
             bio = Membio()
             libcrypto.ASN1_STRING_print_ex(bio.bio, value, self.PRINT_FLAG)
-            return (oid, unicode(bio))
+            return (oid, chartype(bio))
         else:
             raise TypeError("X509 NAME can be indexed by Oids or integers only")
 
@@ -206,14 +258,18 @@ class X509_EXT(object):
             self.ptr = cast(ptr, POINTER(_x509_ext))
     def __del__(self):
         libcrypto.X509_EXTENSION_free(self.ptr)
-    def __str__(self):
+    def __bytes__(self):
         bio = Membio()
         libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
-        return str(bio)
+        return bintype(bio)
     def __unicode__(self):
         bio = Membio()
         libcrypto.X509V3_EXT_print(bio.bio, self.ptr, 0x20010, 0)
-        return unicode(bio)
+        return chartype(bio)
+    if pyver == 2:
+        __str__ = __bytes__
+    else:
+        __str__ = __unicode__
     @property
     def oid(self):
         "Returns OID of the extension"
@@ -327,15 +383,17 @@ class X509(object):
         Frees certificate object
         """
         libcrypto.X509_free(self.cert)
-    def __str__(self):
+    def __bytes__(self):
         """ Returns der string of the certificate """
         bio = Membio()
         if libcrypto.i2d_X509_bio(bio.bio, self.cert) == 0:
             raise X509Error("error serializing certificate")
         return str(bio)
+    if pyver == 2:
+        __str__ = __bytes__
     def __repr__(self):
         """ Returns valid call to the constructor """
-        return "X509(data=" + repr(str(self)) + ",format='DER')"
+        return "X509(data=" + repr(self.pem()) + ",format='PEM')"
     @property
     def pubkey(self):
         """EVP PKEy object of certificate public key"""
@@ -407,21 +465,16 @@ class X509(object):
         certificate version as integer. Really certificate stores 0 for
         version 1 and 2 for version 3, but we return 1 and 3
         """
-        asn1int = cast(self.cert, _px509)[0].cert_info[0].version
-        return libcrypto.ASN1_INTEGER_get(asn1int) + 1
+        return _X509_get_version(self.cert) + 1
     @property
     def startDate(self):
         """ Certificate validity period start date """
-        # Need deep poke into certificate structure
-        # (x)->cert_info->validity->notBefore
-        asn1 = cast(self.cert, _px509)[0].cert_info[0].validity[0].notBefore
+        asn1 = _X509_get_notBefore(self.cert)
         return __asn1date_to_datetime(asn1)
     @property
     def endDate(self):
         """ Certificate validity period end date """
-        # Need deep poke into certificate structure
-        # (x)->cert_info->validity->notAfter
-        asn1 = cast(self.cert, _px509)[0].cert_info[0].validity[0].notAfter
+        asn1 = _X509_get_notAfter(self.cert)
         return __asn1date_to_datetime(asn1)
     def check_ca(self):
         """ Returns True if certificate is CA certificate """
@@ -455,14 +508,22 @@ class X509Store(object):
         if lookup is None:
             raise X509Error("error installing file lookup method")
         if file is not None:
-            if not libcrypto.X509_LOOKUP_ctrl(lookup, 1, file, 1, None) > 0:
+            if pyver == 2:
+                fn = file
+            else:
+                fn = file.encode(sys.getfilesystemencoding())
+            if not libcrypto.X509_LOOKUP_ctrl(lookup, 1, fn, 1, None) > 0:
                 raise X509Error("error loading trusted certs from file "+file)
         lookup = libcrypto.X509_STORE_add_lookup(self.store,
                                              libcrypto.X509_LOOKUP_hash_dir())
         if lookup is None:
             raise X509Error("error installing hashed lookup method")
         if dir is not None:
-            if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, dir, 1, None) > 0:
+            if pyver == 2:
+                dr = dir
+            else:
+                dr = dir.encode(sys.getfilesystemencoding())
+            if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, dr, 1, None) > 0:
                 raise X509Error("error adding hashed  trusted certs dir "+dir)
         if default:
             if not libcrypto.X509_LOOKUP_ctrl(lookup, 2, None, 3, None) > 0:
@@ -541,9 +602,10 @@ class StackOfX509(object):
                 freeid. If false, it is just pointer into another
                 structure i.e. CMS_ContentInfo
         """
+        self.need_free = False
         if  ptr is None:
             self.need_free = True
-            self.ptr = libcrypto.sk_new_null()
+            self.ptr = sk_new_null()
             if certs is not None:
                 for crt in certs:
                     self.append(crt)
@@ -553,11 +615,11 @@ class StackOfX509(object):
             self.need_free = disposable
             self.ptr = ptr
     def __len__(self):
-        return libcrypto.sk_num(self.ptr)
+        return sk_num(self.ptr)
     def __getitem__(self, index):
         if index < 0 or index >= len(self):
             raise IndexError
-        p = libcrypto.sk_value(self.ptr, index)
+        p = sk_value(self.ptr, index)
         return X509(ptr=libcrypto.X509_dup(p))
     def __setitem__(self, index, value):
         if not self.need_free:
@@ -565,28 +627,32 @@ class StackOfX509(object):
         if index < 0 or index >= len(self):
             raise IndexError
         if not isinstance(value, X509):
-            raise TypeError('StackOfX508 can contain only X509 objects')
-        p = libcrypto.sk_value(self.ptr, index)
-        libcrypto.sk_set(self.ptr, index, libcrypto.X509_dup(value.cert))
+            raise TypeError('StackOfX509 can contain only X509 objects')
+        p = sk_value(self.ptr, index)
+        sk_set(self.ptr, index, libcrypto.X509_dup(value.cert))
         libcrypto.X509_free(p)
     def __delitem__(self, index):
         if not self.need_free:
             raise ValueError("Stack is read-only")
         if index < 0 or index >= len(self):
             raise IndexError
-        p = libcrypto.sk_delete(self.ptr, index)
+        p = sk_delete(self.ptr, index)
         libcrypto.X509_free(p)
     def __del__(self):
         if self.need_free:
-            libcrypto.sk_pop_free(self.ptr, libcrypto.X509_free)
+            sk_pop_free(self.ptr, libcrypto.X509_free)
     def append(self, value):
         """ Adds certificate to stack """
         if not self.need_free:
             raise ValueError("Stack is read-only")
         if not isinstance(value, X509):
-            raise TypeError('StackOfX508 can contain only X509 objects')
-        libcrypto.sk_push(self.ptr, libcrypto.X509_dup(value.cert))
+            raise TypeError('StackOfX509 can contain only X509 objects')
+        sk_push(self.ptr, libcrypto.X509_dup(value.cert))
 
+libcrypto.d2i_X509_bio.argtypes = (c_void_p,POINTER(c_void_p))
+libcrypto.X509_free.argtypes = (c_void_p,)
+libcrypto.X509_dup.restype = c_void_p
+libcrypto.X509_dup.argtypes = (c_void_p, )
 libcrypto.i2a_ASN1_INTEGER.argtypes = (c_void_p, c_void_p)
 libcrypto.ASN1_STRING_print_ex.argtypes = (c_void_p, c_void_p, c_long)
 libcrypto.PEM_read_bio_X509.restype = c_void_p
@@ -597,35 +663,71 @@ libcrypto.PEM_write_bio_X509.argtypes = (c_void_p, c_void_p)
 libcrypto.ASN1_TIME_print.argtypes = (c_void_p, c_void_p)
 libcrypto.ASN1_INTEGER_get.argtypes = (c_void_p, )
 libcrypto.ASN1_INTEGER_get.restype = c_long
+libcrypto.X509_check_ca.argtypes = (c_void_p, )
 libcrypto.X509_get_serialNumber.argtypes = (c_void_p, )
 libcrypto.X509_get_serialNumber.restype = c_void_p
+libcrypto.X509_get_subject_name.argtypes = (c_void_p, )
+libcrypto.X509_get_subject_name.restype = c_void_p
+libcrypto.X509_get_issuer_name.argtypes = (c_void_p, )
+libcrypto.X509_get_issuer_name.restype = c_void_p
 libcrypto.X509_NAME_ENTRY_get_object.restype = c_void_p
 libcrypto.X509_NAME_ENTRY_get_object.argtypes = (c_void_p, )
+libcrypto.X509_NAME_ENTRY_get_data.restype = c_void_p
+libcrypto.X509_NAME_ENTRY_get_data.argtypes = (c_void_p, )
 libcrypto.OBJ_obj2nid.argtypes = (c_void_p, )
 libcrypto.X509_NAME_get_entry.restype = c_void_p
 libcrypto.X509_NAME_get_entry.argtypes = (c_void_p, c_int)
 libcrypto.X509_STORE_new.restype = c_void_p
 libcrypto.X509_STORE_add_lookup.restype = c_void_p
 libcrypto.X509_STORE_add_lookup.argtypes = (c_void_p, c_void_p)
+libcrypto.X509_STORE_add_cert.argtypes = (c_void_p, c_void_p)
+libcrypto.X509_STORE_CTX_new.restype = c_void_p
+libcrypto.X509_STORE_CTX_free.argtypes = (c_void_p,)
+libcrypto.X509_STORE_CTX_init.argtypes = (c_void_p, c_void_p, c_void_p,
+                                            c_void_p)
+libcrypto.X509_STORE_set_depth.argtypes = (c_void_p, c_int)
+libcrypto.X509_STORE_set_flags.argtypes = (c_void_p, c_ulong)
+libcrypto.X509_STORE_set_purpose.argtypes = (c_void_p, c_int)
 libcrypto.X509_LOOKUP_file.restype = c_void_p
 libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
 libcrypto.X509_LOOKUP_ctrl.restype = c_int
 libcrypto.X509_LOOKUP_ctrl.argtypes = (c_void_p, c_int, c_char_p, c_long,
                                        POINTER(c_char_p))
+libcrypto.X509_EXTENSION_free.argtypes = (c_void_p, )
 libcrypto.X509_EXTENSION_dup.argtypes = (c_void_p, )
 libcrypto.X509_EXTENSION_dup.restype = POINTER(_x509_ext)
 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
                                        c_int)
 libcrypto.X509_get_ext.restype = c_void_p
 libcrypto.X509_get_ext.argtypes = (c_void_p, c_int)
+libcrypto.X509_get_ext_by_critical.argtypes = (c_void_p, c_int, c_int)
+libcrypto.X509_get_ext_by_NID.argtypes = (c_void_p, c_int, c_int)
+libcrypto.X509_get_ext_count.argtypes = (c_void_p, )
+libcrypto.X509_get_pubkey.restype = c_void_p
+libcrypto.X509_get_pubkey.argtypes = (c_void_p, )
 libcrypto.X509V3_EXT_print.argtypes = (c_void_p, POINTER(_x509_ext), c_long,
-                                       c_int)
-libcrypto.sk_set.argtypes = (c_void_p, c_int, c_void_p)
-libcrypto.sk_set.restype = c_void_p
-libcrypto.sk_value.argtypes = (c_void_p, c_int)
-libcrypto.sk_value.restype = c_void_p
-libcrypto.X509_dup.restype = c_void_p
-libcrypto.sk_new_null.restype = c_void_p
-libcrypto.X509_dup.argtypes = (c_void_p, )
+      c_int)
+libcrypto.X509_LOOKUP_file.restype = c_void_p
+libcrypto.X509_LOOKUP_hash_dir.restype = c_void_p
+libcrypto.X509_NAME_cmp.argtypes = (c_void_p, c_void_p)
+libcrypto.X509_NAME_entry_count.argtypes = (c_void_p,) 
+libcrypto.X509_NAME_free.argtypes = (c_void_p,)
+libcrypto.X509_NAME_new.restype = c_void_p
+libcrypto.X509_NAME_print_ex.argtypes = (c_void_p, c_void_p, c_int, c_ulong)
+libcrypto.X509_PURPOSE_get_by_sname.argtypes=(c_char_p,)
+libcrypto.X509_verify.argtypes = (c_void_p, c_void_p)
+libcrypto.X509_verify_cert.argtypes = (c_void_p,)
+sk_num.restype = c_int
+sk_num.argtypes= (c_void_p,)
+sk_set.argtypes = (c_void_p, c_int, c_void_p)
+sk_set.restype = c_void_p
+sk_value.argtypes = (c_void_p, c_int)
+sk_value.restype = c_void_p
+sk_delete.argtypes = (c_void_p, c_int)
+sk_delete.restype = c_void_p
+sk_new_null.restype = c_void_p
+sk_pop_free.argtypes = (c_void_p, c_void_p)
+sk_push.argtypes = (c_void_p, c_void_p)
 libcrypto.X509_NAME_hash.restype = c_long
 libcrypto.X509_NAME_hash.argtypes = (c_void_p, )
+libcrypto.X509_NAME_get_index_by_NID.argtypes = (c_void_p, c_int, c_int)