From 4440e05d488e115bb4c398532fd24707f5dc1915 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Sun, 16 Mar 2014 02:00:17 +0100 Subject: [PATCH] Added Windows Cryptography API: Next Generation based backend --- Makefile.WinCNG.inc | 2 + configure.ac | 30 + src/Makefile.am | 3 + src/crypto.h | 4 + src/wincng.c | 1785 +++++++++++++++++++++++++++++++++++++++++++ src/wincng.h | 327 ++++++++ 6 files changed, 2151 insertions(+) create mode 100644 Makefile.WinCNG.inc create mode 100644 src/wincng.c create mode 100644 src/wincng.h diff --git a/Makefile.WinCNG.inc b/Makefile.WinCNG.inc new file mode 100644 index 0000000..c18350e --- /dev/null +++ b/Makefile.WinCNG.inc @@ -0,0 +1,2 @@ +CRYPTO_CSOURCES = wincng.c +CRYPTO_HHEADERS = wincng.h diff --git a/configure.ac b/configure.ac index b24ace2..8e52687 100644 --- a/configure.ac +++ b/configure.ac @@ -89,6 +89,9 @@ AC_ARG_WITH(openssl, AC_ARG_WITH(libgcrypt, AC_HELP_STRING([--with-libgcrypt],[Use libgcrypt for crypto]), use_libgcrypt=$withval,use_libgcrypt=auto) +AC_ARG_WITH(wincng, + AC_HELP_STRING([--with-wincng],[Use Windows CNG for crypto]), + use_wincng=$withval,use_wincng=auto) AC_ARG_WITH(libz, AC_HELP_STRING([--with-libz],[Use zlib for compression]), use_libz=$withval,use_libz=auto) @@ -125,10 +128,37 @@ if test "$ac_cv_libgcrypt" = "yes"; then fi AM_CONDITIONAL(LIBGCRYPT, test "$ac_cv_libgcrypt" = "yes") +# Look for Windows Cryptography API: Next Generation +if test "$found_crypto" = "none" && test "$use_wincng" != "no"; then + AC_LIB_HAVE_LINKFLAGS([bcrypt], [], [ + #include + #include + ]) + AC_LIB_HAVE_LINKFLAGS([crypt32], [], [ + #include + #include + ]) + AC_CHECK_HEADERS([ntdef.h ntstatus.h], [], [], [ + #include + ]) +fi +if test "$ac_cv_libbcrypt" = "yes"; then + AC_DEFINE(LIBSSH2_WINCNG, 1, [Use Windows CNG]) + LIBSREQUIRED= # wincng doesn't provide a .pc file. sad face. + LIBS="$LIBS -lbcrypt" + if test "$ac_cv_libcrypt32" = "yes"; then + LIBS="$LIBS -lcrypt32" + fi + found_crypto="Windows Cryptography API: Next Generation" +fi +AM_CONDITIONAL(WINCNG, test "$ac_cv_libbcrypt" = "yes") + +# Check if crypto library was found if test "$found_crypto" = "none"; then AC_MSG_ERROR([No crypto library found! Try --with-libssl-prefix=PATH\ or --with-libgcrypt-prefix=PATH\ + or --with-wincng on Windows\ ]) fi diff --git a/src/Makefile.am b/src/Makefile.am index da7beb5..5979a27 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,9 @@ endif if LIBGCRYPT include ../Makefile.libgcrypt.inc endif +if WINCNG +include ../Makefile.WinCNG.inc +endif # Makefile.inc provides the CSOURCES and HHEADERS defines include ../Makefile.inc diff --git a/src/crypto.h b/src/crypto.h index 9a052e3..a615bb1 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -46,6 +46,10 @@ #include "libgcrypt.h" #endif +#ifdef LIBSSH2_WINCNG +#include "wincng.h" +#endif + int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, const unsigned char *edata, unsigned long elen, diff --git a/src/wincng.c b/src/wincng.c new file mode 100644 index 0000000..398fe89 --- /dev/null +++ b/src/wincng.c @@ -0,0 +1,1785 @@ +/* + * Copyright (C) 2013-2014 Marc Hoersken + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" + +#ifdef LIBSSH2_WINCNG /* compile only if we build with wincng */ + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_LIBCRYPT32 +#include +#endif + + +/*******************************************************************/ +/* + * Windows CNG backend: Missing definitions (for MinGW[-w64]) + */ +#ifndef BCRYPT_RNG_ALGORITHM +#define BCRYPT_RNG_ALGORITHM L"RNG" +#endif + +#ifndef BCRYPT_MD5_ALGORITHM +#define BCRYPT_MD5_ALGORITHM L"MD5" +#endif + +#ifndef BCRYPT_SHA1_ALGORITHM +#define BCRYPT_SHA1_ALGORITHM L"SHA1" +#endif + +#ifndef BCRYPT_RSA_ALGORITHM +#define BCRYPT_RSA_ALGORITHM L"RSA" +#endif + +#ifndef BCRYPT_DSA_ALGORITHM +#define BCRYPT_DSA_ALGORITHM L"DSA" +#endif + +#ifndef BCRYPT_AES_ALGORITHM +#define BCRYPT_AES_ALGORITHM L"AES" +#endif + +#ifndef BCRYPT_RC4_ALGORITHM +#define BCRYPT_RC4_ALGORITHM L"RC4" +#endif + +#ifndef BCRYPT_3DES_ALGORITHM +#define BCRYPT_3DES_ALGORITHM L"3DES" +#endif + +#ifndef BCRYPT_ALG_HANDLE_HMAC_FLAG +#define BCRYPT_ALG_HANDLE_HMAC_FLAG 0x00000008 +#endif + +#ifndef BCRYPT_DSA_PUBLIC_BLOB +#define BCRYPT_DSA_PUBLIC_BLOB L"DSAPUBLICBLOB" +#endif + +#ifndef BCRYPT_DSA_PUBLIC_MAGIC +#define BCRYPT_DSA_PUBLIC_MAGIC 0x42505344 /* DSPB */ +#endif + +#ifndef BCRYPT_DSA_PRIVATE_BLOB +#define BCRYPT_DSA_PRIVATE_BLOB L"DSAPRIVATEBLOB" +#endif + +#ifndef BCRYPT_DSA_PRIVATE_MAGIC +#define BCRYPT_DSA_PRIVATE_MAGIC 0x56505344 /* DSPV */ +#endif + +#ifndef BCRYPT_RSAPUBLIC_BLOB +#define BCRYPT_RSAPUBLIC_BLOB L"RSAPUBLICBLOB" +#endif + +#ifndef BCRYPT_RSAPUBLIC_MAGIC +#define BCRYPT_RSAPUBLIC_MAGIC 0x31415352 /* RSA1 */ +#endif + +#ifndef BCRYPT_RSAFULLPRIVATE_BLOB +#define BCRYPT_RSAFULLPRIVATE_BLOB L"RSAFULLPRIVATEBLOB" +#endif + +#ifndef BCRYPT_RSAFULLPRIVATE_MAGIC +#define BCRYPT_RSAFULLPRIVATE_MAGIC 0x33415352 /* RSA3 */ +#endif + +#ifndef BCRYPT_KEY_DATA_BLOB +#define BCRYPT_KEY_DATA_BLOB L"KeyDataBlob" +#endif + +#ifndef BCRYPT_MESSAGE_BLOCK_LENGTH +#define BCRYPT_MESSAGE_BLOCK_LENGTH L"MessageBlockLength" +#endif + +#ifndef BCRYPT_NO_KEY_VALIDATION +#define BCRYPT_NO_KEY_VALIDATION 0x00000008 +#endif + +#ifndef BCRYPT_BLOCK_PADDING +#define BCRYPT_BLOCK_PADDING 0x00000001 +#endif + +#ifndef BCRYPT_PAD_NONE +#define BCRYPT_PAD_NONE 0x00000001 +#endif + +#ifndef BCRYPT_PAD_PKCS1 +#define BCRYPT_PAD_PKCS1 0x00000002 +#endif + +#ifndef BCRYPT_PAD_OAEP +#define BCRYPT_PAD_OAEP 0x00000004 +#endif + +#ifndef BCRYPT_PAD_PSS +#define BCRYPT_PAD_PSS 0x00000008 +#endif + +#ifndef CRYPT_STRING_ANY +#define CRYPT_STRING_ANY 0x00000007 +#endif + +#ifndef LEGACY_RSAPRIVATE_BLOB +#define LEGACY_RSAPRIVATE_BLOB L"CAPIPRIVATEBLOB" +#endif + +#ifndef PKCS_RSA_PRIVATE_KEY +#define PKCS_RSA_PRIVATE_KEY (LPCSTR)43 +#endif + + +/*******************************************************************/ +/* + * Windows CNG backend: Generic functions + */ + +void +_libssh2_wincng_init(void) +{ + int ret; + + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRNG, + BCRYPT_RNG_ALGORITHM, NULL, 0); + + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashMD5, + BCRYPT_MD5_ALGORITHM, NULL, 0); + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA1, + BCRYPT_SHA1_ALGORITHM, NULL, 0); + + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacMD5, + BCRYPT_MD5_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA1, + BCRYPT_SHA1_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRSA, + BCRYPT_RSA_ALGORITHM, NULL, 0); + BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDSA, + BCRYPT_DSA_ALGORITHM, NULL, 0); + + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgAES_CBC, + BCRYPT_AES_ALGORITHM, NULL, 0); + if (ret == STATUS_SUCCESS) { + ret = BCryptSetProperty(_libssh2_wincng.hAlgAES_CBC, BCRYPT_CHAINING_MODE, + (PBYTE)BCRYPT_CHAIN_MODE_CBC, + sizeof(BCRYPT_CHAIN_MODE_CBC), 0); + if (ret != STATUS_SUCCESS) { + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); + } + } + + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRC4_NA, + BCRYPT_RC4_ALGORITHM, NULL, 0); + if (ret == STATUS_SUCCESS) { + ret = BCryptSetProperty(_libssh2_wincng.hAlgRC4_NA, BCRYPT_CHAINING_MODE, + (PBYTE)BCRYPT_CHAIN_MODE_NA, + sizeof(BCRYPT_CHAIN_MODE_NA), 0); + if (ret != STATUS_SUCCESS) { + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); + } + } + + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlg3DES_CBC, + BCRYPT_3DES_ALGORITHM, NULL, 0); + if (ret == STATUS_SUCCESS) { + ret = BCryptSetProperty(_libssh2_wincng.hAlg3DES_CBC, BCRYPT_CHAINING_MODE, + (PBYTE)BCRYPT_CHAIN_MODE_CBC, + sizeof(BCRYPT_CHAIN_MODE_CBC), 0); + if (ret != STATUS_SUCCESS) { + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0); + } + } +} + +void +_libssh2_wincng_free(void) +{ + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRNG, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashMD5, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA1, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacMD5, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA1, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRSA, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDSA, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); + BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0); + + memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng)); +} + +int +_libssh2_wincng_random(void *buf, int len) +{ + return BCryptGenRandom(_libssh2_wincng.hAlgRNG, buf, len, 0) + == STATUS_SUCCESS ? 0 : -1; +} + + +/*******************************************************************/ +/* + * Windows CNG backend: Hash functions + */ + +int +_libssh2_wincng_hash_init(_libssh2_wincng_hash_ctx *ctx, + BCRYPT_ALG_HANDLE hAlg, unsigned long hashlen, + unsigned char *key, unsigned long keylen) +{ + BCRYPT_HASH_HANDLE hHash; + unsigned char *pbHashObject; + unsigned long dwHashObject, dwHash, cbData; + int ret; + + ret = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, + (unsigned char *)&dwHash, + sizeof(dwHash), + &cbData, 0); + if (ret != STATUS_SUCCESS || dwHash != hashlen) { + return -1; + } + + ret = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, + (unsigned char *)&dwHashObject, + sizeof(dwHashObject), + &cbData, 0); + if (ret != STATUS_SUCCESS) { + return -1; + } + + pbHashObject = malloc(dwHashObject); + if (!pbHashObject) { + return -1; + } + + + ret = BCryptCreateHash(hAlg, &hHash, + pbHashObject, dwHashObject, + key, keylen, 0); + if (ret != STATUS_SUCCESS) { + free(pbHashObject); + return -1; + } + + + ctx->hHash = hHash; + ctx->pbHashObject = pbHashObject; + ctx->dwHashObject = dwHashObject; + ctx->cbHash = dwHash; + + return 0; +} + +int +_libssh2_wincng_hash_update(_libssh2_wincng_hash_ctx *ctx, + unsigned char *data, unsigned long datalen) +{ + return BCryptHashData(ctx->hHash, data, datalen, 0) + == STATUS_SUCCESS ? 0 : -1; +} + +int +_libssh2_wincng_hash_final(_libssh2_wincng_hash_ctx *ctx, + unsigned char *hash) +{ + int ret; + + ret = BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0); + + BCryptDestroyHash(ctx->hHash); + + if (ctx->pbHashObject) + free(ctx->pbHashObject); + + memset(ctx, 0, sizeof(_libssh2_wincng_hash_ctx)); + + return ret; +} + +int +_libssh2_wincng_hash(unsigned char *data, unsigned long datalen, + BCRYPT_ALG_HANDLE hAlg, + unsigned char *hash, unsigned long hashlen) +{ + _libssh2_wincng_hash_ctx ctx; + + if (!_libssh2_wincng_hash_init(&ctx, hAlg, hashlen, NULL, 0)) { + if (!_libssh2_wincng_hash_update(&ctx, data, datalen)) { + if (!_libssh2_wincng_hash_final(&ctx, hash)) { + return 0; + } + } + } + + return -1; +} + + +/*******************************************************************/ +/* + * Windows CNG backend: HMAC functions + */ + +int +_libssh2_wincng_hmac_final(_libssh2_wincng_hash_ctx *ctx, + unsigned char *hash) +{ + return BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0) + == STATUS_SUCCESS ? 0 : -1; +} + +void +_libssh2_wincng_hmac_cleanup(_libssh2_wincng_hash_ctx *ctx) +{ + BCryptDestroyHash(ctx->hHash); + + if (ctx->pbHashObject) + free(ctx->pbHashObject); + + memset(ctx, 0, sizeof(_libssh2_wincng_hash_ctx)); +} + + +/*******************************************************************/ +/* + * Windows CNG backend: Key functions + */ + +int +_libssh2_wincng_key_sha1_verify(_libssh2_wincng_key_ctx *ctx, + const unsigned char *sig, + unsigned long sig_len, + const unsigned char *m, + unsigned long m_len, + unsigned long flags) +{ + BCRYPT_PKCS1_PADDING_INFO paddingInfoPKCS1; + void *pPaddingInfo; + unsigned char *data, *hash; + unsigned long datalen, hashlen; + int ret; + + datalen = m_len; + data = malloc(datalen); + if (!data) { + return -1; + } + + hashlen = SHA_DIGEST_LENGTH; + hash = malloc(hashlen); + if (!hash) { + free(data); + return -1; + } + + memcpy(data, m, datalen); + + ret = _libssh2_wincng_hash(data, datalen, + _libssh2_wincng.hAlgHashSHA1, + hash, hashlen); + + free(data); + + if (ret) { + free(hash); + return -1; + } + + datalen = sig_len; + data = malloc(datalen); + if (!data) { + free(hash); + return -1; + } + + if (flags & BCRYPT_PAD_PKCS1) { + paddingInfoPKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM; + pPaddingInfo = &paddingInfoPKCS1; + } + + memcpy(data, sig, datalen); + + ret = BCryptVerifySignature(ctx->hKey, pPaddingInfo, + hash, hashlen, data, datalen, flags); + + free(hash); + free(data); + + return ret == STATUS_SUCCESS ? 0 : -1; +} + +#ifdef HAVE_LIBCRYPT32 +static int +_libssh2_wincng_load_pem(LIBSSH2_SESSION *session, + const char *filename, + const char *passphrase, + const char *headerbegin, + const char *headerend, + unsigned char **data, + unsigned int *datalen) +{ + FILE *fp; + int ret; + + (void)passphrase; + + fp = fopen(filename, "r"); + if (!fp) { + return -1; + } + + ret = _libssh2_pem_parse(session, headerbegin, headerend, + fp, data, datalen); + + fclose(fp); + + return ret; +} + +static int +_libssh2_wincng_load_private(LIBSSH2_SESSION *session, + const char *filename, + const char *passphrase, + unsigned char **ppbEncoded, + unsigned long *pcbEncoded) +{ + unsigned char *data; + int ret, datalen; + + ret = _libssh2_wincng_load_pem(session, filename, passphrase, + "-----BEGIN RSA PRIVATE KEY-----", + "-----END RSA PRIVATE KEY-----", + &data, &datalen); + + if (ret) { + ret = _libssh2_wincng_load_pem(session, filename, passphrase, + "-----BEGIN DSA PRIVATE KEY-----", + "-----END DSA PRIVATE KEY-----", + &data, &datalen); + } + + if (!ret) { + *ppbEncoded = data; + *pcbEncoded = datalen; + } + + return ret; +} + +static int +_libssh2_wincng_asn_decode(unsigned char *pbEncoded, + unsigned long cbEncoded, + LPCSTR lpszStructType, + unsigned char **ppbDecoded, + unsigned long *pcbDecoded) +{ + unsigned char *pbDecoded; + unsigned long cbDecoded; + int ret; + + ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + lpszStructType, + pbEncoded, cbEncoded, 0, NULL, + NULL, &cbDecoded); + if (!ret) { + return -1; + } + + pbDecoded = malloc(cbDecoded); + if (!pbDecoded) { + return -1; + } + + ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + lpszStructType, + pbEncoded, cbEncoded, 0, NULL, + pbDecoded, &cbDecoded); + if (!ret) { + free(pbDecoded); + return -1; + } + + + *ppbDecoded = pbDecoded; + *pcbDecoded = cbDecoded; + + return 0; +} + +static int +_libssh2_wincng_bn_ltob(unsigned char *pbInput, + unsigned long cbInput, + unsigned char **ppbOutput, + unsigned long *pcbOutput) +{ + unsigned char *pbOutput; + unsigned long cbOutput, index, offset, length; + + if (cbInput < 1) { + return 0; + } + + offset = 0; + length = cbInput - 1; + cbOutput = cbInput; + if (pbInput[length] & (1 << 7)) { + offset++; + cbOutput++; + } + + pbOutput = malloc(cbOutput); + if (!pbOutput) { + return -1; + } + + pbOutput[0] = 0; + for (index = 0; index < cbInput; index++) { + pbOutput[index + offset] = pbInput[length - index]; + } + + + *ppbOutput = pbOutput; + *pcbOutput = cbOutput; + + return 0; +} + +static int +_libssh2_wincng_asn_decode_bn(unsigned char *pbEncoded, + unsigned long cbEncoded, + unsigned char **ppbDecoded, + unsigned long *pcbDecoded) +{ + unsigned char *pbDecoded, *pbInteger; + unsigned long cbDecoded, cbInteger; + int ret; + + ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded, + X509_MULTI_BYTE_UINT, + &pbInteger, &cbInteger); + if (!ret) { + ret = _libssh2_wincng_bn_ltob(((PCRYPT_DATA_BLOB)pbInteger)->pbData, + ((PCRYPT_DATA_BLOB)pbInteger)->cbData, + &pbDecoded, &cbDecoded); + if (!ret) { + *ppbDecoded = pbDecoded; + *pcbDecoded = cbDecoded; + } + free(pbInteger); + } + + return ret; +} + +static int +_libssh2_wincng_asn_decode_bns(unsigned char *pbEncoded, + unsigned long cbEncoded, + unsigned char ***prpbDecoded, + unsigned long **prcbDecoded, + unsigned long *pcbCount) +{ + PCRYPT_DER_BLOB pBlob; + unsigned char *pbDecoded, **rpbDecoded; + unsigned long cbDecoded, *rcbDecoded, index, length; + int ret; + + ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded, + X509_SEQUENCE_OF_ANY, + &pbDecoded, &cbDecoded); + if (!ret) { + length = ((PCRYPT_DATA_BLOB)pbDecoded)->cbData; + + rpbDecoded = malloc(sizeof(PBYTE) * length); + if (rpbDecoded) { + rcbDecoded = malloc(sizeof(DWORD) * length); + if (rcbDecoded) { + for (index = 0; index < length; index++) { + pBlob = &((PCRYPT_DER_BLOB) + ((PCRYPT_DATA_BLOB)pbDecoded)->pbData)[index]; + ret = _libssh2_wincng_asn_decode_bn(pBlob->pbData, + pBlob->cbData, + &rpbDecoded[index], + &rcbDecoded[index]); + if (ret) + break; + } + + if (ret) { + for (length = 0; length < index; length++) { + if (rpbDecoded[length]) { + free(rpbDecoded[length]); + rpbDecoded[length] = NULL; + } + } + } else { + *prpbDecoded = rpbDecoded; + *prcbDecoded = rcbDecoded; + *pcbCount = length; + } + } else { + free(rpbDecoded); + ret = -1; + } + + } else { + ret = -1; + } + + free(pbDecoded); + } + + return ret; +} +#endif /* HAVE_LIBCRYPT32 */ + +static unsigned long +_libssh2_wincng_bn_size(const unsigned char *bignum, + unsigned long length) +{ + unsigned long offset; + + if (!bignum) + return 0; + + length--; + + offset = 0; + while (!(*(bignum + offset)) && (offset < length)) + offset++; + + length++; + + return length - offset; +} + + +/*******************************************************************/ +/* + * Windows CNG backend: RSA functions + */ + +int +_libssh2_wincng_rsa_new(libssh2_rsa_ctx **rsa, + const unsigned char *edata, + unsigned long elen, + const unsigned char *ndata, + unsigned long nlen, + const unsigned char *ddata, + unsigned long dlen, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *e1data, + unsigned long e1len, + const unsigned char *e2data, + unsigned long e2len, + const unsigned char *coeffdata, + unsigned long coefflen) +{ + BCRYPT_KEY_HANDLE hKey; + BCRYPT_RSAKEY_BLOB *rsakey; + LPCWSTR lpszBlobType; + unsigned char *key; + unsigned long keylen, offset, mlen, p1len, p2len; + int ret; + + mlen = max(_libssh2_wincng_bn_size(ndata, nlen), + _libssh2_wincng_bn_size(ddata, dlen)); + offset = sizeof(BCRYPT_RSAKEY_BLOB); + keylen = offset + elen + mlen; + if (ddata && dlen > 0) { + p1len = max(_libssh2_wincng_bn_size(pdata, plen), + _libssh2_wincng_bn_size(e1data, e1len)); + p2len = max(_libssh2_wincng_bn_size(qdata, qlen), + _libssh2_wincng_bn_size(e2data, e2len)); + keylen += p1len * 3 + p2len * 2 + mlen; + } + + key = malloc(keylen); + if (!key) { + return -1; + } + + memset(key, 0, keylen); + + + /* http://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */ + rsakey = (BCRYPT_RSAKEY_BLOB *)key; + rsakey->BitLength = mlen * 8; + rsakey->cbPublicExp = elen; + rsakey->cbModulus = mlen; + + memcpy(key + offset, edata, elen); + offset += elen; + + if (nlen < mlen) + memcpy(key + offset + mlen - nlen, ndata, nlen); + else + memcpy(key + offset, ndata + nlen - mlen, mlen); + + if (ddata && dlen > 0) { + offset += mlen; + + if (plen < p1len) + memcpy(key + offset + p1len - plen, pdata, plen); + else + memcpy(key + offset, pdata + plen - p1len, p1len); + offset += p1len; + + if (qlen < p2len) + memcpy(key + offset + p2len - qlen, qdata, qlen); + else + memcpy(key + offset, qdata + qlen - p2len, p2len); + offset += p2len; + + if (e1len < p1len) + memcpy(key + offset + p1len - e1len, e1data, e1len); + else + memcpy(key + offset, e1data + e1len - p1len, p1len); + offset += p1len; + + if (e2len < p2len) + memcpy(key + offset + p2len - e2len, e2data, e2len); + else + memcpy(key + offset, e2data + e2len - p2len, p2len); + offset += p2len; + + if (coefflen < p1len) + memcpy(key + offset + p1len - coefflen, coeffdata, coefflen); + else + memcpy(key + offset, coeffdata + coefflen - p1len, p1len); + offset += p1len; + + if (dlen < mlen) + memcpy(key + offset + mlen - dlen, ddata, dlen); + else + memcpy(key + offset, ddata + dlen - mlen, mlen); + + lpszBlobType = BCRYPT_RSAFULLPRIVATE_BLOB; + rsakey->Magic = BCRYPT_RSAFULLPRIVATE_MAGIC; + rsakey->cbPrime1 = p1len; + rsakey->cbPrime2 = p2len; + } else { + lpszBlobType = BCRYPT_RSAPUBLIC_BLOB; + rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC; + rsakey->cbPrime1 = 0; + rsakey->cbPrime2 = 0; + } + + + ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, lpszBlobType, + &hKey, key, keylen, 0); + if (ret != STATUS_SUCCESS) { + free(key); + return -1; + } + + + *rsa = malloc(sizeof(libssh2_rsa_ctx)); + if (!(*rsa)) { + BCryptDestroyKey(hKey); + free(key); + return -1; + } + + (*rsa)->hKey = hKey; + (*rsa)->pbKeyObject = key; + (*rsa)->cbKeyObject = keylen; + + return 0; +} + +int +_libssh2_wincng_rsa_new_private(libssh2_rsa_ctx **rsa, + LIBSSH2_SESSION *session, + const char *filename, + const unsigned char *passphrase) +{ +#ifdef HAVE_LIBCRYPT32 + BCRYPT_KEY_HANDLE hKey; + unsigned char *pbEncoded, *pbStructInfo; + unsigned long cbEncoded, cbStructInfo; + int ret; + + (void)session; + + ret = _libssh2_wincng_load_private(session, filename, + (const char *)passphrase, + &pbEncoded, &cbEncoded); + if (ret) { + return -1; + } + + ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded, + PKCS_RSA_PRIVATE_KEY, + &pbStructInfo, &cbStructInfo); + + free(pbEncoded); + + if (ret) { + return -1; + } + + + ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, + LEGACY_RSAPRIVATE_BLOB, &hKey, + pbStructInfo, cbStructInfo, 0); + if (ret != STATUS_SUCCESS) { + free(pbStructInfo); + return -1; + } + + + *rsa = malloc(sizeof(libssh2_rsa_ctx)); + if (!(*rsa)) { + BCryptDestroyKey(hKey); + free(pbStructInfo); + return -1; + } + + (*rsa)->hKey = hKey; + (*rsa)->pbKeyObject = pbStructInfo; + (*rsa)->cbKeyObject = cbStructInfo; + + return 0; +#else + (void)rsa; + (void)filename; + (void)passphrase; + + return _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to load RSA key from private key file: " + "Method unsupported in Windows CNG backend"); +#endif /* HAVE_LIBCRYPT32 */ +} + +int +_libssh2_wincng_rsa_sha1_verify(libssh2_rsa_ctx *rsa, + const unsigned char *sig, + unsigned long sig_len, + const unsigned char *m, + unsigned long m_len) +{ + return _libssh2_wincng_key_sha1_verify(rsa, sig, sig_len, m, m_len, + BCRYPT_PAD_PKCS1); +} + +int +_libssh2_wincng_rsa_sha1_sign(LIBSSH2_SESSION *session, + libssh2_rsa_ctx *rsa, + const unsigned char *hash, + size_t hash_len, + unsigned char **signature, + size_t *signature_len) +{ + BCRYPT_PKCS1_PADDING_INFO paddingInfo; + unsigned char *data, *sig; + unsigned long cbData, datalen, siglen; + int ret; + + datalen = (unsigned long)hash_len; + data = malloc(datalen); + if (!data) { + return -1; + } + + paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM; + + memcpy(data, hash, datalen); + + ret = BCryptSignHash(rsa->hKey, &paddingInfo, + data, datalen, NULL, 0, + &cbData, BCRYPT_PAD_PKCS1); + if (ret == STATUS_SUCCESS) { + siglen = cbData; + sig = LIBSSH2_ALLOC(session, siglen); + if (sig) { + ret = BCryptSignHash(rsa->hKey, &paddingInfo, + data, datalen, sig, siglen, + &cbData, BCRYPT_PAD_PKCS1); + if (ret == STATUS_SUCCESS) { + *signature_len = siglen; + *signature = sig; + } else { + LIBSSH2_FREE(session, sig); + } + } else + ret = STATUS_NO_MEMORY; + } + + free(data); + + return ret == STATUS_SUCCESS ? 0 : -1; +} + +void +_libssh2_wincng_rsa_free(libssh2_rsa_ctx *rsa) +{ + if (!rsa) + return; + + BCryptDestroyKey(rsa->hKey); + + if (rsa->pbKeyObject) + free(rsa->pbKeyObject); + + memset(rsa, 0, sizeof(libssh2_rsa_ctx)); + free(rsa); +} + + +/*******************************************************************/ +/* + * Windows CNG backend: DSA functions + */ + +#if LIBSSH2_DSA +int +_libssh2_wincng_dsa_new(libssh2_dsa_ctx **dsa, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *gdata, + unsigned long glen, + const unsigned char *ydata, + unsigned long ylen, + const unsigned char *xdata, + unsigned long xlen) +{ + BCRYPT_KEY_HANDLE hKey; + BCRYPT_DSA_KEY_BLOB *dsakey; + LPCWSTR lpszBlobType; + unsigned char *key; + unsigned long keylen, offset, length; + int ret; + + length = max(max(_libssh2_wincng_bn_size(pdata, plen), + _libssh2_wincng_bn_size(gdata, glen)), + _libssh2_wincng_bn_size(ydata, ylen)); + offset = sizeof(BCRYPT_DSA_KEY_BLOB); + keylen = offset + length * 3; + if (xdata && xlen > 0) + keylen += 20; + + key = malloc(keylen); + if (!key) { + return -1; + } + + memset(key, 0, keylen); + + + /* http://msdn.microsoft.com/library/windows/desktop/aa833126.aspx */ + dsakey = (BCRYPT_DSA_KEY_BLOB *)key; + dsakey->cbKey = length; + + memset(dsakey->Count, -1, sizeof(dsakey->Count)); + memset(dsakey->Seed, -1, sizeof(dsakey->Seed)); + + if (qlen < 20) + memcpy(dsakey->q + 20 - qlen, qdata, qlen); + else + memcpy(dsakey->q, qdata + qlen - 20, 20); + + if (plen < length) + memcpy(key + offset + length - plen, pdata, plen); + else + memcpy(key + offset, pdata + plen - length, length); + offset += length; + + if (glen < length) + memcpy(key + offset + length - glen, gdata, glen); + else + memcpy(key + offset, gdata + glen - length, length); + offset += length; + + if (ylen < length) + memcpy(key + offset + length - ylen, ydata, ylen); + else + memcpy(key + offset, ydata + ylen - length, length); + + if (xdata && xlen > 0) { + offset += length; + + if (xlen < 20) + memcpy(key + offset + 20 - xlen, xdata, xlen); + else + memcpy(key + offset, xdata + xlen - 20, 20); + + lpszBlobType = BCRYPT_DSA_PRIVATE_BLOB; + dsakey->dwMagic = BCRYPT_DSA_PRIVATE_MAGIC; + } else { + lpszBlobType = BCRYPT_DSA_PUBLIC_BLOB; + dsakey->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC; + } + + + ret = BCryptImportKeyPair(_libssh2_wincng.hAlgDSA, NULL, lpszBlobType, + &hKey, key, keylen, 0); + if (ret != STATUS_SUCCESS) { + free(key); + return -1; + } + + + *dsa = malloc(sizeof(libssh2_dsa_ctx)); + if (!(*dsa)) { + BCryptDestroyKey(hKey); + free(key); + return -1; + } + + (*dsa)->hKey = hKey; + (*dsa)->pbKeyObject = key; + (*dsa)->cbKeyObject = keylen; + + return 0; +} + +int +_libssh2_wincng_dsa_new_private(libssh2_dsa_ctx **dsa, + LIBSSH2_SESSION *session, + const char *filename, + const unsigned char *passphrase) +{ +#ifdef HAVE_LIBCRYPT32 + unsigned char *pbEncoded, **rpbDecoded; + unsigned long cbEncoded, *rcbDecoded, index, length; + int ret; + + (void)session; + + ret = _libssh2_wincng_load_private(session, filename, + (const char *)passphrase, + &pbEncoded, &cbEncoded); + if (ret) { + return -1; + } + + ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded, + &rpbDecoded, &rcbDecoded, &length); + + free(pbEncoded); + + if (ret) { + return -1; + } + + + if (length == 6) { + ret = _libssh2_wincng_dsa_new(dsa, + rpbDecoded[1], rcbDecoded[1], + rpbDecoded[2], rcbDecoded[2], + rpbDecoded[3], rcbDecoded[3], + rpbDecoded[4], rcbDecoded[4], + rpbDecoded[5], rcbDecoded[5]); + } else { + ret = -1; + } + + for (index = 0; index < length; index++) { + if (rpbDecoded[index]) { + free(rpbDecoded[index]); + rpbDecoded[index] = NULL; + } + } + + free(rpbDecoded); + free(rcbDecoded); + + return ret; +#else + (void)dsa; + (void)filename; + (void)passphrase; + + return _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to load DSA key from private key file: " + "Method unsupported in Windows CNG backend"); +#endif /* HAVE_LIBCRYPT32 */ +} + +int +_libssh2_wincng_dsa_sha1_verify(libssh2_dsa_ctx *dsa, + const unsigned char *sig_fixed, + const unsigned char *m, + unsigned long m_len) +{ + return _libssh2_wincng_key_sha1_verify(dsa, sig_fixed, 40, m, m_len, 0); +} + +int +_libssh2_wincng_dsa_sha1_sign(libssh2_dsa_ctx *dsa, + const unsigned char *hash, + unsigned long hash_len, + unsigned char *sig_fixed) +{ + unsigned char *data, *sig; + unsigned long cbData, datalen, siglen; + int ret; + + datalen = hash_len; + data = malloc(datalen); + if (!data) { + return -1; + } + + memcpy(data, hash, datalen); + + ret = BCryptSignHash(dsa->hKey, NULL, data, datalen, + NULL, 0, &cbData, 0); + if (ret == STATUS_SUCCESS) { + siglen = cbData; + if (siglen == 40) { + sig = malloc(siglen); + if (sig) { + ret = BCryptSignHash(dsa->hKey, NULL, data, datalen, + sig, siglen, &cbData, 0); + if (ret == STATUS_SUCCESS) { + memcpy(sig_fixed, sig, siglen); + } + + free(sig); + } else + ret = STATUS_NO_MEMORY; + } else + ret = STATUS_NO_MEMORY; + } + + free(data); + + return ret == STATUS_SUCCESS ? 0 : -1; +} + +void +_libssh2_wincng_dsa_free(libssh2_dsa_ctx *dsa) +{ + if (!dsa) + return; + + BCryptDestroyKey(dsa->hKey); + + if (dsa->pbKeyObject) + free(dsa->pbKeyObject); + + memset(dsa, 0, sizeof(libssh2_dsa_ctx)); + free(dsa); +} +#endif + + +/*******************************************************************/ +/* + * Windows CNG backend: Key functions + */ + +static unsigned long +_libssh2_wincng_pub_priv_write(unsigned char *key, + unsigned long offset, + const unsigned char *bignum, + const unsigned long length) +{ + _libssh2_htonu32(key + offset, length); + offset += 4; + + memcpy(key + offset, bignum, length); + offset += length; + + return offset; +} + +int +_libssh2_wincng_pub_priv_keyfile(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + const char *privatekey, + const char *passphrase) +{ +#ifdef HAVE_LIBCRYPT32 + unsigned char *pbEncoded, **rpbDecoded; + unsigned long cbEncoded, *rcbDecoded; + unsigned char *key, *mth; + unsigned long keylen, mthlen, index, offset, length; + int ret; + + ret = _libssh2_wincng_load_private(session, privatekey, passphrase, + &pbEncoded, &cbEncoded); + if (ret) { + return -1; + } + + ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded, + &rpbDecoded, &rcbDecoded, &length); + + free(pbEncoded); + + if (ret) { + return -1; + } + + + if (length == 9) { /* private RSA key */ + mthlen = 7; + mth = LIBSSH2_ALLOC(session, mthlen); + if (mth) { + memcpy(mth, "ssh-rsa", mthlen); + } else { + ret = -1; + } + + + keylen = 4 + mthlen + 4 + rcbDecoded[2] + 4 + rcbDecoded[1]; + key = LIBSSH2_ALLOC(session, keylen); + if (key) { + offset = _libssh2_wincng_pub_priv_write(key, 0, mth, mthlen); + + offset = _libssh2_wincng_pub_priv_write(key, offset, + rpbDecoded[2], + rcbDecoded[2]); + + _libssh2_wincng_pub_priv_write(key, offset, + rpbDecoded[1], + rcbDecoded[1]); + } else { + ret = -1; + } + + } else if (length == 6) { /* private DSA key */ + mthlen = 7; + mth = LIBSSH2_ALLOC(session, mthlen); + if (mth) { + memcpy(mth, "ssh-dss", mthlen); + } else { + ret = -1; + } + + keylen = 4 + mthlen + 4 + rcbDecoded[1] + 4 + rcbDecoded[2] + + 4 + rcbDecoded[3] + 4 + rcbDecoded[4]; + key = LIBSSH2_ALLOC(session, keylen); + if (key) { + offset = _libssh2_wincng_pub_priv_write(key, 0, mth, mthlen); + + offset = _libssh2_wincng_pub_priv_write(key, offset, + rpbDecoded[1], + rcbDecoded[1]); + + offset = _libssh2_wincng_pub_priv_write(key, offset, + rpbDecoded[2], + rcbDecoded[2]); + + offset = _libssh2_wincng_pub_priv_write(key, offset, + rpbDecoded[3], + rcbDecoded[3]); + + _libssh2_wincng_pub_priv_write(key, offset, + rpbDecoded[4], + rcbDecoded[4]); + } else { + ret = -1; + } + + } else { + ret = -1; + } + + + for (index = 0; index < length; index++) { + if (rpbDecoded[index]) { + free(rpbDecoded[index]); + rpbDecoded[index] = NULL; + } + } + + free(rpbDecoded); + free(rcbDecoded); + + + if (ret) { + if (mth) + LIBSSH2_FREE(session, mth); + if (key) + LIBSSH2_FREE(session, key); + } else { + *method = mth; + *method_len = mthlen; + *pubkeydata = key; + *pubkeydata_len = keylen; + } + + return ret; +#else + (void)method; + (void)method_len; + (void)pubkeydata; + (void)pubkeydata_len; + (void)privatekey; + (void)passphrase; + + return _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to load public key from private key file: " + "Method unsupported in Windows CNG backend"); +#endif /* HAVE_LIBCRYPT32 */ +} + + +/*******************************************************************/ +/* + * Windows CNG backend: Cipher functions + */ + +int +_libssh2_wincng_cipher_init(_libssh2_cipher_ctx *ctx, + _libssh2_cipher_type(type), + unsigned char *iv, + unsigned char *secret, + int encrypt) +{ + BCRYPT_KEY_HANDLE hKey; + BCRYPT_KEY_DATA_BLOB_HEADER *header; + unsigned char *pbKeyObject, *pbIV, *key; + unsigned long dwKeyObject, dwIV, dwBlockLength, cbData, keylen; + int ret; + + (void)encrypt; + + ret = BCryptGetProperty(*type.phAlg, BCRYPT_OBJECT_LENGTH, + (unsigned char *)&dwKeyObject, + sizeof(dwKeyObject), + &cbData, 0); + if (ret != STATUS_SUCCESS) { + return -1; + } + + ret = BCryptGetProperty(*type.phAlg, BCRYPT_BLOCK_LENGTH, + (unsigned char *)&dwBlockLength, + sizeof(dwBlockLength), + &cbData, 0); + if (ret != STATUS_SUCCESS) { + return -1; + } + + pbKeyObject = malloc(dwKeyObject); + if (!pbKeyObject) { + return -1; + } + + + keylen = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + type.dwKeyLength; + key = malloc(keylen); + if (!key) { + free(pbKeyObject); + return -1; + } + + + header = (BCRYPT_KEY_DATA_BLOB_HEADER *)key; + header->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; + header->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; + header->cbKeyData = type.dwKeyLength; + + memcpy(key + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), + secret, type.dwKeyLength); + + ret = BCryptImportKey(*type.phAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, + pbKeyObject, dwKeyObject, key, keylen, 0); + + free(key); + + if (ret != STATUS_SUCCESS) { + free(pbKeyObject); + return -1; + } + + if (type.dwUseIV) { + pbIV = malloc(dwBlockLength); + if (!pbIV) { + BCryptDestroyKey(hKey); + free(pbKeyObject); + return -1; + } + dwIV = dwBlockLength; + memcpy(pbIV, iv, dwIV); + } else { + pbIV = NULL; + dwIV = 0; + } + + + ctx->hKey = hKey; + ctx->pbKeyObject = pbKeyObject; + ctx->pbIV = pbIV; + ctx->dwKeyObject = dwKeyObject; + ctx->dwIV = dwIV; + ctx->dwBlockLength = dwBlockLength; + + return 0; +} + +int +_libssh2_wincng_cipher_crypt(_libssh2_cipher_ctx *ctx, + _libssh2_cipher_type(type), + int encrypt, + unsigned char *block, + size_t blocklen) +{ + unsigned char *pbOutput; + unsigned long cbOutput, cbInput; + int ret; + + (void)type; + + cbInput = (unsigned long)blocklen; + + if (encrypt) { + ret = BCryptEncrypt(ctx->hKey, block, cbInput, NULL, + ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0); + } else { + ret = BCryptDecrypt(ctx->hKey, block, cbInput, NULL, + ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0); + } + if (ret == STATUS_SUCCESS) { + pbOutput = malloc(cbOutput); + if (pbOutput) { + if (encrypt) { + ret = BCryptEncrypt(ctx->hKey, block, cbInput, NULL, + ctx->pbIV, ctx->dwIV, + pbOutput, cbOutput, &cbOutput, 0); + } else { + ret = BCryptDecrypt(ctx->hKey, block, cbInput, NULL, + ctx->pbIV, ctx->dwIV, + pbOutput, cbOutput, &cbOutput, 0); + } + if (ret == STATUS_SUCCESS) { + memcpy(block, pbOutput, cbOutput); + } + + free(pbOutput); + } else + ret = STATUS_NO_MEMORY; + } + + return ret == STATUS_SUCCESS ? 0 : -1; +} + +void +_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx) +{ + BCryptDestroyKey(ctx->hKey); + + if (ctx->pbKeyObject) { + free(ctx->pbKeyObject); + ctx->pbKeyObject = NULL; + } + + memset(ctx, 0, sizeof(_libssh2_cipher_ctx)); +} + + +/*******************************************************************/ +/* + * Windows CNG backend: BigNumber functions + */ + +_libssh2_bn * +_libssh2_wincng_bignum_init(void) +{ + _libssh2_bn *bignum; + + bignum = malloc(sizeof(_libssh2_bn)); + bignum->bignum = NULL; + bignum->length = 0; + + return bignum; +} + +static int +_libssh2_wincng_bignum_resize(_libssh2_bn *bn, unsigned long length) +{ + unsigned char *bignum; + + if (!bn) + return -1; + + if (length == bn->length) + return 0; + + bignum = realloc(bn->bignum, length); + if (!bignum) + return -1; + + bn->bignum = bignum; + bn->length = length; + + return 0; +} + +int +_libssh2_wincng_bignum_rand(_libssh2_bn *rnd, int bits, int top, int bottom) +{ + unsigned char *bignum; + unsigned long length; + + if (!rnd) + return -1; + + length = (unsigned long)(ceil((float)bits / 8) * sizeof(unsigned char)); + if (_libssh2_wincng_bignum_resize(rnd, length)) + return -1; + + bignum = rnd->bignum; + + if (_libssh2_wincng_random(bignum, length)) + return -1; + + /* calculate significant bits in most significant byte */ + bits %= 8; + + /* fill most significant byte with zero padding */ + bignum[0] &= (1 << (8 - bits)) - 1; + + /* set some special last bits in most significant byte */ + if (top == 0) + bignum[0] |= (1 << (7 - bits)); + else if (top == 1) + bignum[0] |= (3 << (6 - bits)); + + /* make odd by setting first bit in least significant byte */ + if (bottom) + bignum[length - 1] |= 1; + + return 0; +} + +int +_libssh2_wincng_bignum_mod_exp(_libssh2_bn *r, + _libssh2_bn *a, + _libssh2_bn *p, + _libssh2_bn *m, + _libssh2_bn_ctx *bnctx) +{ + BCRYPT_KEY_HANDLE hKey; + BCRYPT_RSAKEY_BLOB *rsakey; + unsigned char *key, *bignum; + unsigned long keylen, offset, length; + int ret; + + (void)bnctx; + + if (!r || !a || !p || !m) + return -1; + + offset = sizeof(BCRYPT_RSAKEY_BLOB); + keylen = offset + p->length + m->length; + + key = malloc(keylen); + if (!key) + return -1; + + + /* http://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */ + rsakey = (BCRYPT_RSAKEY_BLOB *)key; + rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC; + rsakey->BitLength = m->length * 8; + rsakey->cbPublicExp = p->length; + rsakey->cbModulus = m->length; + rsakey->cbPrime1 = 0; + rsakey->cbPrime2 = 0; + + memcpy(key + offset, p->bignum, p->length); + offset += p->length; + + memcpy(key + offset, m->bignum, m->length); + + ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, + BCRYPT_RSAPUBLIC_BLOB, &hKey, key, keylen, + BCRYPT_NO_KEY_VALIDATION); + + if (ret == STATUS_SUCCESS) { + ret = BCryptEncrypt(hKey, a->bignum, a->length, NULL, NULL, 0, + NULL, 0, &length, BCRYPT_PAD_NONE); + if (ret == STATUS_SUCCESS) { + if (!_libssh2_wincng_bignum_resize(r, length)) { + length = max(a->length, length); + bignum = malloc(length); + if (bignum) { + offset = length - a->length; + memset(bignum, 0, offset); + memcpy(bignum + offset, a->bignum, a->length); + + ret = BCryptEncrypt(hKey, bignum, length, NULL, NULL, 0, + r->bignum, r->length, &offset, + BCRYPT_PAD_NONE); + + free(bignum); + + if (ret == STATUS_SUCCESS) { + _libssh2_wincng_bignum_resize(r, offset); + } + } else + ret = STATUS_NO_MEMORY; + } else + ret = STATUS_NO_MEMORY; + } + + BCryptDestroyKey(hKey); + } + + free(key); + + return ret == STATUS_SUCCESS ? 0 : -1; +} + +int +_libssh2_wincng_bignum_set_word(_libssh2_bn *bn, unsigned long word) +{ + unsigned long offset, number, bits, length; + + if (!bn) + return -1; + + bits = 0; + number = word; + while (number >>= 1) + bits++; + + length = (unsigned long)(ceil((double)(bits+1)/8)*sizeof(unsigned char)); + if (_libssh2_wincng_bignum_resize(bn, length)) + return -1; + + for (offset = 0; offset < length; offset++) + bn->bignum[offset] = (word >> (offset * 8)) & 0xff; + + return 0; +} + +unsigned long +_libssh2_wincng_bignum_bits(const _libssh2_bn *bn) +{ + unsigned char number; + unsigned long offset, length, bits; + + if (!bn) + return 0; + + length = bn->length - 1; + + offset = 0; + while (!(*(bn->bignum + offset)) && (offset < length)) + offset++; + + bits = (length - offset) * 8; + number = bn->bignum[offset]; + + while (number >>= 1) + bits++; + + bits++; + + return bits; +} + +void +_libssh2_wincng_bignum_from_bin(_libssh2_bn *bn, unsigned long len, + const unsigned char *bin) +{ + unsigned char *bignum; + unsigned long offset, length, bits; + + if (bn && bin && len > 0) { + if (!_libssh2_wincng_bignum_resize(bn, len)) { + memcpy(bn->bignum, bin, len); + + bits = _libssh2_wincng_bignum_bits(bn); + length = ceil((double)bits / 8) * sizeof(unsigned char); + + offset = bn->length - length; + if (offset > 0) { + memmove(bn->bignum, bn->bignum + offset, length); + + bignum = realloc(bn->bignum, length); + if (bignum) { + bn->bignum = bignum; + bn->length = length; + } + } + } + } +} + +void +_libssh2_wincng_bignum_to_bin(const _libssh2_bn *bn, unsigned char *bin) +{ + if (bin && bn && bn->bignum && bn->length > 0) { + memcpy(bin, bn->bignum, bn->length); + } +} + +void +_libssh2_wincng_bignum_free(_libssh2_bn *bn) +{ + if (bn) { + if (bn->bignum) { + free(bn->bignum); + bn->bignum = NULL; + } + bn->length = 0; + free(bn); + } +} + + +/* + * Windows CNG backend: other functions + */ + +void _libssh2_init_aes_ctr(void) +{ + /* no implementation */ + (void)0; +} + +#endif /* LIBSSH2_WINCNG */ diff --git a/src/wincng.h b/src/wincng.h new file mode 100644 index 0000000..a327b55 --- /dev/null +++ b/src/wincng.h @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2013 Marc Hoersken + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 + +#ifdef HAVE_WINDOWS_H +#include +#endif +#ifdef HAVE_NTDEF_H +#include +#endif +#ifdef HAVE_NTSTATUS_H +#include +#endif + +#include + + +#define LIBSSH2_MD5 1 + +#define LIBSSH2_HMAC_RIPEMD 0 + +#define LIBSSH2_AES 1 +#define LIBSSH2_AES_CTR 0 +#define LIBSSH2_BLOWFISH 0 +#define LIBSSH2_RC4 1 +#define LIBSSH2_CAST 0 +#define LIBSSH2_3DES 1 + +#define LIBSSH2_RSA 1 +#define LIBSSH2_DSA 1 + +#define MD5_DIGEST_LENGTH 16 +#define SHA_DIGEST_LENGTH 20 + + +/*******************************************************************/ +/* + * Windows CNG backend: Global context handles + */ + +struct _libssh2_wincng_ctx { + BCRYPT_ALG_HANDLE hAlgRNG; + BCRYPT_ALG_HANDLE hAlgHashMD5; + BCRYPT_ALG_HANDLE hAlgHashSHA1; + BCRYPT_ALG_HANDLE hAlgHmacMD5; + BCRYPT_ALG_HANDLE hAlgHmacSHA1; + BCRYPT_ALG_HANDLE hAlgRSA; + BCRYPT_ALG_HANDLE hAlgDSA; + BCRYPT_ALG_HANDLE hAlgAES_CBC; + BCRYPT_ALG_HANDLE hAlgRC4_NA; + BCRYPT_ALG_HANDLE hAlg3DES_CBC; +}; + +struct _libssh2_wincng_ctx _libssh2_wincng; + + +/*******************************************************************/ +/* + * Windows CNG backend: Generic functions + */ + +void _libssh2_wincng_init(void); +void _libssh2_wincng_free(void); + +#define libssh2_crypto_init() \ + _libssh2_wincng_init() +#define libssh2_crypto_exit() \ + _libssh2_wincng_free() + +#define _libssh2_random(buf, len) \ + _libssh2_wincng_random(buf, len) + + +/*******************************************************************/ +/* + * Windows CNG backend: Hash structure + */ + +struct _libssh2_wincng_hash_ctx { + BCRYPT_HASH_HANDLE hHash; + unsigned char *pbHashObject; + unsigned long dwHashObject; + unsigned long cbHash; +}; + +#define _libssh2_wincng_hash_ctx struct _libssh2_wincng_hash_ctx + +/* + * Windows CNG backend: Hash functions + */ + +#define libssh2_sha1_ctx _libssh2_wincng_hash_ctx +#define libssh2_sha1_init(ctx) \ + _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA1, \ + SHA_DIGEST_LENGTH, NULL, 0) +#define libssh2_sha1_update(ctx, data, datalen) \ + _libssh2_wincng_hash_update(&ctx, data, datalen) +#define libssh2_sha1_final(ctx, hash) \ + _libssh2_wincng_hash_final(&ctx, hash) +#define libssh2_sha1(data, datalen, hash) \ + _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA1, \ + hash, SHA_DIGEST_LENGTH) + +#define libssh2_md5_ctx _libssh2_wincng_hash_ctx +#define libssh2_md5_init(ctx) \ + _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashMD5, \ + MD5_DIGEST_LENGTH, NULL, 0) +#define libssh2_md5_update(ctx, data, datalen) \ + _libssh2_wincng_hash_update(&ctx, data, datalen) +#define libssh2_md5_final(ctx, hash) \ + _libssh2_wincng_hash_final(&ctx, hash) +#define libssh2_md5(data, datalen, hash) \ + _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashMD5, \ + hash, MD5_DIGEST_LENGTH) + +/* + * Windows CNG backend: HMAC functions + */ + +#define libssh2_hmac_ctx _libssh2_wincng_hash_ctx +#define libssh2_hmac_sha1_init(ctx, key, keylen) \ + _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA1, \ + SHA_DIGEST_LENGTH, key, keylen) +#define libssh2_hmac_md5_init(ctx, key, keylen) \ + _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacMD5, \ + MD5_DIGEST_LENGTH, key, keylen) +#define libssh2_hmac_ripemd160_init(ctx, key, keylen) + /* not implemented */ +#define libssh2_hmac_update(ctx, data, datalen) \ + _libssh2_wincng_hash_update(&ctx, data, datalen) +#define libssh2_hmac_final(ctx, hash) \ + _libssh2_wincng_hmac_final(&ctx, hash) +#define libssh2_hmac_cleanup(ctx) \ + _libssh2_wincng_hmac_cleanup(ctx) + + +/*******************************************************************/ +/* + * Windows CNG backend: Key Context structure + */ + +struct _libssh2_wincng_key_ctx { + BCRYPT_KEY_HANDLE hKey; + unsigned char *pbKeyObject; + unsigned long cbKeyObject; +}; + +#define _libssh2_wincng_key_ctx struct _libssh2_wincng_key_ctx + +/* + * Windows CNG backend: RSA functions + */ + +#define libssh2_rsa_ctx _libssh2_wincng_key_ctx +#define _libssh2_rsa_new(rsactx, e, e_len, n, n_len, \ + d, d_len, p, p_len, q, q_len, \ + e1, e1_len, e2, e2_len, c, c_len) \ + _libssh2_wincng_rsa_new(rsactx, e, e_len, n, n_len, \ + d, d_len, p, p_len, q, q_len, \ + e1, e1_len, e2, e2_len, c, c_len) +#define _libssh2_rsa_new_private(rsactx, s, filename, passphrase) \ + _libssh2_wincng_rsa_new_private(rsactx, s, filename, passphrase) +#define _libssh2_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len) \ + _libssh2_wincng_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len) +#define _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len) \ + _libssh2_wincng_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len) +#define _libssh2_rsa_free(rsactx) \ + _libssh2_wincng_rsa_free(rsactx) + +/* + * Windows CNG backend: DSA functions + */ + +#define libssh2_dsa_ctx _libssh2_wincng_key_ctx +#define _libssh2_dsa_new(dsactx, p, p_len, q, q_len, \ + g, g_len, y, y_len, x, x_len) \ + _libssh2_wincng_dsa_new(dsactx, p, p_len, q, q_len, \ + g, g_len, y, y_len, x, x_len) +#define _libssh2_dsa_new_private(rsactx, s, filename, passphrase) \ + _libssh2_wincng_dsa_new_private(rsactx, s, filename, passphrase) +#define _libssh2_dsa_sha1_sign(dsactx, hash, hash_len, sig) \ + _libssh2_wincng_dsa_sha1_sign(dsactx, hash, hash_len, sig) +#define _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len) \ + _libssh2_wincng_dsa_sha1_verify(dsactx, sig, m, m_len) +#define _libssh2_dsa_free(dsactx) \ + _libssh2_wincng_dsa_free(dsactx) + +/* + * Windows CNG backend: Key functions + */ + +#define _libssh2_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw) \ + _libssh2_wincng_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw) + + +/*******************************************************************/ +/* + * Windows CNG backend: Cipher Context structure + */ + +struct _libssh2_wincng_cipher_ctx { + BCRYPT_KEY_HANDLE hKey; + unsigned char *pbKeyObject; + unsigned char *pbIV; + unsigned long dwKeyObject; + unsigned long dwIV; + unsigned long dwBlockLength; +}; + +#define _libssh2_cipher_ctx struct _libssh2_wincng_cipher_ctx + +/* + * Windows CNG backend: Cipher Type structure + */ + +struct _libssh2_wincng_cipher_type { + BCRYPT_ALG_HANDLE *phAlg; + unsigned long dwKeyLength; + unsigned long dwUseIV; +}; + +#define _libssh2_cipher_type(type) struct _libssh2_wincng_cipher_type type + +#define _libssh2_cipher_aes256ctr { NULL, 32, 1 } /* not supported */ +#define _libssh2_cipher_aes192ctr { NULL, 24, 1 } /* not supported */ +#define _libssh2_cipher_aes128ctr { NULL, 16, 1 } /* not supported */ +#define _libssh2_cipher_aes256 { &_libssh2_wincng.hAlgAES_CBC, 32, 1 } +#define _libssh2_cipher_aes192 { &_libssh2_wincng.hAlgAES_CBC, 24, 1 } +#define _libssh2_cipher_aes128 { &_libssh2_wincng.hAlgAES_CBC, 16, 1 } +#define _libssh2_cipher_blowfish { NULL, 16, 0 } /* not supported */ +#define _libssh2_cipher_arcfour { &_libssh2_wincng.hAlgRC4_NA, 16, 0 } +#define _libssh2_cipher_cast5 { NULL, 16, 0 } /* not supported */ +#define _libssh2_cipher_3des { &_libssh2_wincng.hAlg3DES_CBC, 24, 1 } + +/* + * Windows CNG backend: Cipher functions + */ + +#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \ + _libssh2_wincng_cipher_init(ctx, type, iv, secret, encrypt) +#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen) \ + _libssh2_wincng_cipher_crypt(ctx, type, encrypt, block, blocklen) +#define _libssh2_cipher_dtor(ctx) \ + _libssh2_wincng_cipher_dtor(ctx) + +/*******************************************************************/ +/* + * Windows CNG backend: BigNumber Context + */ + +#define _libssh2_bn_ctx int /* not used */ +#define _libssh2_bn_ctx_new() 0 /* not used */ +#define _libssh2_bn_ctx_free(bnctx) ((void)0) /* not used */ + + +/*******************************************************************/ +/* + * Windows CNG backend: BigNumber structure + */ + +struct _libssh2_wincng_bignum { + unsigned char *bignum; + unsigned long length; +}; + +#define _libssh2_bn struct _libssh2_wincng_bignum + +/* + * Windows CNG backend: BigNumber functions + */ + +_libssh2_bn *_libssh2_wincng_bignum_init(void); + +#define _libssh2_bn_init() \ + _libssh2_wincng_bignum_init() +#define _libssh2_bn_rand(bn, bits, top, bottom) \ + _libssh2_wincng_bignum_rand(bn, bits, top, bottom) +#define _libssh2_bn_mod_exp(r, a, p, m, ctx) \ + _libssh2_wincng_bignum_mod_exp(r, a, p, m, ctx) +#define _libssh2_bn_set_word(bn, word) \ + _libssh2_wincng_bignum_set_word(bn, word) +#define _libssh2_bn_from_bin(bn, len, bin) \ + _libssh2_wincng_bignum_from_bin(bn, len, bin) +#define _libssh2_bn_to_bin(bn, bin) \ + _libssh2_wincng_bignum_to_bin(bn, bin) +#define _libssh2_bn_bytes(bn) bn->length +#define _libssh2_bn_bits(bn) \ + _libssh2_wincng_bignum_bits(bn) +#define _libssh2_bn_free(bn) \ + _libssh2_wincng_bignum_free(bn)