summaryrefslogtreecommitdiff
path: root/lib/system/keys-win.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system/keys-win.c')
-rw-r--r--lib/system/keys-win.c1387
1 files changed, 1387 insertions, 0 deletions
diff --git a/lib/system/keys-win.c b/lib/system/keys-win.c
new file mode 100644
index 0000000000..1f5ffad4a2
--- /dev/null
+++ b/lib/system/keys-win.c
@@ -0,0 +1,1387 @@
+/*
+ * Copyright © 2014-2016 Red Hat, Inc.
+ * Copyright © 2015-2016 Dyalog Ltd.
+ *
+ * Author: Nikos Mavrogiannopoulos, Bjørn Christensen
+ *
+ * GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+// Before including any Windows header we need to set _WIN32_WINNT to Vista
+// (or higher) so that the NCRYPT stuff can be used.
+#if _WIN32_WINNT < 0x600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x600
+#endif
+
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/abstract.h>
+#include <gnutls/pkcs12.h>
+#include <gnutls/system-keys.h>
+#include "system-keys.h"
+#include <tls-sig.h>
+#include <pk.h>
+#include <urls.h>
+
+#if !defined(_WIN32)
+# error should not be included
+#endif
+
+#include <wincrypt.h>
+#include <winbase.h>
+
+#define DYN_NCRYPT
+
+#include <ncrypt.h>
+
+// MinGW headers may not have these defines
+#ifndef NCRYPT_SHA1_ALGORITHM
+#define NCRYPT_SHA1_ALGORITHM BCRYPT_SHA1_ALGORITHM
+#endif
+#ifndef NCRYPT_SHA256_ALGORITHM
+#define NCRYPT_SHA256_ALGORITHM BCRYPT_SHA256_ALGORITHM
+#endif
+#ifndef NCRYPT_SHA384_ALGORITHM
+#define NCRYPT_SHA384_ALGORITHM BCRYPT_SHA384_ALGORITHM
+#endif
+#ifndef NCRYPT_SHA512_ALGORITHM
+#define NCRYPT_SHA512_ALGORITHM BCRYPT_SHA512_ALGORITHM
+#endif
+#ifndef NCRYPT_PAD_PKCS1_FLAG
+#define NCRYPT_PAD_PKCS1_FLAG 2
+#endif
+#ifndef NCRYPT_ALGORITHM_PROPERTY
+#define NCRYPT_ALGORITHM_PROPERTY L"Algorithm Name"
+#endif
+#ifndef CERT_NCRYPT_KEY_HANDLE_TRANSFER_PROP_ID
+#define CERT_NCRYPT_KEY_HANDLE_TRANSFER_PROP_ID 99
+#endif
+
+#define MAX_WID_SIZE 48
+
+static
+void *memrev(unsigned char *pvData, DWORD cbData);
+
+struct system_key_iter_st {
+ HCERTSTORE store;
+ const CERT_CONTEXT *cert;
+};
+
+typedef struct priv_st {
+ DWORD dwKeySpec; /* CAPI key */
+ HCRYPTPROV hCryptProv; /* CAPI keystore*/
+ NCRYPT_KEY_HANDLE nc; /* CNG Keystore*/
+ gnutls_pk_algorithm_t pk;
+ gnutls_sign_algorithm_t sign_algo;
+} priv_st;
+
+
+typedef SECURITY_STATUS (WINAPI *NCryptDeleteKeyFunc)(
+ NCRYPT_KEY_HANDLE hKey,DWORD dwFlags);
+
+typedef SECURITY_STATUS (WINAPI *NCryptOpenStorageProviderFunc)(
+ NCRYPT_PROV_HANDLE *phProvider, LPCWSTR pszProviderName,
+ DWORD dwFlags);
+
+typedef SECURITY_STATUS (WINAPI *NCryptOpenKeyFunc)(
+ NCRYPT_PROV_HANDLE hProvider, NCRYPT_KEY_HANDLE *phKey,
+ LPCWSTR pszKeyName, DWORD dwLegacyKeySpec,
+ DWORD dwFlags);
+
+typedef SECURITY_STATUS (WINAPI *NCryptGetPropertyFunc)(
+ NCRYPT_HANDLE hObject, LPCWSTR pszProperty,
+ PBYTE pbOutput, DWORD cbOutput,
+ DWORD *pcbResult, DWORD dwFlags);
+
+typedef SECURITY_STATUS (WINAPI *NCryptFreeObjectFunc)(
+ NCRYPT_HANDLE hObject);
+
+typedef SECURITY_STATUS (WINAPI *NCryptDecryptFunc)(
+ NCRYPT_KEY_HANDLE hKey, PBYTE pbInput,
+ DWORD cbInput, VOID *pPaddingInfo,
+ PBYTE pbOutput, DWORD cbOutput,
+ DWORD *pcbResult, DWORD dwFlags);
+
+typedef SECURITY_STATUS (WINAPI *NCryptSignHashFunc)(
+ NCRYPT_KEY_HANDLE hKey, VOID* pPaddingInfo,
+ PBYTE pbHashValue, DWORD cbHashValue,
+ PBYTE pbSignature, DWORD cbSignature,
+ DWORD* pcbResult, DWORD dwFlags);
+
+static int StrCmpW(const WCHAR *str1, const WCHAR *str2 )
+{
+ while (*str1 && (*str1 == *str2)) { str1++; str2++; }
+ return *str1 - *str2;
+}
+
+#ifdef DYN_NCRYPT
+static NCryptDeleteKeyFunc pNCryptDeleteKey;
+static NCryptOpenStorageProviderFunc pNCryptOpenStorageProvider;
+static NCryptOpenKeyFunc pNCryptOpenKey;
+static NCryptGetPropertyFunc pNCryptGetProperty;
+static NCryptFreeObjectFunc pNCryptFreeObject;
+static NCryptDecryptFunc pNCryptDecrypt;
+static NCryptSignHashFunc pNCryptSignHash;
+#else
+#define pNCryptDeleteKey NCryptDeleteKey
+#define pNCryptOpenStorageProvider NCryptOpenStorageProvider
+#define pNCryptOpenKey NCryptOpenKey
+#define pNCryptGetProperty NCryptGetProperty
+#define pNCryptFreeObject NCryptFreeObject
+#define pNCryptDecrypt NCryptDecrypt
+#define pNCryptSignHash NCryptSignHash
+#endif
+
+static unsigned ncrypt_init = 0;
+static HMODULE ncrypt_lib;
+
+#define WIN_URL SYSTEM_URL"win:"
+#define WIN_URL_SIZE 11
+
+static int
+get_id(const char *url, uint8_t *bin, size_t *bin_size, unsigned cert)
+{
+ int ret;
+ unsigned url_size = strlen(url);
+ const char *p = url, *p2;
+ gnutls_datum_t tmp;
+
+ if (cert != 0) {
+ if (url_size < sizeof(WIN_URL) || strncmp(url, WIN_URL, WIN_URL_SIZE) != 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ } else {
+ if (url_size < sizeof(WIN_URL) || strncmp(url, WIN_URL, WIN_URL_SIZE) != 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ p += sizeof(WIN_URL) - 1;
+
+ p = strstr(p, "id=");
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ p += 3;
+
+ p2 = strchr(p, ';');
+ if (p2 == NULL) {
+ url_size = strlen(p);
+ } else {
+ url_size = (p2 - p);
+ }
+
+ tmp.data = p;
+ tmp.size = url_size;
+ ret = gnutls_hex_decode(&tmp, bin, bin_size);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static
+void *memrev(unsigned char *pvData, DWORD cbData)
+{
+ char t;
+ DWORD i;
+
+ for (i = 0; i < cbData / 2; i++){
+ t = pvData[i];
+ pvData[i] = pvData[cbData - 1 - i];
+ pvData[cbData - 1 - i] = t;
+ }
+ return pvData;
+}
+
+static
+int capi_sign(gnutls_privkey_t key, void *userdata,
+ const gnutls_datum_t *raw_data,
+ gnutls_datum_t *signature)
+{
+ priv_st *priv = (priv_st*)userdata;
+ ALG_ID Algid;
+ HCRYPTHASH hHash = NULL;
+ uint8_t digest[MAX_HASH_SIZE];
+ unsigned int digest_size;
+ gnutls_digest_algorithm_t algo;
+ DWORD size1 = 0, sizesize = sizeof(DWORD);
+ DWORD ret_sig = 0;
+ int ret;
+
+ signature->data = NULL;
+ signature->size = 0;
+
+ digest_size = raw_data->size;
+
+ switch (digest_size) {
+ case 16: Algid = CALG_MD5; break;
+ //case 35: size=20; // DigestInfo SHA1
+ case 20: Algid = CALG_SHA1; break;
+ //case 51: size=32; // DigestInto SHA-256
+ case 32: Algid = CALG_SHA_256; break;
+ case 36: Algid = CALG_SSL3_SHAMD5; break;
+ case 48: Algid = CALG_SHA_384; break;
+ case 64: Algid = CALG_SHA_512; break;
+ default:
+ digest_size = sizeof(digest);
+ ret = decode_ber_digest_info(raw_data, &algo, digest, &digest_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ switch (algo) {
+ case GNUTLS_DIG_SHA1: Algid = CALG_SHA1; break;
+#ifdef NCRYPT_SHA224_ALGORITHM
+ case GNUTLS_DIG_SHA224: Algid = CALG_SHA_224; break;
+#endif
+ case GNUTLS_DIG_SHA256: Algid = CALG_SHA_256; break;
+ case GNUTLS_DIG_SHA384: Algid = CALG_SHA_384; break;
+ case GNUTLS_DIG_SHA512: Algid = CALG_SHA_512; break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+ }
+ }
+
+ if (!CryptCreateHash(priv->hCryptProv, Algid, 0, 0, &hHash)) {
+ gnutls_assert();
+ _gnutls_debug_log("error in create hash: %d\n", (int)GetLastError());
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+ if (!CryptSetHashParam(hHash, HP_HASHVAL, digest, 0)) {
+ gnutls_assert();
+ _gnutls_debug_log("error in set hash val: %d\n", (int)GetLastError());
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+
+ if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&size1, &sizesize, 0) ||
+ digest_size != size1) {
+ gnutls_assert();
+ _gnutls_debug_log("error in hash size: %d\n", (int)size1);
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+ if (!CryptSignHash(hHash, priv->dwKeySpec, NULL, 0, NULL, &ret_sig)) {
+ gnutls_assert();
+ _gnutls_debug_log("error in pre-signing: %d\n", (int)GetLastError());
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+ signature->size = ret_sig;
+ signature->data = (unsigned char*)gnutls_malloc(signature->size);
+
+ if (signature->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ if (!CryptSignHash(hHash, priv->dwKeySpec, NULL, 0, signature->data, &ret_sig)) {
+ gnutls_assert();
+ _gnutls_debug_log("error in signing: %d\n", (int)GetLastError());
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+ memrev(signature->data, signature->size);
+
+ CryptDestroyHash(hHash);
+ signature->size = ret_sig;
+
+ return 0;
+fail:
+ if (hHash != 0)
+ CryptDestroyHash(hHash);
+ gnutls_free(signature->data);
+ return ret;
+}
+
+static
+int capi_decrypt(gnutls_privkey_t key, void *userdata,
+ const gnutls_datum_t *ciphertext,
+ gnutls_datum_t *plaintext)
+{
+ priv_st *priv = (priv_st*)userdata;
+ DWORD size = 0;
+ int ret;
+
+ plaintext->data = NULL;
+ plaintext->size = 0;
+
+ if (priv->pk != GNUTLS_PK_RSA) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ plaintext->size = size = ciphertext->size;
+ plaintext->data = (unsigned char*)gnutls_malloc(plaintext->size);
+ if (plaintext->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ memcpy(plaintext->data, ciphertext->data, size);
+ if (0 == CryptDecrypt(priv->hCryptProv, 0, true, 0, plaintext->data, &size))
+ {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_DECRYPTION_FAILED;
+ goto fail;
+ }
+
+ return 0;
+fail:
+ gnutls_free(plaintext->data);
+ return ret;
+}
+
+static
+void capi_deinit(gnutls_privkey_t key, void *userdata)
+{
+ priv_st *priv = (priv_st*)userdata;
+ CryptReleaseContext(priv->hCryptProv, 0);
+ gnutls_free(priv);
+}
+
+static int capi_info(gnutls_privkey_t key, unsigned int flags, void *userdata)
+{
+ priv_st *priv = (priv_st*)userdata;
+
+ if (flags & GNUTLS_PRIVKEY_INFO_PK_ALGO)
+ return priv->pk;
+ if (flags & GNUTLS_PRIVKEY_INFO_SIGN_ALGO)
+ return priv->sign_algo;
+ return -1;
+}
+
+static
+int cng_sign(gnutls_privkey_t key, void *userdata,
+ const gnutls_datum_t *raw_data,
+ gnutls_datum_t *signature)
+{
+ priv_st *priv = userdata;
+ BCRYPT_PKCS1_PADDING_INFO _info;
+ void *info = NULL;
+ DWORD ret_sig = 0;
+ int ret;
+ DWORD flags = 0;
+ gnutls_datum_t data = {raw_data->data, raw_data->size};
+ uint8_t digest[MAX_HASH_SIZE];
+ unsigned int digest_size;
+ gnutls_digest_algorithm_t algo;
+ SECURITY_STATUS r;
+
+ signature->data = NULL;
+ signature->size = 0;
+
+ if (priv->pk == GNUTLS_PK_RSA) {
+
+ flags = BCRYPT_PAD_PKCS1;
+ info = &_info;
+
+ if (raw_data->size == 36) { /* TLS 1.0 MD5+SHA1 */
+ _info.pszAlgId = NULL;
+ } else {
+ digest_size = sizeof(digest);
+ ret = decode_ber_digest_info(raw_data, &algo, digest, &digest_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ switch(algo) {
+ case GNUTLS_DIG_SHA1:
+ _info.pszAlgId = NCRYPT_SHA1_ALGORITHM;
+ break;
+#ifdef NCRYPT_SHA224_ALGORITHM
+ case GNUTLS_DIG_SHA224:
+ _info.pszAlgId = NCRYPT_SHA224_ALGORITHM;
+ break;
+#endif
+ case GNUTLS_DIG_SHA256:
+ _info.pszAlgId = NCRYPT_SHA256_ALGORITHM;
+ break;
+ case GNUTLS_DIG_SHA384:
+ _info.pszAlgId = NCRYPT_SHA384_ALGORITHM;
+ break;
+ case GNUTLS_DIG_SHA512:
+ _info.pszAlgId = NCRYPT_SHA512_ALGORITHM;
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+ }
+ data.data = digest;
+ data.size = digest_size;
+ }
+ }
+
+ r = pNCryptSignHash(priv->nc, info, data.data, data.size,
+ NULL, 0, &ret_sig, flags);
+ if (FAILED(r)) {
+ gnutls_assert();
+ _gnutls_debug_log("error in pre-signing: %d\n", (int)GetLastError());
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+ signature->size = ret_sig;
+ signature->data = gnutls_malloc(signature->size);
+ if (signature->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ r = pNCryptSignHash(priv->nc, info, data.data, data.size,
+ signature->data, signature->size,
+ &ret_sig, flags);
+ if (FAILED(r)) {
+ gnutls_assert();
+ _gnutls_debug_log("error in signing: %d\n", (int)GetLastError());
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto fail;
+ }
+
+ signature->size = ret_sig;
+
+ return 0;
+ fail:
+ gnutls_free(signature->data);
+ return ret;
+}
+
+static
+int cng_decrypt(gnutls_privkey_t key, void *userdata,
+ const gnutls_datum_t *ciphertext,
+ gnutls_datum_t *plaintext)
+{
+ priv_st *priv = userdata;
+ SECURITY_STATUS r;
+ DWORD ret_dec = 0;
+ int ret;
+
+ plaintext->data = NULL;
+ plaintext->size = 0;
+
+ if (priv->pk != GNUTLS_PK_RSA) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ r = pNCryptDecrypt(priv->nc, ciphertext->data, ciphertext->size,
+ NULL, NULL, 0, &ret_dec, NCRYPT_PAD_PKCS1_FLAG);
+ if (FAILED(r)) {
+ gnutls_assert();
+ return GNUTLS_E_PK_DECRYPTION_FAILED;
+ }
+
+ plaintext->size = ret_dec;
+ plaintext->data = gnutls_malloc(plaintext->size);
+ if (plaintext->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ r = pNCryptDecrypt(priv->nc, ciphertext->data, ciphertext->size,
+ NULL, plaintext->data, plaintext->size,
+ &ret_dec, NCRYPT_PAD_PKCS1_FLAG);
+ if (FAILED(r)) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_DECRYPTION_FAILED;
+ goto fail;
+ }
+ plaintext->size = ret_dec;
+
+ return 0;
+ fail:
+ gnutls_free(plaintext->data);
+ return ret;
+}
+
+static
+void cng_deinit(gnutls_privkey_t key, void *userdata)
+{
+ priv_st *priv = userdata;
+ pNCryptFreeObject(priv->nc);
+ gnutls_free(priv);
+}
+
+static int cng_info(gnutls_privkey_t key, unsigned int flags, void *userdata)
+{
+ priv_st *priv = userdata;
+
+ if (flags & GNUTLS_PRIVKEY_INFO_PK_ALGO)
+ return priv->pk;
+ if (flags & GNUTLS_PRIVKEY_INFO_SIGN_ALGO)
+ return priv->sign_algo;
+ return -1;
+}
+
+/*-
+ * _gnutls_privkey_import_system:
+ * @pkey: The private key
+ * @url: The URL of the key
+ *
+ * This function will import the given private key to the abstract
+ * #gnutls_privkey_t type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ *
+ -*/
+int
+_gnutls_privkey_import_system_url(gnutls_privkey_t pkey,
+ const char *url)
+{
+ uint8_t id[MAX_WID_SIZE];
+ HCERTSTORE store = NULL;
+ size_t id_size;
+ const CERT_CONTEXT *cert = NULL;
+ CRYPT_HASH_BLOB blob;
+ CRYPT_KEY_PROV_INFO *kpi = NULL;
+ NCRYPT_KEY_HANDLE nc = NULL;
+ HCRYPTPROV hCryptProv = NULL;
+ NCRYPT_PROV_HANDLE sctx = NULL;
+ DWORD kpi_size;
+ SECURITY_STATUS r;
+ int ret, enc_too = 0;
+ WCHAR algo_str[64];
+ DWORD algo_str_size = 0;
+ priv_st *priv;
+ DWORD i,dwErrCode = 0;
+
+
+ if (ncrypt_init == 0)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ if (url == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ priv = gnutls_calloc(1, sizeof(*priv));
+ if (priv == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ id_size = sizeof(id);
+ ret = get_id(url, id, &id_size, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ blob.cbData = id_size;
+ blob.pbData = id;
+
+ store = CertOpenSystemStore(0, "MY");
+ if (store == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_FILE_ERROR;
+ goto cleanup;
+ }
+
+ cert = CertFindCertificateInStore(store,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_KEY_IDENTIFIER,
+ &blob,
+ NULL);
+
+ if (cert == NULL) {
+ char buf[64];
+ _gnutls_debug_log("cannot find ID: %s from %s\n",
+ _gnutls_bin2hex(id, id_size,
+ buf, sizeof(buf), NULL), url);
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ kpi_size = 0;
+ r = CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID,
+ NULL, &kpi_size);
+ if (r == 0) {
+ _gnutls_debug_log("error in getting context: %d from %s\n",
+ (int)GetLastError(), url);
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ kpi = gnutls_malloc(kpi_size);
+ if (kpi == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ r = CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID,
+ kpi, &kpi_size);
+ if (r == 0) {
+ _gnutls_debug_log("error in getting context: %d from %s\n",
+ (int)GetLastError(), url);
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ r = pNCryptOpenStorageProvider(&sctx, kpi->pwszProvName, 0);
+ if (!FAILED(r)) /* if this works carry on with CNG*/
+ {
+
+ r = pNCryptOpenKey(sctx, &nc, kpi->pwszContainerName, 0, 0);
+ if (FAILED(r)) {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ r = pNCryptGetProperty(nc, NCRYPT_ALGORITHM_PROPERTY,
+ (BYTE*)algo_str, sizeof(algo_str),
+ &algo_str_size, 0);
+ if (FAILED(r)) {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ if (StrCmpW(algo_str, BCRYPT_RSA_ALGORITHM) == 0) {
+ priv->pk = GNUTLS_PK_RSA;
+ priv->sign_algo = GNUTLS_SIGN_RSA_SHA256;
+ enc_too = 1;
+ } else if (StrCmpW(algo_str, BCRYPT_DSA_ALGORITHM) == 0) {
+ priv->pk = GNUTLS_PK_DSA;
+ priv->sign_algo = GNUTLS_SIGN_DSA_SHA1;
+ } else if (StrCmpW(algo_str, BCRYPT_ECDSA_P256_ALGORITHM) == 0) {
+ priv->pk = GNUTLS_PK_EC;
+ priv->sign_algo = GNUTLS_SIGN_ECDSA_SHA256;
+ } else if (StrCmpW(algo_str, BCRYPT_ECDSA_P384_ALGORITHM) == 0) {
+ priv->pk = GNUTLS_PK_EC;
+ priv->sign_algo = GNUTLS_SIGN_ECDSA_SHA384;
+ } else if (StrCmpW(algo_str, BCRYPT_ECDSA_P521_ALGORITHM) == 0) {
+ priv->pk = GNUTLS_PK_EC;
+ priv->sign_algo = GNUTLS_SIGN_ECDSA_SHA512;
+ } else {
+ _gnutls_debug_log("unknown key algorithm: %ls\n", algo_str);
+ ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
+ goto cleanup;
+ }
+ priv->nc = nc;
+
+ ret = gnutls_privkey_import_ext3(pkey, priv, cng_sign,
+ (enc_too!=0)?cng_decrypt:NULL,
+ cng_deinit,
+ cng_info, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ /* this should be CAPI*/
+ _gnutls_debug_log("error in opening CNG keystore: %x from %ls\n",
+ (int) r, kpi->pwszProvName);
+
+ if (CryptAcquireContextW(&hCryptProv,
+ kpi->pwszContainerName,
+ kpi->pwszProvName,
+ kpi->dwProvType,
+ kpi->dwFlags)) {
+ for (i = 0; i < kpi->cProvParam; i++)
+ if (!CryptSetProvParam(hCryptProv,
+ kpi->rgProvParam[i].dwParam,
+ kpi->rgProvParam[i].pbData,
+ kpi->rgProvParam[i].dwFlags))
+ {
+ dwErrCode = GetLastError();
+ break;
+ };
+ } else {
+ dwErrCode = GetLastError();
+ }
+
+ if (ERROR_SUCCESS != dwErrCode) {
+ _gnutls_debug_log("error in getting cryptprov: %d from %s\n",
+ (int)GetLastError(), url);
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ {
+ BYTE buf[100 + sizeof(PROV_ENUMALGS_EX) * 2];
+ PROV_ENUMALGS_EX *pAlgo = (PROV_ENUMALGS_EX *)buf;
+ DWORD len = sizeof(buf);
+
+ if (CryptGetProvParam(hCryptProv, PP_ENUMALGS_EX, buf, &len, CRYPT_FIRST)) {
+ DWORD hash = 0;
+ do {
+ switch (pAlgo->aiAlgid) {
+ case CALG_RSA_SIGN:
+ priv->pk = GNUTLS_PK_RSA;
+ enc_too = 1;
+ break;
+ case CALG_DSS_SIGN:
+ priv->pk = priv->pk == GNUTLS_PK_RSA ? GNUTLS_PK_RSA : GNUTLS_PK_DSA;
+ break;
+ case CALG_SHA1:
+ hash = 1;
+ break;
+ case CALG_SHA_256:
+ hash = 256;
+ break;
+ default:
+ break;
+ }
+
+ len = sizeof(buf); // reset the buffer size
+ } while (CryptGetProvParam(hCryptProv, PP_ENUMALGS_EX, buf, &len, CRYPT_NEXT));
+
+ if (priv->pk == GNUTLS_PK_DSA)
+ priv->sign_algo = GNUTLS_SIGN_DSA_SHA1;
+ else
+ priv->sign_algo = (hash > 1) ? GNUTLS_SIGN_RSA_SHA256 : GNUTLS_SIGN_RSA_SHA1;
+ }
+ }
+
+ priv->hCryptProv = hCryptProv;
+ priv->dwKeySpec = kpi->dwKeySpec;
+
+ ret = gnutls_privkey_import_ext3(pkey, priv, capi_sign,
+ (enc_too != 0) ? capi_decrypt : NULL,
+ capi_deinit,
+ capi_info, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ ret = 0;
+ cleanup:
+ if (ret < 0) {
+ if (nc != 0)
+ pNCryptFreeObject(nc);
+ if (hCryptProv != 0)
+ CryptReleaseContext(hCryptProv, 0);
+ gnutls_free(priv);
+ }
+ if (sctx != 0)
+ pNCryptFreeObject(sctx);
+
+ gnutls_free(kpi);
+
+ if (cert != 0)
+ CertFreeCertificateContext(cert);
+
+ CertCloseStore(store, 0);
+ return ret;
+}
+
+int
+_gnutls_x509_crt_import_system_url(gnutls_x509_crt_t crt, const char *url)
+{
+ uint8_t id[MAX_WID_SIZE];
+ HCERTSTORE store = NULL;
+ size_t id_size;
+ const CERT_CONTEXT *cert = NULL;
+ CRYPT_HASH_BLOB blob;
+ int ret;
+ gnutls_datum_t data;
+
+ if (ncrypt_init == 0)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ id_size = sizeof(id);
+ ret = get_id(url, id, &id_size, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ blob.cbData = id_size;
+ blob.pbData = id;
+
+ store = CertOpenSystemStore(0, "MY");
+ if (store == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_FILE_ERROR;
+ goto cleanup;
+ }
+
+ cert = CertFindCertificateInStore(store,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_KEY_IDENTIFIER,
+ &blob,
+ NULL);
+
+ if (cert == NULL) {
+ char buf[64];
+ _gnutls_debug_log("cannot find ID: %s from %s\n",
+ _gnutls_bin2hex(id, id_size,
+ buf, sizeof(buf), NULL),
+ url);
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ data.data = cert->pbCertEncoded;
+ data.size = cert->cbCertEncoded;
+
+ ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ if (cert != 0)
+ CertFreeCertificateContext(cert);
+
+ CertCloseStore(store, 0);
+ return ret;
+}
+
+/**
+ * gnutls_system_key_iter_deinit:
+ * @iter: an iterator of system keys
+ *
+ * This function will deinitialize the iterator.
+ *
+ * Since: 3.4.0
+ **/
+void gnutls_system_key_iter_deinit(gnutls_system_key_iter_t iter)
+{
+ if (ncrypt_init == 0)
+ return;
+
+ CertCloseStore(iter->store, 0);
+ gnutls_free(iter);
+}
+
+static
+int get_win_urls(const CERT_CONTEXT *cert, char **cert_url, char **key_url,
+ char **label, gnutls_datum_t *der)
+{
+ BOOL r;
+ int ret;
+ DWORD tl_size;
+ gnutls_datum_t tmp_label = {NULL, 0};
+ char name[MAX_CN*2];
+ char hex[MAX_WID_SIZE*2+1];
+ gnutls_buffer_st str;
+#ifdef WORDS_BIGENDIAN
+ const unsigned bigendian = 1;
+#else
+ const unsigned bigendian = 0;
+#endif
+
+ if (cert == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (der) {
+ der->data = gnutls_malloc(cert->cbCertEncoded);
+ if (der->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(der->data, cert->pbCertEncoded, cert->cbCertEncoded);
+ der->size = cert->cbCertEncoded;
+ }
+
+ _gnutls_buffer_init(&str);
+ if (label)
+ *label = NULL;
+ if (key_url)
+ *key_url = NULL;
+ if (cert_url)
+ *cert_url = NULL;
+
+
+ tl_size = sizeof(name);
+ r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID,
+ name, &tl_size);
+ if (r != 0) { /* optional */
+ ret = _gnutls_ucs2_to_utf8(name, tl_size, &tmp_label, bigendian);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ if (label)
+ *label = (char*)tmp_label.data;
+ }
+
+ tl_size = sizeof(name);
+ r = CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID,
+ name, &tl_size);
+ if (r == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto fail;
+ }
+
+ if (_gnutls_bin2hex(name, tl_size, hex, sizeof(hex), 0) == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ goto fail;
+ }
+
+ ret = _gnutls_buffer_append_printf(&str, WIN_URL"id=%s;type=cert", hex);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (tmp_label.data) {
+ ret = _gnutls_buffer_append_str(&str, ";name=");
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = _gnutls_buffer_append_escape(&str, tmp_label.data, tmp_label.size, " ");
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ ret = _gnutls_buffer_append_data(&str, "\x00", 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (cert_url)
+ *cert_url = (char*)str.data;
+ _gnutls_buffer_init(&str);
+
+ ret = _gnutls_buffer_append_printf(&str, WIN_URL"id=%s;type=privkey", hex);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (tmp_label.data) {
+ ret = _gnutls_buffer_append_str(&str, ";name=");
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = _gnutls_buffer_append_escape(&str, tmp_label.data, tmp_label.size, " ");
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ ret = _gnutls_buffer_append_data(&str, "\x00", 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (key_url)
+ *key_url = (char*)str.data;
+ _gnutls_buffer_init(&str);
+
+ ret = 0;
+ goto cleanup;
+
+ fail:
+ if (der)
+ gnutls_free(der->data);
+ if (cert_url)
+ gnutls_free(*cert_url);
+ if (key_url)
+ gnutls_free(*key_url);
+ if (label)
+ gnutls_free(*label);
+ cleanup:
+ _gnutls_buffer_clear(&str);
+ return ret;
+}
+
+/**
+ * gnutls_system_key_iter_get_info:
+ * @iter: an iterator of the system keys (must be set to %NULL initially)
+ * @cert_type: A value of gnutls_certificate_type_t which indicates the type of certificate to look for
+ * @cert_url: The certificate URL of the pair (may be %NULL)
+ * @key_url: The key URL of the pair (may be %NULL)
+ * @label: The friendly name (if any) of the pair (may be %NULL)
+ * @der: if non-NULL the DER data of the certificate
+ * @flags: should be zero
+ *
+ * This function will return on each call a certificate
+ * and key pair URLs, as well as a label associated with them,
+ * and the DER-encoded certificate. When the iteration is complete it will
+ * return %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ *
+ * Typically @cert_type should be %GNUTLS_CRT_X509.
+ *
+ * All values set are allocated and must be cleared using gnutls_free(),
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_system_key_iter_get_info(gnutls_system_key_iter_t *iter,
+ unsigned cert_type,
+ char **cert_url,
+ char **key_url,
+ char **label,
+ gnutls_datum_t *der,
+ unsigned int flags)
+{
+ if (ncrypt_init == 0)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ if (cert_type != GNUTLS_CRT_X509)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ if (*iter == NULL) {
+ *iter = gnutls_calloc(1, sizeof(struct system_key_iter_st));
+ if (*iter == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ (*iter)->store = CertOpenSystemStore(0, "MY");
+ if ((*iter)->store == NULL) {
+ gnutls_free(*iter);
+ *iter = NULL;
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ }
+
+ (*iter)->cert = CertEnumCertificatesInStore((*iter)->store, NULL);
+
+ return get_win_urls((*iter)->cert, cert_url, key_url, label, der);
+ } else {
+ if ((*iter)->cert == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ (*iter)->cert = CertEnumCertificatesInStore((*iter)->store, (*iter)->cert);
+ return get_win_urls((*iter)->cert, cert_url, key_url, label, der);
+
+ }
+}
+
+/**
+ * gnutls_system_key_delete:
+ * @cert_url: the URL of the certificate
+ * @key_url: the URL of the key
+ *
+ * This function will delete the key and certificate pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ **/
+int gnutls_system_key_delete(const char *cert_url, const char *key_url)
+{
+ uint8_t id[MAX_WID_SIZE];
+ HCERTSTORE store = NULL;
+ size_t id_size;
+ const CERT_CONTEXT *cert = NULL;
+ CRYPT_HASH_BLOB blob;
+ NCRYPT_KEY_HANDLE nc;
+ DWORD nc_size;
+ BOOL r;
+ int ret;
+
+ if (ncrypt_init == 0)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ if (cert_url == NULL && key_url == NULL)
+ return 0;
+
+ if (cert_url != NULL) {
+ id_size = sizeof(id);
+ ret = get_id(cert_url, id, &id_size, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ } else {
+ id_size = sizeof(id);
+ ret = get_id(key_url, id, &id_size, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ blob.cbData = id_size;
+ blob.pbData = id;
+
+ store = CertOpenSystemStore(0, "MY");
+ if (store != NULL) {
+ do {
+ cert = CertFindCertificateInStore(store,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_KEY_IDENTIFIER,
+ &blob,
+ cert);
+
+ if (cert && key_url) {
+ nc_size = sizeof(nc);
+ r = CertGetCertificateContextProperty(cert, CERT_NCRYPT_KEY_HANDLE_TRANSFER_PROP_ID,
+ &nc, &nc_size);
+ if (r != 0) {
+ pNCryptDeleteKey(nc, 0);
+ pNCryptFreeObject(nc);
+ } else {
+ gnutls_assert();
+ }
+ }
+
+ if (cert && cert_url)
+ CertDeleteCertificateFromStore(cert);
+ } while(cert != NULL);
+ CertCloseStore(store, 0);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_system_key_add_x509:
+ * @crt: the certificate to be added
+ * @privkey: the key to be added
+ * @label: the friendly name to describe the key
+ * @cert_url: if non-NULL it will contain an allocated value with the certificate URL
+ * @key_url: if non-NULL it will contain an allocated value with the key URL
+ *
+ * This function will added the given key and certificate pair,
+ * to the system list.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ **/
+int gnutls_system_key_add_x509(gnutls_x509_crt_t crt, gnutls_x509_privkey_t privkey,
+ const char *label, char **cert_url, char **key_url)
+{
+ HCERTSTORE store = NULL;
+ CRYPT_DATA_BLOB pfx;
+ gnutls_datum_t _pfx = {NULL, 0};
+ gnutls_pkcs12_t p12 = NULL;
+ gnutls_pkcs12_bag_t bag1 = NULL, bag2 = NULL;
+ uint8_t id[MAX_WID_SIZE];
+ size_t id_size;
+ gnutls_datum_t kid;
+ int ret;
+
+ if (ncrypt_init == 0)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ if (label == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ id_size = sizeof(id);
+ ret = gnutls_x509_crt_get_key_id(crt, 0, id, &id_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ kid.data = id;
+ kid.size = id_size;
+
+ /* the idea: import the cert and private key into PKCS #12
+ * format, export it into pfx, and import it into store */
+ ret = gnutls_pkcs12_init(&p12);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pkcs12_bag_init(&bag1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_bag_set_crt(bag1, crt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_bag_set_key_id(bag1, 0, &kid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (label)
+ gnutls_pkcs12_bag_set_friendly_name(bag1, 0, label);
+
+ ret = gnutls_pkcs12_bag_init(&bag2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_bag_set_privkey(bag2, privkey, NULL, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_bag_set_key_id(bag2, 0, &kid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (label)
+ gnutls_pkcs12_bag_set_friendly_name(bag2, 0, label);
+
+ ret = gnutls_pkcs12_set_bag(p12, bag1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_set_bag(p12, bag2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_generate_mac(p12, "123456");
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pkcs12_export2(p12, GNUTLS_X509_FMT_DER, &_pfx);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ pfx.cbData = _pfx.size;
+ pfx.pbData = _pfx.data;
+
+ store = PFXImportCertStore(&pfx, L"123456", 0);
+ if (store == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ if (cert_url || key_url) {
+ unsigned char sha[20];
+ CRYPT_HASH_BLOB blob;
+ const CERT_CONTEXT *cert = NULL;
+ gnutls_datum_t data;
+
+ ret = gnutls_x509_crt_export2(crt, GNUTLS_X509_FMT_DER, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_hash_fast(GNUTLS_DIG_SHA1, data.data, data.size, sha);
+ gnutls_free(data.data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ blob.cbData = sizeof(sha);
+ blob.pbData = sha;
+
+ cert = CertFindCertificateInStore(store,
+ X509_ASN_ENCODING,
+ 0,
+ CERT_FIND_SHA1_HASH,
+ &blob,
+ NULL);
+
+ if (cert == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_KEY_IMPORT_FAILED;
+ goto cleanup;
+ }
+
+ ret = get_win_urls(cert, cert_url, key_url, NULL, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (p12 != NULL)
+ gnutls_pkcs12_deinit(p12);
+ if (bag1 != NULL)
+ gnutls_pkcs12_bag_deinit(bag1);
+ if (bag2 != NULL)
+ gnutls_pkcs12_bag_deinit(bag2);
+ if (store != NULL)
+ CertCloseStore(store, 0);
+ gnutls_free(_pfx.data);
+ return ret;
+}
+
+int _gnutls_system_key_init(void)
+{
+ int ret;
+
+#ifdef DYN_NCRYPT
+ ncrypt_lib = LoadLibraryA("ncrypt.dll");
+ if (ncrypt_lib == NULL) {
+ return gnutls_assert_val(GNUTLS_E_CRYPTO_INIT_FAILED);
+ }
+
+ pNCryptDeleteKey = (NCryptDeleteKeyFunc)GetProcAddress(ncrypt_lib, "NCryptDeleteKey");
+ if (pNCryptDeleteKey == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+
+ pNCryptOpenStorageProvider = (NCryptOpenStorageProviderFunc)GetProcAddress(ncrypt_lib, "NCryptOpenStorageProvider");
+ if (pNCryptOpenStorageProvider == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+
+ pNCryptOpenKey = (NCryptOpenKeyFunc)GetProcAddress(ncrypt_lib, "NCryptOpenKey");
+ if (pNCryptOpenKey == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+
+ pNCryptGetProperty = (NCryptGetPropertyFunc)GetProcAddress(ncrypt_lib, "NCryptGetProperty");
+ if (pNCryptGetProperty == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+
+ pNCryptFreeObject = (NCryptFreeObjectFunc)GetProcAddress(ncrypt_lib, "NCryptFreeObject");
+ if (pNCryptFreeObject == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+
+ pNCryptDecrypt = (NCryptDecryptFunc)GetProcAddress(ncrypt_lib, "NCryptDecrypt");
+ if (pNCryptDecrypt == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+
+ pNCryptSignHash = (NCryptSignHashFunc)GetProcAddress(ncrypt_lib, "NCryptSignHash");
+ if (pNCryptSignHash == NULL) {
+ ret = GNUTLS_E_CRYPTO_INIT_FAILED;
+ goto fail;
+ }
+#endif
+ ncrypt_init = 1;
+
+ return 0;
+ fail:
+ FreeLibrary(ncrypt_lib);
+ return ret;
+}
+
+void _gnutls_system_key_deinit(void)
+{
+ if (ncrypt_init != 0) {
+ FreeLibrary(ncrypt_lib);
+ ncrypt_init = 0;
+ }
+}