/* * Crypto wrapper for Microsoft CryptoAPI * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #include <windows.h> #include <wincrypt.h> #include "common.h" #include "crypto.h" #ifndef MS_ENH_RSA_AES_PROV #ifdef UNICODE #define MS_ENH_RSA_AES_PROV \ L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" #else #define MS_ENH_RSA_AES_PROV \ "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" #endif #endif /* MS_ENH_RSA_AES_PROV */ #ifndef CALG_HMAC #define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) #endif #ifdef CONFIG_TLS_INTERNAL #ifdef __MINGW32_VERSION /* * MinGW does not yet include all the needed definitions for CryptoAPI, so * define here whatever extra is needed. */ static BOOL WINAPI (*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) = NULL; /* to be loaded from crypt32.dll */ static int mingw_load_crypto_func(void) { HINSTANCE dll; /* MinGW does not yet have full CryptoAPI support, so load the needed * function here. */ if (CryptImportPublicKeyInfo) return 0; dll = LoadLibrary("crypt32"); if (dll == NULL) { wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " "library"); return -1; } CryptImportPublicKeyInfo = GetProcAddress( dll, "CryptImportPublicKeyInfo"); if (CryptImportPublicKeyInfo == NULL) { wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " "CryptImportPublicKeyInfo() address from " "crypt32 library"); return -1; } return 0; } #else /* __MINGW32_VERSION */ static int mingw_load_crypto_func(void) { return 0; } #endif /* __MINGW32_VERSION */ #endif /* CONFIG_TLS_INTERNAL */ static void cryptoapi_report_error(const char *msg) { char *s, *pos; DWORD err = GetLastError(); if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); } pos = s; while (*pos) { if (*pos == '\n' || *pos == '\r') { *pos = '\0'; break; } pos++; } wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); LocalFree(s); } int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { HCRYPTPROV prov; HCRYPTHASH hash; size_t i; DWORD hlen; int ret = 0; if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { cryptoapi_report_error("CryptAcquireContext"); return -1; } if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { cryptoapi_report_error("CryptCreateHash"); CryptReleaseContext(prov, 0); return -1; } for (i = 0; i < num_elem; i++) { if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { cryptoapi_report_error("CryptHashData"); CryptDestroyHash(hash); CryptReleaseContext(prov, 0); } } hlen = hash_len; if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { cryptoapi_report_error("CryptGetHashParam"); ret = -1; } CryptDestroyHash(hash); CryptReleaseContext(prov, 0); return ret; } void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); } void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 next, tmp; int i; HCRYPTPROV prov; HCRYPTKEY ckey; DWORD dlen; struct { BLOBHEADER hdr; DWORD len; BYTE key[8]; } key_blob; DWORD mode = CRYPT_MODE_ECB; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; key_blob.hdr.aiKeyAlg = CALG_DES; key_blob.len = 8; /* Add parity bits to the key */ next = 0; for (i = 0; i < 7; i++) { tmp = key[i]; key_blob.key[i] = (tmp >> i) | next | 1; next = tmp << (7 - i); } key_blob.key[i] = next | 1; if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " "%d", (int) GetLastError()); return; } if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, &ckey)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", (int) GetLastError()); CryptReleaseContext(prov, 0); return; } if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " "failed: %d", (int) GetLastError()); CryptDestroyKey(ckey); CryptReleaseContext(prov, 0); return; } os_memcpy(cypher, clear, 8); dlen = 8; if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", (int) GetLastError()); os_memset(cypher, 0, 8); } CryptDestroyKey(ckey); CryptReleaseContext(prov, 0); } #ifdef EAP_TLS_FUNCS void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); } void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); } struct aes_context { HCRYPTPROV prov; HCRYPTKEY ckey; }; void * aes_encrypt_init(const u8 *key, size_t len) { struct aes_context *akey; struct { BLOBHEADER hdr; DWORD len; BYTE key[16]; } key_blob; DWORD mode = CRYPT_MODE_ECB; if (len != 16) return NULL; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; key_blob.hdr.aiKeyAlg = CALG_AES_128; key_blob.len = len; os_memcpy(key_blob.key, key, len); akey = os_zalloc(sizeof(*akey)); if (akey == NULL) return NULL; if (!CryptAcquireContext(&akey->prov, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " "%d", (int) GetLastError()); os_free(akey); return NULL; } if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, &akey->ckey)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", (int) GetLastError()); CryptReleaseContext(akey->prov, 0); os_free(akey); return NULL; } if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " "failed: %d", (int) GetLastError()); CryptDestroyKey(akey->ckey); CryptReleaseContext(akey->prov, 0); os_free(akey); return NULL; } return akey; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { struct aes_context *akey = ctx; DWORD dlen; os_memcpy(crypt, plain, 16); dlen = 16; if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", (int) GetLastError()); os_memset(crypt, 0, 16); } } void aes_encrypt_deinit(void *ctx) { struct aes_context *akey = ctx; if (akey) { CryptDestroyKey(akey->ckey); CryptReleaseContext(akey->prov, 0); os_free(akey); } } void * aes_decrypt_init(const u8 *key, size_t len) { return aes_encrypt_init(key, len); } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { struct aes_context *akey = ctx; DWORD dlen; os_memcpy(plain, crypt, 16); dlen = 16; if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", (int) GetLastError()); } } void aes_decrypt_deinit(void *ctx) { aes_encrypt_deinit(ctx); } #ifdef CONFIG_TLS_INTERNAL struct crypto_hash { enum crypto_hash_alg alg; int error; HCRYPTPROV prov; HCRYPTHASH hash; HCRYPTKEY key; }; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len) { struct crypto_hash *ctx; ALG_ID calg; struct { BLOBHEADER hdr; DWORD len; BYTE key[32]; } key_blob; os_memset(&key_blob, 0, sizeof(key_blob)); switch (alg) { case CRYPTO_HASH_ALG_MD5: calg = CALG_MD5; break; case CRYPTO_HASH_ALG_SHA1: calg = CALG_SHA; break; case CRYPTO_HASH_ALG_HMAC_MD5: case CRYPTO_HASH_ALG_HMAC_SHA1: calg = CALG_HMAC; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; /* * Note: RC2 is not really used, but that can be used to * import HMAC keys of up to 16 byte long. * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to * be able to import longer keys (HMAC-SHA1 uses 20-byte key). */ key_blob.hdr.aiKeyAlg = CALG_RC2; key_blob.len = key_len; if (key_len > sizeof(key_blob.key)) return NULL; os_memcpy(key_blob.key, key, key_len); break; default: return NULL; } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; ctx->alg = alg; if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { cryptoapi_report_error("CryptAcquireContext"); os_free(ctx); return NULL; } if (calg == CALG_HMAC) { #ifndef CRYPT_IPSEC_HMAC_KEY #define CRYPT_IPSEC_HMAC_KEY 0x00000100 #endif if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, &ctx->key)) { cryptoapi_report_error("CryptImportKey"); CryptReleaseContext(ctx->prov, 0); os_free(ctx); return NULL; } } if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { cryptoapi_report_error("CryptCreateHash"); CryptReleaseContext(ctx->prov, 0); os_free(ctx); return NULL; } if (calg == CALG_HMAC) { HMAC_INFO info; os_memset(&info, 0, sizeof(info)); switch (alg) { case CRYPTO_HASH_ALG_HMAC_MD5: info.HashAlgid = CALG_MD5; break; case CRYPTO_HASH_ALG_HMAC_SHA1: info.HashAlgid = CALG_SHA; break; default: /* unreachable */ break; } if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, 0)) { cryptoapi_report_error("CryptSetHashParam"); CryptDestroyHash(ctx->hash); CryptReleaseContext(ctx->prov, 0); os_free(ctx); return NULL; } } return ctx; } void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) { if (ctx == NULL || ctx->error) return; if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { cryptoapi_report_error("CryptHashData"); ctx->error = 1; } } int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) { int ret = 0; DWORD hlen; if (ctx == NULL) return -2; if (mac == NULL || len == NULL) goto done; if (ctx->error) { ret = -2; goto done; } hlen = *len; if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { cryptoapi_report_error("CryptGetHashParam"); ret = -2; } *len = hlen; done: if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) CryptDestroyKey(ctx->key); os_free(ctx); return ret; } struct crypto_cipher { HCRYPTPROV prov; HCRYPTKEY key; }; struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) { struct crypto_cipher *ctx; struct { BLOBHEADER hdr; DWORD len; BYTE key[32]; } key_blob; DWORD mode = CRYPT_MODE_CBC; key_blob.hdr.bType = PLAINTEXTKEYBLOB; key_blob.hdr.bVersion = CUR_BLOB_VERSION; key_blob.hdr.reserved = 0; key_blob.len = key_len; if (key_len > sizeof(key_blob.key)) return NULL; os_memcpy(key_blob.key, key, key_len); switch (alg) { case CRYPTO_CIPHER_ALG_AES: if (key_len == 32) key_blob.hdr.aiKeyAlg = CALG_AES_256; else if (key_len == 24) key_blob.hdr.aiKeyAlg = CALG_AES_192; else key_blob.hdr.aiKeyAlg = CALG_AES_128; break; case CRYPTO_CIPHER_ALG_3DES: key_blob.hdr.aiKeyAlg = CALG_3DES; break; case CRYPTO_CIPHER_ALG_DES: key_blob.hdr.aiKeyAlg = CALG_DES; break; case CRYPTO_CIPHER_ALG_RC2: key_blob.hdr.aiKeyAlg = CALG_RC2; break; case CRYPTO_CIPHER_ALG_RC4: key_blob.hdr.aiKeyAlg = CALG_RC4; break; default: return NULL; } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) return NULL; if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { cryptoapi_report_error("CryptAcquireContext"); goto fail1; } if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, &ctx->key)) { cryptoapi_report_error("CryptImportKey"); goto fail2; } if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); goto fail3; } if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); goto fail3; } return ctx; fail3: CryptDestroyKey(ctx->key); fail2: CryptReleaseContext(ctx->prov, 0); fail1: os_free(ctx); return NULL; } int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { DWORD dlen; os_memcpy(crypt, plain, len); dlen = len; if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { cryptoapi_report_error("CryptEncrypt"); os_memset(crypt, 0, len); return -1; } return 0; } int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { DWORD dlen; os_memcpy(plain, crypt, len); dlen = len; if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { cryptoapi_report_error("CryptDecrypt"); return -1; } return 0; } void crypto_cipher_deinit(struct crypto_cipher *ctx) { CryptDestroyKey(ctx->key); CryptReleaseContext(ctx->prov, 0); os_free(ctx); } struct crypto_public_key { HCRYPTPROV prov; HCRYPTKEY rsa; }; struct crypto_private_key { HCRYPTPROV prov; HCRYPTKEY rsa; }; struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) { /* Use crypto_public_key_from_cert() instead. */ return NULL; } struct crypto_private_key * crypto_private_key_import(const u8 *key, size_t len) { /* TODO */ return NULL; } struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, size_t len) { struct crypto_public_key *pk; PCCERT_CONTEXT cc; pk = os_zalloc(sizeof(*pk)); if (pk == NULL) return NULL; cc = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); if (!cc) { cryptoapi_report_error("CryptCreateCertificateContext"); os_free(pk); return NULL; } if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, 0)) { cryptoapi_report_error("CryptAcquireContext"); os_free(pk); CertFreeCertificateContext(cc); return NULL; } if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &cc->pCertInfo->SubjectPublicKeyInfo, &pk->rsa)) { cryptoapi_report_error("CryptImportPublicKeyInfo"); CryptReleaseContext(pk->prov, 0); os_free(pk); CertFreeCertificateContext(cc); return NULL; } CertFreeCertificateContext(cc); return pk; } int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { DWORD clen; u8 *tmp; size_t i; if (*outlen < inlen) return -1; tmp = malloc(*outlen); if (tmp == NULL) return -1; os_memcpy(tmp, in, inlen); clen = inlen; if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " "public key: %d", (int) GetLastError()); os_free(tmp); return -1; } *outlen = clen; /* Reverse the output */ for (i = 0; i < *outlen; i++) out[i] = tmp[*outlen - 1 - i]; os_free(tmp); return 0; } int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, const u8 *in, size_t inlen, u8 *out, size_t *outlen) { /* TODO */ return -1; } void crypto_public_key_free(struct crypto_public_key *key) { if (key) { CryptDestroyKey(key->rsa); CryptReleaseContext(key->prov, 0); os_free(key); } } void crypto_private_key_free(struct crypto_private_key *key) { if (key) { CryptDestroyKey(key->rsa); CryptReleaseContext(key->prov, 0); os_free(key); } } int crypto_global_init(void) { return mingw_load_crypto_func(); } void crypto_global_deinit(void) { } #endif /* CONFIG_TLS_INTERNAL */ #endif /* EAP_TLS_FUNCS */