/* mz_crypt_win32.c -- Crypto/hash functions for Windows part of the minizip-ng project Copyright (C) 2010-2021 Nathan Moinvaziri https://github.com/zlib-ng/minizip-ng This program is distributed under the terms of the same license as zlib. See the accompanying LICENSE file for the full text of the license. */ #include "mz.h" #include "mz_os.h" #include "mz_crypt.h" #include #include /***************************************************************************/ int32_t mz_crypt_rand(uint8_t *buf, int32_t size) { HCRYPTPROV provider; int32_t result = 0; result = CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); if (result) { result = CryptGenRandom(provider, size, buf); CryptReleaseContext(provider, 0); if (result) return size; } return mz_os_rand(buf, size); } /***************************************************************************/ typedef struct mz_crypt_sha_s { HCRYPTPROV provider; HCRYPTHASH hash; int32_t error; uint16_t algorithm; } mz_crypt_sha; /***************************************************************************/ void mz_crypt_sha_reset(void *handle) { mz_crypt_sha *sha = (mz_crypt_sha *)handle; if (sha->hash) CryptDestroyHash(sha->hash); sha->hash = 0; if (sha->provider) CryptReleaseContext(sha->provider, 0); sha->provider = 0; sha->error = 0; } int32_t mz_crypt_sha_begin(void *handle) { mz_crypt_sha *sha = (mz_crypt_sha *)handle; ALG_ID alg_id = 0; int32_t result = 0; int32_t err = MZ_OK; if (sha == NULL) return MZ_PARAM_ERROR; if (sha->algorithm == MZ_HASH_SHA1) alg_id = CALG_SHA1; else alg_id = CALG_SHA_256; result = CryptAcquireContext(&sha->provider, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); if (!result) { sha->error = GetLastError(); err = MZ_CRYPT_ERROR; } if (result) { result = CryptCreateHash(sha->provider, alg_id, 0, 0, &sha->hash); if (!result) { sha->error = GetLastError(); err = MZ_HASH_ERROR; } } return err; } int32_t mz_crypt_sha_update(void *handle, const void *buf, int32_t size) { mz_crypt_sha *sha = (mz_crypt_sha *)handle; int32_t result = 0; if (sha == NULL || buf == NULL || sha->hash == 0) return MZ_PARAM_ERROR; result = CryptHashData(sha->hash, buf, size, 0); if (!result) { sha->error = GetLastError(); return MZ_HASH_ERROR; } return size; } int32_t mz_crypt_sha_end(void *handle, uint8_t *digest, int32_t digest_size) { mz_crypt_sha *sha = (mz_crypt_sha *)handle; int32_t result = 0; int32_t expected_size = 0; if (sha == NULL || digest == NULL || sha->hash == 0) return MZ_PARAM_ERROR; result = CryptGetHashParam(sha->hash, HP_HASHVAL, NULL, (DWORD *)&expected_size, 0); if (expected_size > digest_size) return MZ_BUF_ERROR; if (!result) return MZ_HASH_ERROR; result = CryptGetHashParam(sha->hash, HP_HASHVAL, digest, (DWORD *)&digest_size, 0); if (!result) { sha->error = GetLastError(); return MZ_HASH_ERROR; } return MZ_OK; } void mz_crypt_sha_set_algorithm(void *handle, uint16_t algorithm) { mz_crypt_sha *sha = (mz_crypt_sha *)handle; sha->algorithm = algorithm; } void *mz_crypt_sha_create(void **handle) { mz_crypt_sha *sha = NULL; sha = (mz_crypt_sha *)MZ_ALLOC(sizeof(mz_crypt_sha)); if (sha != NULL) { memset(sha, 0, sizeof(mz_crypt_sha)); sha->algorithm = MZ_HASH_SHA256; } if (handle != NULL) *handle = sha; return sha; } void mz_crypt_sha_delete(void **handle) { mz_crypt_sha *sha = NULL; if (handle == NULL) return; sha = (mz_crypt_sha *)*handle; if (sha != NULL) { mz_crypt_sha_reset(*handle); MZ_FREE(sha); } *handle = NULL; } /***************************************************************************/ typedef struct mz_crypt_aes_s { HCRYPTPROV provider; HCRYPTKEY key; int32_t mode; int32_t error; } mz_crypt_aes; /***************************************************************************/ static void mz_crypt_aes_free(void *handle) { mz_crypt_aes *aes = (mz_crypt_aes *)handle; if (aes->key) CryptDestroyKey(aes->key); aes->key = 0; if (aes->provider) CryptReleaseContext(aes->provider, 0); aes->provider = 0; } void mz_crypt_aes_reset(void *handle) { mz_crypt_aes_free(handle); } int32_t mz_crypt_aes_encrypt(void *handle, uint8_t *buf, int32_t size) { mz_crypt_aes *aes = (mz_crypt_aes *)handle; int32_t result = 0; if (aes == NULL || buf == NULL) return MZ_PARAM_ERROR; if (size != MZ_AES_BLOCK_SIZE) return MZ_PARAM_ERROR; result = CryptEncrypt(aes->key, 0, 0, 0, buf, (DWORD *)&size, size); if (!result) { aes->error = GetLastError(); return MZ_CRYPT_ERROR; } return size; } int32_t mz_crypt_aes_decrypt(void *handle, uint8_t *buf, int32_t size) { mz_crypt_aes *aes = (mz_crypt_aes *)handle; int32_t result = 0; if (aes == NULL || buf == NULL) return MZ_PARAM_ERROR; if (size != MZ_AES_BLOCK_SIZE) return MZ_PARAM_ERROR; result = CryptDecrypt(aes->key, 0, 0, 0, buf, (DWORD *)&size); if (!result) { aes->error = GetLastError(); return MZ_CRYPT_ERROR; } return size; } static int32_t mz_crypt_aes_set_key(void *handle, const void *key, int32_t key_length) { mz_crypt_aes *aes = (mz_crypt_aes *)handle; HCRYPTHASH hash = 0; ALG_ID alg_id = 0; typedef struct key_blob_header_s { BLOBHEADER hdr; uint32_t key_length; } key_blob_header_s; key_blob_header_s *key_blob_s = NULL; uint32_t mode = CRYPT_MODE_ECB; uint8_t *key_blob = NULL; int32_t key_blob_size = 0; int32_t result = 0; int32_t err = MZ_OK; if (aes == NULL || key == NULL) return MZ_PARAM_ERROR; mz_crypt_aes_reset(handle); if (key_length == MZ_AES_KEY_LENGTH(MZ_AES_ENCRYPTION_MODE_128)) alg_id = CALG_AES_128; else if (key_length == MZ_AES_KEY_LENGTH(MZ_AES_ENCRYPTION_MODE_192)) alg_id = CALG_AES_192; else if (key_length == MZ_AES_KEY_LENGTH(MZ_AES_ENCRYPTION_MODE_256)) alg_id = CALG_AES_256; else return MZ_PARAM_ERROR; result = CryptAcquireContext(&aes->provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); if (result) { key_blob_size = sizeof(key_blob_header_s) + key_length; key_blob = (uint8_t *)MZ_ALLOC(key_blob_size); if (key_blob) { key_blob_s = (key_blob_header_s *)key_blob; key_blob_s->hdr.bType = PLAINTEXTKEYBLOB; key_blob_s->hdr.bVersion = CUR_BLOB_VERSION; key_blob_s->hdr.aiKeyAlg = alg_id; key_blob_s->hdr.reserved = 0; key_blob_s->key_length = key_length; memcpy(key_blob + sizeof(key_blob_header_s), key, key_length); result = CryptImportKey(aes->provider, key_blob, key_blob_size, 0, 0, &aes->key); SecureZeroMemory(key_blob, key_blob_size); MZ_FREE(key_blob); } else { err = MZ_MEM_ERROR; } } if (result && err == MZ_OK) result = CryptSetKeyParam(aes->key, KP_MODE, (const uint8_t *)&mode, 0); if (!result && err == MZ_OK) { aes->error = GetLastError(); err = MZ_CRYPT_ERROR; } if (hash) CryptDestroyHash(hash); return err; } int32_t mz_crypt_aes_set_encrypt_key(void *handle, const void *key, int32_t key_length) { return mz_crypt_aes_set_key(handle, key, key_length); } int32_t mz_crypt_aes_set_decrypt_key(void *handle, const void *key, int32_t key_length) { return mz_crypt_aes_set_key(handle, key, key_length); } void mz_crypt_aes_set_mode(void *handle, int32_t mode) { mz_crypt_aes *aes = (mz_crypt_aes *)handle; aes->mode = mode; } void *mz_crypt_aes_create(void **handle) { mz_crypt_aes *aes = NULL; aes = (mz_crypt_aes *)MZ_ALLOC(sizeof(mz_crypt_aes)); if (aes != NULL) memset(aes, 0, sizeof(mz_crypt_aes)); if (handle != NULL) *handle = aes; return aes; } void mz_crypt_aes_delete(void **handle) { mz_crypt_aes *aes = NULL; if (handle == NULL) return; aes = (mz_crypt_aes *)*handle; if (aes != NULL) { mz_crypt_aes_free(*handle); MZ_FREE(aes); } *handle = NULL; } /***************************************************************************/ typedef struct mz_crypt_hmac_s { HCRYPTPROV provider; HCRYPTHASH hash; HCRYPTKEY key; HMAC_INFO info; int32_t mode; int32_t error; uint16_t algorithm; } mz_crypt_hmac; /***************************************************************************/ static void mz_crypt_hmac_free(void *handle) { mz_crypt_hmac *hmac = (mz_crypt_hmac *)handle; if (hmac->key) CryptDestroyKey(hmac->key); hmac->key = 0; if (hmac->hash) CryptDestroyHash(hmac->hash); hmac->hash = 0; if (hmac->provider) CryptReleaseContext(hmac->provider, 0); hmac->provider = 0; memset(&hmac->info, 0, sizeof(hmac->info)); } void mz_crypt_hmac_reset(void *handle) { mz_crypt_hmac_free(handle); } int32_t mz_crypt_hmac_init(void *handle, const void *key, int32_t key_length) { mz_crypt_hmac *hmac = (mz_crypt_hmac *)handle; ALG_ID alg_id = 0; typedef struct key_blob_header_s { BLOBHEADER hdr; uint32_t key_length; } key_blob_header_s; key_blob_header_s *key_blob_s = NULL; uint8_t *key_blob = NULL; int32_t key_blob_size = 0; int32_t result = 0; int32_t err = MZ_OK; if (hmac == NULL || key == NULL) return MZ_PARAM_ERROR; mz_crypt_hmac_reset(handle); if (hmac->algorithm == MZ_HASH_SHA1) alg_id = CALG_SHA1; else alg_id = CALG_SHA_256; hmac->info.HashAlgid = alg_id; result = CryptAcquireContext(&hmac->provider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); if (!result) { hmac->error = GetLastError(); err = MZ_CRYPT_ERROR; } else { /* Zero-pad odd key lengths */ if (key_length % 2 == 1) key_length += 1; key_blob_size = sizeof(key_blob_header_s) + key_length; key_blob = (uint8_t *)MZ_ALLOC(key_blob_size); } if (key_blob) { memset(key_blob, 0, key_blob_size); key_blob_s = (key_blob_header_s *)key_blob; key_blob_s->hdr.bType = PLAINTEXTKEYBLOB; key_blob_s->hdr.bVersion = CUR_BLOB_VERSION; key_blob_s->hdr.aiKeyAlg = CALG_RC2; key_blob_s->hdr.reserved = 0; key_blob_s->key_length = key_length; memcpy(key_blob + sizeof(key_blob_header_s), key, key_length); result = CryptImportKey(hmac->provider, key_blob, key_blob_size, 0, CRYPT_IPSEC_HMAC_KEY, &hmac->key); if (result) result = CryptCreateHash(hmac->provider, CALG_HMAC, hmac->key, 0, &hmac->hash); if (result) result = CryptSetHashParam(hmac->hash, HP_HMAC_INFO, (uint8_t *)&hmac->info, 0); SecureZeroMemory(key_blob, key_blob_size); MZ_FREE(key_blob); } else if (err == MZ_OK) { err = MZ_MEM_ERROR; } if (!result) { hmac->error = GetLastError(); err = MZ_CRYPT_ERROR; } if (err != MZ_OK) mz_crypt_hmac_free(handle); return err; } int32_t mz_crypt_hmac_update(void *handle, const void *buf, int32_t size) { mz_crypt_hmac *hmac = (mz_crypt_hmac *)handle; int32_t result = 0; if (hmac == NULL || buf == NULL || hmac->hash == 0) return MZ_PARAM_ERROR; result = CryptHashData(hmac->hash, buf, size, 0); if (!result) { hmac->error = GetLastError(); return MZ_HASH_ERROR; } return MZ_OK; } int32_t mz_crypt_hmac_end(void *handle, uint8_t *digest, int32_t digest_size) { mz_crypt_hmac *hmac = (mz_crypt_hmac *)handle; int32_t result = 0; int32_t expected_size = 0; if (hmac == NULL || digest == NULL || hmac->hash == 0) return MZ_PARAM_ERROR; result = CryptGetHashParam(hmac->hash, HP_HASHVAL, NULL, (DWORD *)&expected_size, 0); if (expected_size > digest_size) return MZ_BUF_ERROR; if (!result) return MZ_HASH_ERROR; result = CryptGetHashParam(hmac->hash, HP_HASHVAL, digest, (DWORD *)&digest_size, 0); if (!result) { hmac->error = GetLastError(); return MZ_HASH_ERROR; } return MZ_OK; } void mz_crypt_hmac_set_algorithm(void *handle, uint16_t algorithm) { mz_crypt_hmac *hmac = (mz_crypt_hmac *)handle; hmac->algorithm = algorithm; } int32_t mz_crypt_hmac_copy(void *src_handle, void *target_handle) { mz_crypt_hmac *source = (mz_crypt_hmac *)src_handle; mz_crypt_hmac *target = (mz_crypt_hmac *)target_handle; int32_t result = 0; int32_t err = MZ_OK; if (target->hash) { CryptDestroyHash(target->hash); target->hash = 0; } result = CryptDuplicateHash(source->hash, NULL, 0, &target->hash); if (!result) { target->error = GetLastError(); err = MZ_HASH_ERROR; } return err; } void *mz_crypt_hmac_create(void **handle) { mz_crypt_hmac *hmac = NULL; hmac = (mz_crypt_hmac *)MZ_ALLOC(sizeof(mz_crypt_hmac)); if (hmac != NULL) { memset(hmac, 0, sizeof(mz_crypt_hmac)); hmac->algorithm = MZ_HASH_SHA256; } if (handle != NULL) *handle = hmac; return hmac; } void mz_crypt_hmac_delete(void **handle) { mz_crypt_hmac *hmac = NULL; if (handle == NULL) return; hmac = (mz_crypt_hmac *)*handle; if (hmac != NULL) { mz_crypt_hmac_free(*handle); MZ_FREE(hmac); } *handle = NULL; } /***************************************************************************/ #if defined(MZ_ZIP_SIGNING) int32_t mz_crypt_sign(uint8_t *message, int32_t message_size, uint8_t *cert_data, int32_t cert_data_size, const char *cert_pwd, uint8_t **signature, int32_t *signature_size) { CRYPT_SIGN_MESSAGE_PARA sign_params; CRYPT_DATA_BLOB cert_data_blob; PCCERT_CONTEXT cert_context = NULL; HCERTSTORE cert_store = 0; wchar_t *password_wide = NULL; int32_t result = 0; int32_t err = MZ_OK; uint32_t messages_sizes[1]; uint8_t *messages[1]; if (message == NULL || cert_data == NULL || signature == NULL || signature_size == NULL) return MZ_PARAM_ERROR; *signature = NULL; *signature_size = 0; cert_data_blob.pbData = cert_data; cert_data_blob.cbData = cert_data_size; password_wide = mz_os_unicode_string_create(cert_pwd, MZ_ENCODING_UTF8); if (password_wide) { cert_store = PFXImportCertStore(&cert_data_blob, password_wide, 0); mz_os_unicode_string_delete(&password_wide); } if (cert_store == NULL) cert_store = PFXImportCertStore(&cert_data_blob, L"", 0); if (cert_store == NULL) cert_store = PFXImportCertStore(&cert_data_blob, NULL, 0); if (cert_store == NULL) return MZ_PARAM_ERROR; if (err == MZ_OK) { cert_context = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HAS_PRIVATE_KEY, NULL, NULL); if (cert_context == NULL) err = MZ_PARAM_ERROR; } if (err == MZ_OK) { memset(&sign_params, 0, sizeof(sign_params)); sign_params.cbSize = sizeof(sign_params); sign_params.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; sign_params.pSigningCert = cert_context; sign_params.HashAlgorithm.pszObjId = szOID_NIST_sha256; sign_params.cMsgCert = 1; sign_params.rgpMsgCert = &cert_context; messages[0] = message; messages_sizes[0] = message_size; #if 0 /* Timestamp support */ CRYPT_ATTR_BLOB crypt_blob; CRYPT_TIMESTAMP_CONTEXT *ts_context = NULL; CRYPT_ATTRIBUTE unauth_attribs[1]; wchar_t *timestamp_url_wide = NULL; const char *timestamp_url = NULL; if (timestamp_url != NULL) timestamp_url_wide = mz_os_unicode_string_create(timestamp_url); if (timestamp_url_wide != NULL) { result = CryptRetrieveTimeStamp(timestamp_url_wide, TIMESTAMP_NO_AUTH_RETRIEVAL | TIMESTAMP_VERIFY_CONTEXT_SIGNATURE, 0, szOID_NIST_sha256, NULL, message, message_size, &ts_context, NULL, NULL); mz_os_unicode_string_delete(×tamp_url_wide); if ((result) && (ts_context != NULL)) { crypt_blob.cbData = ts_context->cbEncoded; crypt_blob.pbData = ts_context->pbEncoded; unauth_attribs[0].pszObjId = "1.2.840.113549.1.9.16.2.14"; //id-smime-aa-timeStampToken unauth_attribs[0].cValue = 1; unauth_attribs[0].rgValue = &crypt_blob; sign_params.rgUnauthAttr = &unauth_attribs[0]; sign_params.cUnauthAttr = 1; } } if (ts_context != NULL) CryptMemFree(ts_context); if (result) #endif result = CryptSignMessage(&sign_params, FALSE, 1, (const BYTE **)messages, (DWORD *)messages_sizes, NULL, (DWORD *)signature_size); if (result && *signature_size > 0) *signature = (uint8_t *)MZ_ALLOC(*signature_size); if (result && *signature != NULL) result = CryptSignMessage(&sign_params, FALSE, 1, (const BYTE **)messages, (DWORD *)messages_sizes, *signature, (DWORD *)signature_size); if (!result) err = MZ_SIGN_ERROR; } if (cert_context != NULL) CertFreeCertificateContext(cert_context); if (cert_store != NULL) CertCloseStore(cert_store, 0); return err; } int32_t mz_crypt_sign_verify(uint8_t *message, int32_t message_size, uint8_t *signature, int32_t signature_size) { CRYPT_VERIFY_MESSAGE_PARA verify_params; CERT_CONTEXT *signer_cert = NULL; CERT_CHAIN_PARA chain_para; CERT_CHAIN_CONTEXT *chain_context = NULL; CERT_CHAIN_POLICY_PARA chain_policy; CERT_CHAIN_POLICY_STATUS chain_policy_status; HCRYPTMSG crypt_msg = 0; int32_t result = 0; int32_t err = MZ_SIGN_ERROR; uint8_t *decoded = NULL; int32_t decoded_size = 0; memset(&verify_params, 0, sizeof(verify_params)); verify_params.cbSize = sizeof(verify_params); verify_params.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING; result = CryptVerifyMessageSignature(&verify_params, 0, signature, signature_size, NULL, (DWORD *)&decoded_size, NULL); if (result && decoded_size > 0) decoded = (uint8_t *)MZ_ALLOC(decoded_size); if (result && decoded != NULL) result = CryptVerifyMessageSignature(&verify_params, 0, signature, signature_size, decoded, (DWORD *)&decoded_size, (const CERT_CONTEXT **)&signer_cert); /* Get and validate certificate chain */ memset(&chain_para, 0, sizeof(chain_para)); if (result && signer_cert != NULL) result = CertGetCertificateChain(NULL, signer_cert, NULL, NULL, &chain_para, CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, NULL, (const CERT_CHAIN_CONTEXT **)&chain_context); memset(&chain_policy, 0, sizeof(chain_policy)); chain_policy.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); memset(&chain_policy_status, 0, sizeof(chain_policy_status)); chain_policy_status.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS); if (result && chain_context != NULL) result = CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, chain_context, &chain_policy, &chain_policy_status); if (chain_policy_status.dwError != S_OK) result = 0; #if 0 crypt_msg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, 0, 0, NULL, NULL); if (crypt_msg != NULL) { /* Timestamp support */ PCRYPT_ATTRIBUTES unauth_attribs = NULL; HCRYPTMSG ts_msg = 0; uint8_t *ts_content = NULL; int32_t ts_content_size = 0; uint8_t *ts_signature = NULL; int32_t ts_signature_size = 0; result = CryptMsgUpdate(crypt_msg, signature, signature_size, 1); if (result) CryptMsgGetParam(crypt_msg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, NULL, &ts_signature_size); if ((result) && (ts_signature_size > 0)) ts_signature = (uint8_t *)MZ_ALLOC(ts_signature_size); if ((result) && (ts_signature != NULL)) { result = CryptMsgGetParam(crypt_msg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, ts_signature, &ts_signature_size); if (result) { unauth_attribs = (PCRYPT_ATTRIBUTES)ts_signature; if ((unauth_attribs->cAttr > 0) && (unauth_attribs->rgAttr[0].cValue > 0)) { ts_content = unauth_attribs->rgAttr[0].rgValue->pbData; ts_content_size = unauth_attribs->rgAttr[0].rgValue->cbData; } } if ((result) && (ts_content != NULL)) result = CryptVerifyTimeStampSignature(ts_content, ts_content_size, decoded, decoded_size, 0, &crypt_context, NULL, NULL); if (result) err = MZ_OK; } if (ts_signature != NULL) MZ_FREE(ts_signature); if (crypt_context != NULL) CryptMemFree(crypt_context); } else { result = 0; } #endif if ((result) && (decoded != NULL) && (decoded_size == message_size)) { /* Verify cms message with our stored message */ if (memcmp(decoded, message, message_size) == 0) err = MZ_OK; } if (chain_context != NULL) CertFreeCertificateChain(chain_context); if (signer_cert != NULL) CertFreeCertificateContext(signer_cert); if (crypt_msg != NULL) CryptMsgClose(crypt_msg); if (decoded != NULL) MZ_FREE(decoded); return err; } #endif