From a057f8c13029d32e0fec0f6e2f003279e5e7078c Mon Sep 17 00:00:00 2001 From: hakril Date: Sun, 4 Mar 2018 18:28:09 +0100 Subject: [PATCH] class CertificateContext(PCERT_CONTEXT) replaced by Certificate(CERT_CONTEXT) + multi-received encrypt/decrypt test --- samples/crypto/certificate.py | 4 +- samples/crypto/encryption_demo.py | 2 +- tests/test_crypto.py | 92 ++++++++++++++++- windows/crypto/certificate.py | 164 +++++++++++++++++++++--------- windows/crypto/cryptmsg.py | 9 +- windows/crypto/encrypt_decrypt.py | 12 ++- windows/crypto/generation.py | 6 +- windows/utils/pythonutils.py | 4 +- windows/winobject/handle.py | 2 - windows/winobject/wingui.py | 76 -------------- 10 files changed, 223 insertions(+), 148 deletions(-) delete mode 100644 windows/winobject/wingui.py diff --git a/samples/crypto/certificate.py b/samples/crypto/certificate.py index de72c5f..4bf293d 100644 --- a/samples/crypto/certificate.py +++ b/samples/crypto/certificate.py @@ -33,7 +33,7 @@ l2ec6CyjDQc6HcQBNCsbJVq6qGtQbYNE+ih+KhIU4tO5jf25xthf2g== raw_cert = ("".join(windowscert.split("\n")[1:-1])).decode('base64') -cert = windows.crypto.CertificateContext.from_buffer(raw_cert) +cert = windows.crypto.Certificate.from_buffer(raw_cert) print("Analysing certificate: {0}".format(cert)) print(" * name: <{0}>".format(cert.name)) @@ -55,7 +55,7 @@ for i, chain in enumerate(chains): print "" cert_to_verif = ccert print("Looking for <{0}> in trusted certificates".format(cert_to_verif.name)) -root_store = windows.crypto.EHCERTSTORE.from_system_store("Root") +root_store = windows.crypto.CertificateStore.from_system_store("Root") # This is not the correct way verify the validity of a certificate chain. # I would say that if the goal is to verify the signature of the certificate: use wintrust. # (or maybe CertVerifyCertificateChainPolicy : https://msdn.microsoft.com/en-us/library/windows/desktop/aa377163(v=vs.85).aspx) diff --git a/samples/crypto/encryption_demo.py b/samples/crypto/encryption_demo.py index 3edb293..e1735c6 100644 --- a/samples/crypto/encryption_demo.py +++ b/samples/crypto/encryption_demo.py @@ -42,7 +42,7 @@ PFW_TMP_KEY_CONTAINER = "PythonForWindowsTMPContainer" def genkeys(common_name, pfxpassword, outname, keysize=2048, **kwargs): """Generate a SHA256/RSA key pair. A self-signed certificate with 'common_name' is stored as 'outname'.cer. The private key is stored in 'outname'.pfx protected with 'pfxpassword'""" - cert_store = crypto.EHCERTSTORE.new_in_memory() + cert_store = crypto.CertificateStore.new_in_memory() # Create a TMP context that will hold our newly generated key-pair with crypto.CryptContext(PFW_TMP_KEY_CONTAINER, None, PROV_RSA_FULL, 0, retrycreate=True) as ctx: key = HCRYPTKEY() diff --git a/tests/test_crypto.py b/tests/test_crypto.py index c4ee150..7b85ff3 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -2,6 +2,7 @@ import pytest import windows.crypto import windows.generated_def as gdef +import windows.crypto.generation from pfwtest import * @@ -63,17 +64,71 @@ def rawcert(): def rawpfx(): return TEST_PFX.decode("base64") +PFW_TEST_TMP_KEY_CONTAINER = "PythonForWindowsTMPContainerTest" +RANDOM_CERTIF_NAME = "PythonForWindowsGeneratedRandomCertifTest" +RANDOM_PFX_PASSWORD = "PythonForWindowsGeneratedRandomPFXPassword" + +@pytest.fixture() +def randomkeypair(keysize=1024): + """Generate a cert / pfx. Based on samples\crypto\encryption_demo.py""" + cert_store = windows.crypto.CertificateStore.new_in_memory() + # Create a TMP context that will hold our newly generated key-pair + with windows.crypto.CryptContext(PFW_TEST_TMP_KEY_CONTAINER, None, gdef.PROV_RSA_FULL, 0, retrycreate=True) as ctx: + key = gdef.HCRYPTKEY() + keysize_flags = keysize << 16 + # Generate a key-pair that is exportable + windows.winproxy.CryptGenKey(ctx, gdef.AT_KEYEXCHANGE, gdef.CRYPT_EXPORTABLE | keysize_flags, key) + # It does NOT destroy the key-pair from the container, + # It only release the key handle + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379918(v=vs.85).aspx + windows.winproxy.CryptDestroyKey(key) + + # Descrption of the key-container that will be used to generate the certificate + KeyProvInfo = gdef.CRYPT_KEY_PROV_INFO() + KeyProvInfo.pwszContainerName = PFW_TEST_TMP_KEY_CONTAINER + KeyProvInfo.pwszProvName = None + KeyProvInfo.dwProvType = gdef.PROV_RSA_FULL + KeyProvInfo.dwFlags = 0 + KeyProvInfo.cProvParam = 0 + KeyProvInfo.rgProvParam = None + #KeyProvInfo.dwKeySpec = AT_SIGNATURE + KeyProvInfo.dwKeySpec = gdef.AT_KEYEXCHANGE + + crypt_algo = gdef.CRYPT_ALGORITHM_IDENTIFIER() + crypt_algo.pszObjId = gdef.szOID_RSA_SHA256RSA + + certif_name = "CN={0}".format(RANDOM_CERTIF_NAME) + # Generate a self-signed certificate based on the given key-container and signature algorithme + certif = windows.crypto.generation.generate_selfsigned_certificate(certif_name, key_info=KeyProvInfo, signature_algo=crypt_algo) + # Add the newly created certificate to our TMP cert-store + cert_store.add_certificate(certif) + # Generate a pfx from the TMP cert-store + pfx = windows.crypto.generation.generate_pfx(cert_store, RANDOM_PFX_PASSWORD) + yield certif, pfx + # Destroy the TMP key container + prov = gdef.HCRYPTPROV() + windows.winproxy.CryptAcquireContextW(prov, PFW_TEST_TMP_KEY_CONTAINER, None, gdef.PROV_RSA_FULL, gdef.CRYPT_DELETEKEYSET) + + def test_certificate(rawcert): - cert = windows.crypto.CertificateContext.from_buffer(rawcert) + cert = windows.crypto.Certificate.from_buffer(rawcert) assert cert.serial == '1b 8e 94 cb 0b 3e eb b6 41 39 f3 c9 09 b1 6b 46' assert cert.name == 'PythonForWindowsTest' + assert cert.issuer == 'PythonForWindowsTest' + assert cert.thumprint == 'EF 0C A8 C9 F9 E0 96 AF 74 18 56 8B C1 C9 57 27 A0 89 29 6A' + assert cert.encoded == rawcert + assert cert.version == 2 + assert cert == cert + assert cert is cert.duplicate() cert.chains # TODO: craft a certificate with a chain for test purpose + cert.store.certs + cert.properties def test_pfx(rawcert, rawpfx): pfx = windows.crypto.import_pfx(rawpfx, TEST_PFX_PASSWORD) - orig_cert = windows.crypto.CertificateContext.from_buffer(rawcert) + orig_cert = windows.crypto.Certificate.from_buffer(rawcert) certs = pfx.certs assert len(certs) == 1 # Test cert comparaison @@ -87,10 +142,10 @@ def test_open_pfx_bad_password(rawpfx): def test_encrypt_decrypt(rawcert, rawpfx): message_to_encrypt = "Testing message \xff\x01" - cert = windows.crypto.CertificateContext.from_buffer(rawcert) + cert = windows.crypto.Certificate.from_buffer(rawcert) # encrypt should accept a cert or iterable of cert res = windows.crypto.encrypt(cert, message_to_encrypt) - res2 = windows.crypto.encrypt([cert], message_to_encrypt) + res2 = windows.crypto.encrypt([cert, cert], message_to_encrypt) del cert assert message_to_encrypt not in res @@ -102,6 +157,33 @@ def test_encrypt_decrypt(rawcert, rawpfx): assert message_to_encrypt == decrypt assert decrypt == decrypt2 + + +def test_randomkeypair(randomkeypair): + randcert, randrawpfx = randomkeypair + assert randcert.name == RANDOM_CERTIF_NAME + randpfx = windows.crypto.import_pfx(randrawpfx, RANDOM_PFX_PASSWORD) # Check password is good too + + +def test_encrypt_decrypt_multiple_receivers(rawcert, rawpfx, randomkeypair): + message_to_encrypt = "\xff\x00 Testing message \xff\x01" + # Receiver 1: random key pair + randcert, randrawpfx = randomkeypair + randpfx = windows.crypto.import_pfx(randrawpfx, RANDOM_PFX_PASSWORD) + # Receiver 1: PFW-test-keypair + pfx = windows.crypto.import_pfx(rawpfx, TEST_PFX_PASSWORD) + cert = windows.crypto.Certificate.from_buffer(rawcert) + assert cert.name != randcert.name + assert cert.encoded != randcert.encoded + # Encrypt the message with 2 differents certificates + encrypted = windows.crypto.encrypt([cert, randcert], message_to_encrypt) + # Decrypt with each PFX and check the result is valid/the same + decrypted = windows.crypto.decrypt(pfx, encrypted) + decrypted2 = windows.crypto.decrypt(randpfx, encrypted) + assert decrypted == decrypted2 == message_to_encrypt + + + def test_crypt_obj(): path = r"C:\windows\system32\kernel32.dll" x = windows.crypto.CryptObject(path) @@ -111,6 +193,6 @@ def test_crypt_obj(): # TODO: Need some better ideas def test_certificate_from_store(): - return windows.crypto.EHCERTSTORE.from_system_store("Root") + return windows.crypto.CertificateStore.from_system_store("Root") diff --git a/windows/crypto/certificate.py b/windows/crypto/certificate.py index 0bdc61b..a715601 100644 --- a/windows/crypto/certificate.py +++ b/windows/crypto/certificate.py @@ -49,7 +49,7 @@ class CryptObject(object): dwEncoding = gdef.DWORD() dwContentType = gdef.DWORD() dwFormatType = gdef.DWORD() - hStore = EHCERTSTORE() + hStore = CertificateStore() hMsg = windows.crypto.cryptmsg.CryptMessage() winproxy.CryptQueryObject(gdef.CERT_QUERY_OBJECT_FILE, @@ -71,6 +71,8 @@ class CryptObject(object): self.content_type = CRYPT_OBJECT_FORMAT_TYPE_DICT[dwContentType.value] def _signers_and_certs_generator(self): + if self.crypt_msg is None: + return for signer in self.crypt_msg.signers: cert = self.cert_store.find(signer.Issuer, signer.SerialNumber) yield signer, cert @@ -84,13 +86,13 @@ class CryptObject(object): # TODO: rename to CertificateStore ? # https://msdn.microsoft.com/en-us/library/windows/desktop/aa382037(v=vs.85).aspx -class EHCERTSTORE(gdef.HCERTSTORE): +class CertificateStore(gdef.HCERTSTORE): """A certificate store""" @property def certs(self): """The certificates in the store - :type: [:class:`CertificateContext`] -- A list of Certificate + :type: [:class:`Certificate`] -- A list of Certificate """ res = [] last = None @@ -102,7 +104,7 @@ class EHCERTSTORE(gdef.HCERTSTORE): return tuple(res) raise # Need to duplicate as CertEnumCertificatesInStore will free the context 'last' - ecert = windows.crypto.CertificateContext(cert[0]) + ecert = windows.crypto.Certificate.from_pointer(cert) res.append(ecert.duplicate()) last = ecert raise RuntimeError("Out of infinit loop") @@ -113,7 +115,7 @@ class EHCERTSTORE(gdef.HCERTSTORE): @classmethod def from_file(cls, filename): - """Create a new :class:`EHCERTSTORE` from ``filename``""" + """Create a new :class:`CertificateStore` from ``filename``""" res = winproxy.CertOpenStore(gdef.CERT_STORE_PROV_FILENAME_A, DEFAULT_ENCODING, None, gdef.CERT_STORE_OPEN_EXISTING_FLAG, filename) return ctypes.cast(res, cls) @@ -128,7 +130,7 @@ class EHCERTSTORE(gdef.HCERTSTORE): # See https://msdn.microsoft.com/en-us/library/windows/desktop/aa388136(v=vs.85).aspx @classmethod def from_system_store(cls, store_name): - """Create a new :class:`EHCERTSTORE` from system store``store_name`` + """Create a new :class:`CertificateStore` from system store``store_name`` (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa388136(v=vs.85).aspx) """ res = winproxy.CertOpenStore(gdef.CERT_STORE_PROV_SYSTEM_A, DEFAULT_ENCODING, None, gdef.CERT_SYSTEM_STORE_LOCAL_MACHINE | gdef.CERT_STORE_READONLY_FLAG, store_name) @@ -136,7 +138,7 @@ class EHCERTSTORE(gdef.HCERTSTORE): @classmethod def new_in_memory(cls): - """Create a new temporary :class:`EHCERTSTORE` in memory""" + """Create a new temporary :class:`CertificateStore` in memory""" res = winproxy.CertOpenStore(gdef.CERT_STORE_PROV_MEMORY, DEFAULT_ENCODING, None, 0, None) return ctypes.cast(res, cls) @@ -145,15 +147,14 @@ class EHCERTSTORE(gdef.HCERTSTORE): def find(self, issuer, serialnumber): """Return the certificate that match `issuer` and `serialnumber` - :return: :class:`CertificateContext` + :return: :class:`Certificate` """ # data = self.get_signer_data(index) cert_info = gdef.CERT_INFO() cert_info.Issuer = issuer cert_info.SerialNumber = serialnumber rawcertcontext = winproxy.CertFindCertificateInStore(self, DEFAULT_ENCODING, 0, gdef.CERT_FIND_SUBJECT_CERT, ctypes.byref(cert_info), None) - # return rawcertcontext - return CertificateContext(rawcertcontext[0]) + return Certificate.from_pointer(rawcertcontext) # PKCS12_NO_PERSIST_KEY -> do not save it in a key container on disk @@ -176,32 +177,23 @@ def import_pfx(pfx, password=None, flags=gdef.CRYPT_USER_KEYSET | gdef.PKCS12_NO ``PKCS12_NO_PERSIST_KEY`` tells ``CryptoAPI`` to NOT save the keys in a on-disk container. - :return: :class:`EHCERTSTORE` + :return: :class:`CertificateStore` """ - if isinstance(pfx, basestring): + if isinstance(pfx, (basestring, bytearray)): pfx = gdef.CRYPT_DATA_BLOB.from_string(pfx) cert_store = winproxy.PFXImportCertStore(pfx, password, flags) - return EHCERTSTORE(cert_store) + return CertificateStore(cert_store) -# Why PCCERT_CONTEXT (pointer type) and not _CERT_CONTEXT ? -class CertificateContext(gdef.PCCERT_CONTEXT): - """Represent a Certificate. - - note: It is a pointer ctypes structure (``PCCERT_CONTEXT``) - """ - _type_ = gdef.PCCERT_CONTEXT._type_ # Not herited from PCCERT_CONTEXT - - - def __repr__(self): - return '<{0} "{1}" serial="{2}">'.format(type(self).__name__, self.name, self.serial) +class Certificate(gdef.CERT_CONTEXT): + """Represent a Certificate """ @property def raw_serial(self): """The raw serial number of the certificate. :type: [:class:`int`]: A list of int ``0 <= x <= 255``""" - serial_number = self[0].pCertInfo[0].SerialNumber + serial_number = self.pCertInfo[0].SerialNumber return [(c & 0xff) for c in serial_number.pbData[:serial_number.cbData][::-1]] @property @@ -210,7 +202,6 @@ class CertificateContext(gdef.PCCERT_CONTEXT): :type: :class:`str` """ - serial_number = self[0].pCertInfo[0].SerialNumber serial_bytes = self.raw_serial return " ".join("{:02x}".format(x) for x in serial_bytes) @@ -230,6 +221,21 @@ class CertificateContext(gdef.PCCERT_CONTEXT): :type: :class:`str`""" + def raw_hash(self): + size = gdef.DWORD(100) + buffer = ctypes.c_buffer(size.value) + winproxy.CryptHashCertificate(None, 0, 0, self.pbCertEncoded, self.cbCertEncoded, ctypes.cast(buffer, gdef.LPBYTE), size) + return buffer[:size.value] + + @property + def thumprint(self): + """The thumprint of the certificate (which is the sha1 of the encoded cert) with the format: + 'XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX' + + :type: :class:`str` + """ + return " ".join("{:02X}".format(x) for x in bytearray(self.raw_hash())) + @property def issuer(self): """The name of the certificate's issuer. @@ -241,9 +247,9 @@ class CertificateContext(gdef.PCCERT_CONTEXT): def store(self): """The certificate store that contains the certificate - :type: :class:`EHCERTSTORE` + :type: :class:`CertificateStore` """ - return EHCERTSTORE(self[0].hCertStore) + return CertificateStore(self.hCertStore) def get_raw_certificate_chains(self): # Rename to all_chains ? chain_context = EPCCERT_CHAIN_CONTEXT() @@ -260,16 +266,18 @@ class CertificateContext(gdef.PCCERT_CONTEXT): chain_para.cbSize = ctypes.sizeof(chain_para) chain_para.RequestedUsage = cert_usage - winproxy.CertGetCertificateChain(None, self, None, self[0].hCertStore, ctypes.byref(chain_para), 0, None, ctypes.byref(chain_context)) + winproxy.CertGetCertificateChain(None, self, None, self.hCertStore, ctypes.byref(chain_para), 0, None, ctypes.byref(chain_context)) + # Lower chains ? + # winproxy.CertGetCertificateChain(None, self, None, self[0].hCertStore, ctypes.byref(chain_para), 0x80, None, ctypes.byref(chain_context)) #return CertficateChain(chain_context) return chain_context @property # fixedproperty ? def chains(self): - """The list of chain context available for this certificate. Each elements of this list is a list of ``CertificateContext`` that should + """The list of chain context available for this certificate. Each elements of this list is a list of ``Certificate`` that should go from the ``self`` certificate to a trusted certificate. - :type: [[:class:`CertificateContext`]] -- A list of chain (list) of :class:`CertificateContext` + :type: [[:class:`Certificate`]] -- A list of chain (list) of :class:`Certificate` """ chain_context = self.get_raw_certificate_chains() res = [] @@ -286,19 +294,62 @@ class CertificateContext(gdef.PCCERT_CONTEXT): note: The object returned is ``self`` - :return: :class:`CertificateContext` + :return: :class:`Certificate` """ res = winproxy.CertDuplicateCertificateContext(self) # Check what the doc says: the pointer returned is actually the PCERT in parameter # Only the refcount is incremented # This postulate allow us to return 'self' directly # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376045(v=vs.85).aspx - if not ctypes.cast(res, gdef.PVOID).value == ctypes.cast(self, gdef.PVOID).value: + if not ctypes.addressof(res[0]) == ctypes.addressof(self): raise ValueError("CertDuplicateCertificateContext did not returned the argument (check doc)") return self def view(self, title=None): - return windows.winproxy.CryptUIDlgViewContext(gdef.CERT_STORE_CERTIFICATE_CONTEXT, self, None, title, 0, None) + return windows.winproxy.CryptUIDlgViewContext(gdef.CERT_STORE_CERTIFICATE_CONTEXT, ctypes.byref(self), None, title, 0, None) + + KNOWN_PROPERTIES_VALUES = gdef.FlagMapper( + gdef.CERT_KEY_PROV_HANDLE_PROP_ID, + gdef.CERT_KEY_PROV_INFO_PROP_ID, + gdef.CERT_SHA1_HASH_PROP_ID, + gdef.CERT_MD5_HASH_PROP_ID, + gdef.CERT_HASH_PROP_ID, + gdef.CERT_KEY_CONTEXT_PROP_ID, + gdef.CERT_KEY_SPEC_PROP_ID, + gdef.CERT_IE30_RESERVED_PROP_ID, + gdef.CERT_PUBKEY_HASH_RESERVED_PROP_ID, + gdef.CERT_ENHKEY_USAGE_PROP_ID, + gdef.CERT_CTL_USAGE_PROP_ID, + gdef.CERT_NEXT_UPDATE_LOCATION_PROP_ID, + gdef.CERT_FRIENDLY_NAME_PROP_ID, + gdef.CERT_PVK_FILE_PROP_ID, + gdef.CERT_DESCRIPTION_PROP_ID, + gdef.CERT_ACCESS_STATE_PROP_ID, + gdef.CERT_SIGNATURE_HASH_PROP_ID, + gdef.CERT_SMART_CARD_DATA_PROP_ID, + gdef.CERT_EFS_PROP_ID, + gdef.CERT_FORTEZZA_DATA_PROP_ID, + gdef.CERT_ARCHIVED_PROP_ID, + gdef.CERT_KEY_IDENTIFIER_PROP_ID, + gdef.CERT_AUTO_ENROLL_PROP_ID, + gdef.CERT_PUBKEY_ALG_PARA_PROP_ID, + gdef.CERT_CROSS_CERT_DIST_POINTS_PROP_ID, + gdef.CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID, + gdef.CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID, + gdef.CERT_ENROLLMENT_PROP_ID, + gdef.CERT_DATE_STAMP_PROP_ID, + gdef.CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID, + gdef.CERT_SUBJECT_NAME_MD5_HASH_PROP_ID, + gdef.CERT_EXTENDED_ERROR_INFO_PROP_ID, + gdef.CERT_RENEWAL_PROP_ID, + gdef.CERT_ARCHIVED_KEY_HASH_PROP_ID, + gdef.CERT_AUTO_ENROLL_RETRY_PROP_ID, + gdef.CERT_AIA_URL_RETRIEVED_PROP_ID, + gdef.CERT_AUTHORITY_INFO_ACCESS_PROP_ID, + gdef.CERT_BACKED_UP_PROP_ID, + gdef.CERT_OCSP_RESPONSE_PROP_ID, + gdef.CERT_REQUEST_ORIGINATOR_PROP_ID, + gdef.CERT_SOURCE_LOCATION_PROP_ID) def enum_properties(self): prop = 0 @@ -307,7 +358,7 @@ class CertificateContext(gdef.PCCERT_CONTEXT): prop = winproxy.CertEnumCertificateContextProperties(self, prop) if not prop: return res - res.append(prop) + res.append(self.KNOWN_PROPERTIES_VALUES[prop]) raise RuntimeError("Unreachable code") properties = property(enum_properties) @@ -323,43 +374,49 @@ class CertificateContext(gdef.PCCERT_CONTEXT): """The encoded certificate. :type: :class:`bytearray`""" - return bytearray(self[0].pbCertEncoded[:self[0].cbCertEncoded]) + return bytearray(self.pbCertEncoded[:self.cbCertEncoded]) @property def version(self): "TODO: doc" - return self[0].pbCertInfo.dwVersion + return self.pCertInfo[0].dwVersion @classmethod def from_file(cls, filename): - """Create a :class:`CertificateContext` from the file ``filename`` + """Create a :class:`Certificate` from the file ``filename`` - :return: :class:`CertificateContext` + :return: :class:`Certificate` """ with open(filename, "rb") as f: data = f.read() buf = (ctypes.c_ubyte * len(data))(*bytearray(data)) - res = windows.winproxy.CertCreateCertificateContext(windows.crypto.DEFAULT_ENCODING, buf, len(data)) - return ctypes.cast(res, cls) + pcert = windows.winproxy.CertCreateCertificateContext(windows.crypto.DEFAULT_ENCODING, buf, len(data)) + return cls.from_pointer(pcert) @classmethod def from_buffer(cls, data): - """Create a :class:`CertificateContext` from the buffer ``data`` + """Create a :class:`Certificate` from the buffer ``data`` - :return: :class:`CertificateContext` + :return: :class:`Certificate` """ buf = (ctypes.c_ubyte * len(data))(*bytearray(data)) - res = windows.winproxy.CertCreateCertificateContext(windows.crypto.DEFAULT_ENCODING, buf, len(data)) - return ctypes.cast(res, cls) + pcert = windows.winproxy.CertCreateCertificateContext(windows.crypto.DEFAULT_ENCODING, buf, len(data)) + return cls.from_pointer(pcert) + + @classmethod + def from_pointer(self, ptr): + return ctypes.cast(ptr, ctypes.POINTER(Certificate))[0] + def __eq__(self, other): - if not isinstance(other, CertificateContext): + if not isinstance(other, Certificate): return NotImplemented - return windows.winproxy.CertCompareCertificate(DEFAULT_ENCODING, self[0].pCertInfo, other[0].pCertInfo) + return windows.winproxy.CertCompareCertificate(DEFAULT_ENCODING, self.pCertInfo, other.pCertInfo) + + def __repr__(self): + return '<{0} "{1}" serial="{2}">'.format(type(self).__name__, self.name, self.serial) - # CertCompareCertificate ? - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376027(v=vs.85).aspx # class CertficateChain(object): @@ -374,12 +431,19 @@ class CertificateContext(gdef.PCCERT_CONTEXT): # Those classes are more of a POC than anything else +# Should be the struct itself (like Certificate ?) class EPCCERT_CHAIN_CONTEXT(gdef.PCCERT_CHAIN_CONTEXT): _type_ = gdef.PCCERT_CHAIN_CONTEXT._type_ @property def chains(self): res = [] + # if (self[0].cLowerQualityChainContext): + # print("LOL") + # import pdb;pdb.set_trace() + # if self[0].cChain > 1: + # print("HAAAAA") + # import pdb;pdb.set_trace() for i in range(self[0].cChain): simple_chain = ctypes.cast(self[0].rgpChain[i], EPCCERT_SIMPLE_CHAIN) res.append(simple_chain) @@ -411,7 +475,7 @@ class EPCERT_CHAIN_ELEMENT(gdef.PCERT_CHAIN_ELEMENT): @property def cert(self): - return ctypes.cast(self[0].pCertContext, CertificateContext) + return Certificate.from_pointer(self[0].pCertContext) # Move this in another .py ? diff --git a/windows/crypto/cryptmsg.py b/windows/crypto/cryptmsg.py index abdfac1..8b5c01c 100644 --- a/windows/crypto/cryptmsg.py +++ b/windows/crypto/cryptmsg.py @@ -10,12 +10,14 @@ class CryptMessage(gdef.HCRYPTMSG): gdef.CMSG_CERT_COUNT_PARAM: gdef.DWORD} - def get_param(self, param_type, index=0): + def get_param(self, param_type, index=0, raw=False): data_size = gdef.DWORD() # https://msdn.microsoft.com/en-us/library/windows/desktop/aa380227(v=vs.85).aspx winproxy.CryptMsgGetParam(self, param_type, index, None, data_size) buffer = ctypes.c_buffer(data_size.value) winproxy.CryptMsgGetParam(self, param_type, index, buffer, data_size) + if raw: + return (buffer, data_size) if param_type in self.MSG_PARAM_KNOW_TYPES: buffer = self.MSG_PARAM_KNOW_TYPES[param_type].from_buffer(buffer) @@ -23,7 +25,6 @@ class CryptMessage(gdef.HCRYPTMSG): return buffer.value return buffer - # Certificate accessors @property @@ -42,9 +43,9 @@ class CryptMessage(gdef.HCRYPTMSG): note: not all embded certificate are directly used to sign the :class:`CryptObject`. - :return: :class:`CertificateContext` + :return: :class:`Certificate` """ - return windows.crypto.CertificateContext.from_buffer(self.get_raw_cert(index)) + return windows.crypto.Certificate.from_buffer(self.get_raw_cert(index)) @property def certs(self): diff --git a/windows/crypto/encrypt_decrypt.py b/windows/crypto/encrypt_decrypt.py index 681ccba..c416736 100644 --- a/windows/crypto/encrypt_decrypt.py +++ b/windows/crypto/encrypt_decrypt.py @@ -4,6 +4,7 @@ from windows import winproxy from windows.crypto import DEFAULT_ENCODING from windows.crypto.helper import ECRYPT_DATA_BLOB from windows.generated_def import * +from windows.crypto import Certificate def encode_init_vector(data): blob = ECRYPT_DATA_BLOB.from_string(data) @@ -46,10 +47,14 @@ def encrypt(cert_or_certlist, msg, algo=szOID_NIST_AES256_CBC, initvector=genini """ alg_ident = CRYPT_ALGORITHM_IDENTIFIER() alg_ident.pszObjId = algo - if isinstance(cert_or_certlist, PCERT_CONTEXT): - certlist = (cert_or_certlist,) + # We want to have automatique translation of Certificate -> PCERT_CONTEXT + # In order to simple create the 'PCERT_CONTEXT[] certs' + # For that we need a tuple of X * 1-item-tuple + # as a (cert,) will be automaticly translatable to a PCERT_CONTEXT + if isinstance(cert_or_certlist, CERT_CONTEXT): + certlist = ((cert_or_certlist,),) else: - certlist = tuple(cert_or_certlist) + certlist = tuple((c,) for c in cert_or_certlist) # Set (compute if needed) the IV if initvector is None: @@ -74,6 +79,7 @@ def encrypt(cert_or_certlist, msg, algo=szOID_NIST_AES256_CBC, initvector=genini param.dwFlags = 0 param.dwInnerContentType = 0 + certs = (PCERT_CONTEXT * len(certlist))(*certlist) #Ask the output buffer size size = DWORD() diff --git a/windows/crypto/generation.py b/windows/crypto/generation.py index c796f23..8db5c7e 100644 --- a/windows/crypto/generation.py +++ b/windows/crypto/generation.py @@ -3,7 +3,7 @@ from windows import winproxy from windows.generated_def import * from windows.crypto.helper import ECRYPT_DATA_BLOB -from windows.crypto import DEFAULT_ENCODING, EHCERTSTORE +from windows.crypto import DEFAULT_ENCODING, CertificateStore def generate_selfsigned_certificate(name="CN=DEFAULT", prov=None, key_info=None, flags=0, signature_algo=None): @@ -11,14 +11,14 @@ def generate_selfsigned_certificate(name="CN=DEFAULT", prov=None, key_info=None, See https://msdn.microsoft.com/en-us/library/windows/desktop/aa376039(v=vs.85).aspx - :return: :class:`windows.crypto.CertificateContext` + :return: :class:`windows.crypto.Certificate` """ size = ULONG(len(name) + 0x100) buffer = (ctypes.c_ubyte * size.value)() winproxy.CertStrToNameA(X509_ASN_ENCODING, name, CERT_OID_NAME_STR, None, buffer, size, None) blobname = ECRYPT_DATA_BLOB(size.value, buffer) cert = winproxy.CertCreateSelfSignCertificate(prov, blobname, flags, key_info, signature_algo, None, None, None) - return windows.crypto.CertificateContext(cert[0]) + return windows.crypto.Certificate.from_pointer(cert) def generate_key(prov, keytype=AT_KEYEXCHANGE, flags=CRYPT_EXPORTABLE): diff --git a/windows/utils/pythonutils.py b/windows/utils/pythonutils.py index fdf75d9..987f5a9 100644 --- a/windows/utils/pythonutils.py +++ b/windows/utils/pythonutils.py @@ -4,8 +4,6 @@ import _ctypes from windows.generated_def import Flag, LPCSTR, LPWSTR - - def buffer(size): # Test buf = ctypes.create_string_buffer(size) buf.size = size @@ -25,6 +23,7 @@ def buffer(size): # Test return ImprovedCtypesBufferImpl() + def fixedpropety(f): cache_name = "_" + f.__name__ @@ -73,5 +72,6 @@ def print_ctypes_struct(struct, name="", ident=0, hexa=False): continue print_ctypes_struct(value, "{0}.{1}".format(name, fname), hexa=hexa) + def sprint(struct, name="struct", hexa=True): return print_ctypes_struct(struct, name=name, hexa=hexa) \ No newline at end of file diff --git a/windows/winobject/handle.py b/windows/winobject/handle.py index 30ec78b..b1eeeca 100644 --- a/windows/winobject/handle.py +++ b/windows/winobject/handle.py @@ -104,8 +104,6 @@ class Handle(SYSTEM_HANDLE): del thread._handle return res - - def __repr__(self): return "<{0} value=<0x{1:x}> in process pid={2}>".format(type(self).__name__, self.wValue, self.dwProcessId) diff --git a/windows/winobject/wingui.py b/windows/winobject/wingui.py deleted file mode 100644 index e34e297..0000000 --- a/windows/winobject/wingui.py +++ /dev/null @@ -1,76 +0,0 @@ -import ctypes - -import windows -from windows import winproxy -from windows.generated_def import * - - - -callback_type = ctypes.WINFUNCTYPE(UINT, HWND, LPARAM) - - -class Point(POINT): - def __repr__(self): - return "<{0} x={1} y={2}>".format(type(self).__name__, self.x, self.y) - #return "<{0} x={1:#x} y={2:#x}>".format(type(self).__name__, self.x, self.y) - -class Rect(RECT): - def __repr__(self): - return "<{0} left={1} top={2} right={3} bottom={4}>".format(type(self).__name__, self.left, self.top, self.right, self.bottom) - #return "<{0} x={1:#x} y={2:#x}>".format(type(self).__name__, self.x, self.y) - - -def get_cursor_pos(): - res = Point() - winproxy.GetCursorPos(res) - return res - -class Window(object): - def __init__(self, handle): - self.handle = handle - - def name(self): - size = 0x1024 - buffer = ctypes.c_buffer(size) - - res = windows.winproxy.GetWindowTextA(self.handle, buffer, size) - return buffer[:res] - - def rect(self): - res = Rect() - winproxy.GetWindowRect(self.handle, res) - return res - - def size(self): - rect = self.rect() - width = rect.right - rect.left - heigth = rect.bottom - rect.top - return width, heigth - - @classmethod - def at_point(cls, point): - handle = winproxy.WindowFromPoint(point) - return cls(handle) - - - # I don't understand the interest: - # Either return "" or C:\Python27\python.exe - #def module(self): - # size = 0x1024 - # buffer = ctypes.c_buffer(size) - # res = windows.winproxy.GetWindowModuleFileNameA(self.handle, buffer, size) - # return buffer[:res] - - -def enumwindows(): - result = [] - def callback(handle, param): - result.append(handle) - return True - - try: - x = windows.winproxy.EnumWindows(callback_type(callback), 0) - except WindowsError: - if not result: - raise - return result