/* * Copyright (C) 2013-2015 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 */ /* required for cross-compilation against the w64 mingw-runtime package */ #if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) #undef _WIN32_WINNT #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0600 #endif /* specify the required libraries for dependencies using MSVC */ #ifdef _MSC_VER #pragma comment(lib, "bcrypt.lib") #ifdef HAVE_LIBCRYPT32 #pragma comment(lib, "crypt32.lib") #endif #endif #include #include #include #include "misc.h" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_LIBCRYPT32 #include #endif #define PEM_RSA_HEADER "-----BEGIN RSA PRIVATE KEY-----" #define PEM_RSA_FOOTER "-----END RSA PRIVATE KEY-----" #define PEM_DSA_HEADER "-----BEGIN DSA PRIVATE KEY-----" #define PEM_DSA_FOOTER "-----END DSA PRIVATE KEY-----" /*******************************************************************/ /* * Windows CNG backend: Missing definitions (for MinGW[-w64]) */ #ifndef BCRYPT_SUCCESS #define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #endif #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_SHA256_ALGORITHM #define BCRYPT_SHA256_ALGORITHM L"SHA256" #endif #ifndef BCRYPT_SHA512_ALGORITHM #define BCRYPT_SHA512_ALGORITHM L"SHA512" #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; (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRNG, BCRYPT_RNG_ALGORITHM, NULL, 0); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashMD5, BCRYPT_MD5_ALGORITHM, NULL, 0); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA1, BCRYPT_SHA1_ALGORITHM, NULL, 0); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA256, BCRYPT_SHA256_ALGORITHM, NULL, 0); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA512, BCRYPT_SHA512_ALGORITHM, NULL, 0); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacMD5, BCRYPT_MD5_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA1, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA256, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA512, BCRYPT_SHA512_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRSA, BCRYPT_RSA_ALGORITHM, NULL, 0); (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDSA, BCRYPT_DSA_ALGORITHM, NULL, 0); ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgAES_CBC, BCRYPT_AES_ALGORITHM, NULL, 0); if(BCRYPT_SUCCESS(ret)) { ret = BCryptSetProperty(_libssh2_wincng.hAlgAES_CBC, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0); if(!BCRYPT_SUCCESS(ret)) { (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); } } ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgAES_ECB, BCRYPT_AES_ALGORITHM, NULL, 0); if(BCRYPT_SUCCESS(ret)) { ret = BCryptSetProperty(_libssh2_wincng.hAlgAES_ECB, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); if(!BCRYPT_SUCCESS(ret)) { (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_ECB, 0); } } ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRC4_NA, BCRYPT_RC4_ALGORITHM, NULL, 0); if(BCRYPT_SUCCESS(ret)) { ret = BCryptSetProperty(_libssh2_wincng.hAlgRC4_NA, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_NA, sizeof(BCRYPT_CHAIN_MODE_NA), 0); if(!BCRYPT_SUCCESS(ret)) { (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); } } ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlg3DES_CBC, BCRYPT_3DES_ALGORITHM, NULL, 0); if(BCRYPT_SUCCESS(ret)) { ret = BCryptSetProperty(_libssh2_wincng.hAlg3DES_CBC, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0); if(!BCRYPT_SUCCESS(ret)) { (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0); } } } void _libssh2_wincng_free(void) { (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRNG, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashMD5, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA1, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA256, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA512, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacMD5, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA1, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA256, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA512, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRSA, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDSA, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0); memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng)); } int _libssh2_wincng_random(void *buf, int len) { int ret; ret = BCryptGenRandom(_libssh2_wincng.hAlgRNG, buf, len, 0); return BCRYPT_SUCCESS(ret) ? 0 : -1; } static void _libssh2_wincng_safe_free(void *buf, int len) { #ifndef LIBSSH2_CLEAR_MEMORY (void)len; #endif if(!buf) return; #ifdef LIBSSH2_CLEAR_MEMORY if(len > 0) SecureZeroMemory(buf, len); #endif free(buf); } /*******************************************************************/ /* * 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((!BCRYPT_SUCCESS(ret)) || dwHash != hashlen) { return -1; } ret = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (unsigned char *)&dwHashObject, sizeof(dwHashObject), &cbData, 0); if(!BCRYPT_SUCCESS(ret)) { return -1; } pbHashObject = malloc(dwHashObject); if(!pbHashObject) { return -1; } ret = BCryptCreateHash(hAlg, &hHash, pbHashObject, dwHashObject, key, keylen, 0); if(!BCRYPT_SUCCESS(ret)) { _libssh2_wincng_safe_free(pbHashObject, dwHashObject); 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, const unsigned char *data, unsigned long datalen) { int ret; ret = BCryptHashData(ctx->hHash, (unsigned char *)data, datalen, 0); return BCRYPT_SUCCESS(ret) ? 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); ctx->hHash = NULL; _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject); ctx->pbHashObject = NULL; ctx->dwHashObject = 0; return BCRYPT_SUCCESS(ret) ? 0 : -1; } 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; int ret; ret = _libssh2_wincng_hash_init(&ctx, hAlg, hashlen, NULL, 0); if(!ret) { ret = _libssh2_wincng_hash_update(&ctx, data, datalen); ret |= _libssh2_wincng_hash_final(&ctx, hash); } return ret; } /*******************************************************************/ /* * Windows CNG backend: HMAC functions */ int _libssh2_wincng_hmac_final(_libssh2_wincng_hash_ctx *ctx, unsigned char *hash) { int ret; ret = BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0); return BCRYPT_SUCCESS(ret) ? 0 : -1; } void _libssh2_wincng_hmac_cleanup(_libssh2_wincng_hash_ctx *ctx) { BCryptDestroyHash(ctx->hHash); ctx->hHash = NULL; _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject); ctx->pbHashObject = NULL; ctx->dwHashObject = 0; } /*******************************************************************/ /* * 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); _libssh2_wincng_safe_free(data, datalen); if(ret) { _libssh2_wincng_safe_free(hash, hashlen); return -1; } datalen = sig_len; data = malloc(datalen); if(!data) { _libssh2_wincng_safe_free(hash, hashlen); return -1; } if(flags & BCRYPT_PAD_PKCS1) { paddingInfoPKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM; pPaddingInfo = &paddingInfoPKCS1; } else pPaddingInfo = NULL; memcpy(data, sig, datalen); ret = BCryptVerifySignature(ctx->hKey, pPaddingInfo, hash, hashlen, data, datalen, flags); _libssh2_wincng_safe_free(hash, hashlen); _libssh2_wincng_safe_free(data, datalen); return BCRYPT_SUCCESS(ret) ? 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; fp = fopen(filename, FOPEN_READTEXT); if(!fp) { return -1; } ret = _libssh2_pem_parse(session, headerbegin, headerend, passphrase, 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, int tryLoadRSA, int tryLoadDSA) { unsigned char *data = NULL; unsigned int datalen = 0; int ret = -1; if(ret && tryLoadRSA) { ret = _libssh2_wincng_load_pem(session, filename, passphrase, PEM_RSA_HEADER, PEM_RSA_FOOTER, &data, &datalen); } if(ret && tryLoadDSA) { ret = _libssh2_wincng_load_pem(session, filename, passphrase, PEM_DSA_HEADER, PEM_DSA_FOOTER, &data, &datalen); } if(!ret) { *ppbEncoded = data; *pcbEncoded = datalen; } return ret; } static int _libssh2_wincng_load_private_memory(LIBSSH2_SESSION *session, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase, unsigned char **ppbEncoded, unsigned long *pcbEncoded, int tryLoadRSA, int tryLoadDSA) { unsigned char *data = NULL; unsigned int datalen = 0; int ret = -1; (void)passphrase; if(ret && tryLoadRSA) { ret = _libssh2_pem_parse_memory(session, PEM_RSA_HEADER, PEM_RSA_FOOTER, privatekeydata, privatekeydata_len, &data, &datalen); } if(ret && tryLoadDSA) { ret = _libssh2_pem_parse_memory(session, PEM_DSA_HEADER, PEM_DSA_FOOTER, privatekeydata, privatekeydata_len, &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 = NULL; unsigned long cbDecoded = 0; 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) { _libssh2_wincng_safe_free(pbDecoded, cbDecoded); 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 += offset; } pbOutput = (unsigned char *)malloc(cbOutput); if(!pbOutput) { return -1; } pbOutput[0] = 0; for(index = 0; ((index + offset) < cbOutput) && (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 = NULL, *pbInteger; unsigned long cbDecoded = 0, 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; } _libssh2_wincng_safe_free(pbInteger, cbInteger); } 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) { *prpbDecoded = rpbDecoded; *prcbDecoded = rcbDecoded; *pcbCount = length; } else { for(length = 0; length < index; length++) { _libssh2_wincng_safe_free(rpbDecoded[length], rcbDecoded[length]); rpbDecoded[length] = NULL; rcbDecoded[length] = 0; } free(rpbDecoded); free(rcbDecoded); } } else { free(rpbDecoded); ret = -1; } } else { ret = -1; } _libssh2_wincng_safe_free(pbDecoded, cbDecoded); } 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 = 0, p2len = 0; 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); /* https://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(!BCRYPT_SUCCESS(ret)) { _libssh2_wincng_safe_free(key, keylen); return -1; } *rsa = malloc(sizeof(libssh2_rsa_ctx)); if(!(*rsa)) { BCryptDestroyKey(hKey); _libssh2_wincng_safe_free(key, keylen); return -1; } (*rsa)->hKey = hKey; (*rsa)->pbKeyObject = key; (*rsa)->cbKeyObject = keylen; return 0; } #ifdef HAVE_LIBCRYPT32 static int _libssh2_wincng_rsa_new_private_parse(libssh2_rsa_ctx **rsa, LIBSSH2_SESSION *session, unsigned char *pbEncoded, unsigned long cbEncoded) { BCRYPT_KEY_HANDLE hKey; unsigned char *pbStructInfo; unsigned long cbStructInfo; int ret; (void)session; ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded, PKCS_RSA_PRIVATE_KEY, &pbStructInfo, &cbStructInfo); _libssh2_wincng_safe_free(pbEncoded, cbEncoded); if(ret) { return -1; } ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, LEGACY_RSAPRIVATE_BLOB, &hKey, pbStructInfo, cbStructInfo, 0); if(!BCRYPT_SUCCESS(ret)) { _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo); return -1; } *rsa = malloc(sizeof(libssh2_rsa_ctx)); if(!(*rsa)) { BCryptDestroyKey(hKey); _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo); return -1; } (*rsa)->hKey = hKey; (*rsa)->pbKeyObject = pbStructInfo; (*rsa)->cbKeyObject = cbStructInfo; return 0; } #endif /* HAVE_LIBCRYPT32 */ int _libssh2_wincng_rsa_new_private(libssh2_rsa_ctx **rsa, LIBSSH2_SESSION *session, const char *filename, const unsigned char *passphrase) { #ifdef HAVE_LIBCRYPT32 unsigned char *pbEncoded; unsigned long cbEncoded; int ret; (void)session; ret = _libssh2_wincng_load_private(session, filename, (const char *)passphrase, &pbEncoded, &cbEncoded, 1, 0); if(ret) { return -1; } return _libssh2_wincng_rsa_new_private_parse(rsa, session, pbEncoded, cbEncoded); #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_new_private_frommemory(libssh2_rsa_ctx **rsa, LIBSSH2_SESSION *session, const char *filedata, size_t filedata_len, unsigned const char *passphrase) { #ifdef HAVE_LIBCRYPT32 unsigned char *pbEncoded; unsigned long cbEncoded; int ret; (void)session; ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len, (const char *)passphrase, &pbEncoded, &cbEncoded, 1, 0); if(ret) { return -1; } return _libssh2_wincng_rsa_new_private_parse(rsa, session, pbEncoded, cbEncoded); #else (void)rsa; (void)filedata; (void)filedata_len; (void)passphrase; return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, "Unable to extract private key from memory: " "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(BCRYPT_SUCCESS(ret)) { siglen = cbData; sig = LIBSSH2_ALLOC(session, siglen); if(sig) { ret = BCryptSignHash(rsa->hKey, &paddingInfo, data, datalen, sig, siglen, &cbData, BCRYPT_PAD_PKCS1); if(BCRYPT_SUCCESS(ret)) { *signature_len = siglen; *signature = sig; } else { LIBSSH2_FREE(session, sig); } } else ret = STATUS_NO_MEMORY; } _libssh2_wincng_safe_free(data, datalen); return BCRYPT_SUCCESS(ret) ? 0 : -1; } void _libssh2_wincng_rsa_free(libssh2_rsa_ctx *rsa) { if(!rsa) return; BCryptDestroyKey(rsa->hKey); rsa->hKey = NULL; _libssh2_wincng_safe_free(rsa->pbKeyObject, rsa->cbKeyObject); _libssh2_wincng_safe_free(rsa, sizeof(libssh2_rsa_ctx)); } /*******************************************************************/ /* * 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); /* https://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(!BCRYPT_SUCCESS(ret)) { _libssh2_wincng_safe_free(key, keylen); return -1; } *dsa = malloc(sizeof(libssh2_dsa_ctx)); if(!(*dsa)) { BCryptDestroyKey(hKey); _libssh2_wincng_safe_free(key, keylen); return -1; } (*dsa)->hKey = hKey; (*dsa)->pbKeyObject = key; (*dsa)->cbKeyObject = keylen; return 0; } #ifdef HAVE_LIBCRYPT32 static int _libssh2_wincng_dsa_new_private_parse(libssh2_dsa_ctx **dsa, LIBSSH2_SESSION *session, unsigned char *pbEncoded, unsigned long cbEncoded) { unsigned char **rpbDecoded; unsigned long *rcbDecoded, index, length; int ret; (void)session; ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded, &rpbDecoded, &rcbDecoded, &length); _libssh2_wincng_safe_free(pbEncoded, cbEncoded); 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++) { _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]); rpbDecoded[index] = NULL; rcbDecoded[index] = 0; } free(rpbDecoded); free(rcbDecoded); return ret; } #endif /* HAVE_LIBCRYPT32 */ 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; unsigned long cbEncoded; int ret; ret = _libssh2_wincng_load_private(session, filename, (const char *)passphrase, &pbEncoded, &cbEncoded, 0, 1); if(ret) { return -1; } return _libssh2_wincng_dsa_new_private_parse(dsa, session, pbEncoded, cbEncoded); #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_new_private_frommemory(libssh2_dsa_ctx **dsa, LIBSSH2_SESSION *session, const char *filedata, size_t filedata_len, unsigned const char *passphrase) { #ifdef HAVE_LIBCRYPT32 unsigned char *pbEncoded; unsigned long cbEncoded; int ret; ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len, (const char *)passphrase, &pbEncoded, &cbEncoded, 0, 1); if(ret) { return -1; } return _libssh2_wincng_dsa_new_private_parse(dsa, session, pbEncoded, cbEncoded); #else (void)dsa; (void)filedata; (void)filedata_len; (void)passphrase; return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, "Unable to extract private key from memory: " "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(BCRYPT_SUCCESS(ret)) { siglen = cbData; if(siglen == 40) { sig = malloc(siglen); if(sig) { ret = BCryptSignHash(dsa->hKey, NULL, data, datalen, sig, siglen, &cbData, 0); if(BCRYPT_SUCCESS(ret)) { memcpy(sig_fixed, sig, siglen); } _libssh2_wincng_safe_free(sig, siglen); } else ret = STATUS_NO_MEMORY; } else ret = STATUS_NO_MEMORY; } _libssh2_wincng_safe_free(data, datalen); return BCRYPT_SUCCESS(ret) ? 0 : -1; } void _libssh2_wincng_dsa_free(libssh2_dsa_ctx *dsa) { if(!dsa) return; BCryptDestroyKey(dsa->hKey); dsa->hKey = NULL; _libssh2_wincng_safe_free(dsa->pbKeyObject, dsa->cbKeyObject); _libssh2_wincng_safe_free(dsa, sizeof(libssh2_dsa_ctx)); } #endif /*******************************************************************/ /* * Windows CNG backend: Key functions */ #ifdef HAVE_LIBCRYPT32 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; } static int _libssh2_wincng_pub_priv_keyfile_parse(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, unsigned char *pbEncoded, unsigned long cbEncoded) { unsigned char **rpbDecoded; unsigned long *rcbDecoded; unsigned char *key = NULL, *mth = NULL; unsigned long keylen = 0, mthlen = 0; unsigned long index, offset, length; int ret; ret = _libssh2_wincng_asn_decode_bns(pbEncoded, cbEncoded, &rpbDecoded, &rcbDecoded, &length); _libssh2_wincng_safe_free(pbEncoded, cbEncoded); 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++) { _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]); rpbDecoded[index] = NULL; rcbDecoded[index] = 0; } 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; } #endif /* HAVE_LIBCRYPT32 */ 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; unsigned long cbEncoded; int ret; ret = _libssh2_wincng_load_private(session, privatekey, passphrase, &pbEncoded, &cbEncoded, 1, 1); if(ret) { return -1; } return _libssh2_wincng_pub_priv_keyfile_parse(session, method, method_len, pubkeydata, pubkeydata_len, pbEncoded, cbEncoded); #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 */ } int _libssh2_wincng_pub_priv_keyfilememory(LIBSSH2_SESSION *session, unsigned char **method, size_t *method_len, unsigned char **pubkeydata, size_t *pubkeydata_len, const char *privatekeydata, size_t privatekeydata_len, const char *passphrase) { #ifdef HAVE_LIBCRYPT32 unsigned char *pbEncoded; unsigned long cbEncoded; int ret; ret = _libssh2_wincng_load_private_memory(session, privatekeydata, privatekeydata_len, passphrase, &pbEncoded, &cbEncoded, 1, 1); if(ret) { return -1; } return _libssh2_wincng_pub_priv_keyfile_parse(session, method, method_len, pubkeydata, pubkeydata_len, pbEncoded, cbEncoded); #else (void)method; (void)method_len; (void)pubkeydata_len; (void)pubkeydata; (void)privatekeydata; (void)privatekeydata_len; (void)passphrase; return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, "Unable to extract public key from private key in memory: " "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, *pbCtr, *pbIVCopy; unsigned long dwKeyObject, dwIV, dwCtrLength, dwBlockLength, cbData, keylen; int ret; (void)encrypt; ret = BCryptGetProperty(*type.phAlg, BCRYPT_OBJECT_LENGTH, (unsigned char *)&dwKeyObject, sizeof(dwKeyObject), &cbData, 0); if(!BCRYPT_SUCCESS(ret)) { return -1; } ret = BCryptGetProperty(*type.phAlg, BCRYPT_BLOCK_LENGTH, (unsigned char *)&dwBlockLength, sizeof(dwBlockLength), &cbData, 0); if(!BCRYPT_SUCCESS(ret)) { 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); _libssh2_wincng_safe_free(key, keylen); if(!BCRYPT_SUCCESS(ret)) { _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject); return -1; } pbIV = NULL; pbCtr = NULL; dwIV = 0; dwCtrLength = 0; if(type.useIV || type.ctrMode) { pbIVCopy = malloc(dwBlockLength); if(!pbIVCopy) { BCryptDestroyKey(hKey); _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject); return -1; } memcpy(pbIVCopy, iv, dwBlockLength); if(type.ctrMode) { pbCtr = pbIVCopy; dwCtrLength = dwBlockLength; } else if(type.useIV) { pbIV = pbIVCopy; dwIV = dwBlockLength; } } ctx->hKey = hKey; ctx->pbKeyObject = pbKeyObject; ctx->pbIV = pbIV; ctx->pbCtr = pbCtr; ctx->dwKeyObject = dwKeyObject; ctx->dwIV = dwIV; ctx->dwBlockLength = dwBlockLength; ctx->dwCtrLength = dwCtrLength; 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, *pbInput; unsigned long cbOutput, cbInput; int ret; (void)type; cbInput = (unsigned long)blocklen; if(type.ctrMode) { pbInput = ctx->pbCtr; } else { pbInput = block; } if(encrypt || type.ctrMode) { ret = BCryptEncrypt(ctx->hKey, pbInput, cbInput, NULL, ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0); } else { ret = BCryptDecrypt(ctx->hKey, pbInput, cbInput, NULL, ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0); } if(BCRYPT_SUCCESS(ret)) { pbOutput = malloc(cbOutput); if(pbOutput) { if(encrypt || type.ctrMode) { ret = BCryptEncrypt(ctx->hKey, pbInput, cbInput, NULL, ctx->pbIV, ctx->dwIV, pbOutput, cbOutput, &cbOutput, 0); } else { ret = BCryptDecrypt(ctx->hKey, pbInput, cbInput, NULL, ctx->pbIV, ctx->dwIV, pbOutput, cbOutput, &cbOutput, 0); } if(BCRYPT_SUCCESS(ret)) { if(type.ctrMode) { _libssh2_xor_data(block, block, pbOutput, blocklen); _libssh2_aes_ctr_increment(ctx->pbCtr, ctx->dwCtrLength); } else { memcpy(block, pbOutput, cbOutput); } } _libssh2_wincng_safe_free(pbOutput, cbOutput); } else ret = STATUS_NO_MEMORY; } return BCRYPT_SUCCESS(ret) ? 0 : -1; } void _libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx) { BCryptDestroyKey(ctx->hKey); ctx->hKey = NULL; _libssh2_wincng_safe_free(ctx->pbKeyObject, ctx->dwKeyObject); ctx->pbKeyObject = NULL; ctx->dwKeyObject = 0; _libssh2_wincng_safe_free(ctx->pbIV, ctx->dwBlockLength); ctx->pbIV = NULL; ctx->dwBlockLength = 0; _libssh2_wincng_safe_free(ctx->pbCtr, ctx->dwCtrLength); ctx->pbCtr = NULL; ctx->dwCtrLength = 0; } /*******************************************************************/ /* * Windows CNG backend: BigNumber functions */ _libssh2_bn * _libssh2_wincng_bignum_init(void) { _libssh2_bn *bignum; bignum = (_libssh2_bn *)malloc(sizeof(_libssh2_bn)); if(bignum) { 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; #ifdef LIBSSH2_CLEAR_MEMORY if(bn->bignum && bn->length > 0 && length < bn->length) { SecureZeroMemory(bn->bignum + length, bn->length - length); } #endif bignum = realloc(bn->bignum, length); if(!bignum) return -1; bn->bignum = bignum; bn->length = length; return 0; } static 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; } static int _libssh2_wincng_bignum_mod_exp(_libssh2_bn *r, _libssh2_bn *a, _libssh2_bn *p, _libssh2_bn *m) { BCRYPT_KEY_HANDLE hKey; BCRYPT_RSAKEY_BLOB *rsakey; unsigned char *key, *bignum; unsigned long keylen, offset, length; int ret; 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; /* https://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(BCRYPT_SUCCESS(ret)) { ret = BCryptEncrypt(hKey, a->bignum, a->length, NULL, NULL, 0, NULL, 0, &length, BCRYPT_PAD_NONE); if(BCRYPT_SUCCESS(ret)) { 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); _libssh2_wincng_safe_free(bignum, length); if(BCRYPT_SUCCESS(ret)) { _libssh2_wincng_bignum_resize(r, offset); } } else ret = STATUS_NO_MEMORY; } else ret = STATUS_NO_MEMORY; } BCryptDestroyKey(hKey); } _libssh2_wincng_safe_free(key, keylen); return BCRYPT_SUCCESS(ret) ? 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.0) * 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) return; if(_libssh2_wincng_bignum_resize(bn, len)) return; memcpy(bn->bignum, bin, len); bits = _libssh2_wincng_bignum_bits(bn); length = (unsigned long) (ceil(((double)bits) / 8.0) * sizeof(unsigned char)); offset = bn->length - length; if(offset > 0) { memmove(bn->bignum, bn->bignum + offset, length); #ifdef LIBSSH2_CLEAR_MEMORY SecureZeroMemory(bn->bignum + length, offset); #endif 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) { _libssh2_wincng_safe_free(bn->bignum, bn->length); bn->bignum = NULL; } bn->length = 0; _libssh2_wincng_safe_free(bn, sizeof(_libssh2_bn)); } } /* * Windows CNG backend: Diffie-Hellman support. */ void _libssh2_dh_init(_libssh2_dh_ctx *dhctx) { *dhctx = _libssh2_wincng_bignum_init(); /* Random from client */ } int _libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, _libssh2_bn *g, _libssh2_bn *p, int group_order) { /* Generate x and e */ if(_libssh2_wincng_bignum_rand(*dhctx, group_order * 8 - 1, 0, -1)) return -1; if(_libssh2_wincng_bignum_mod_exp(public, g, *dhctx, p)) return -1; return 0; } int _libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, _libssh2_bn *f, _libssh2_bn *p) { /* Compute the shared secret */ _libssh2_wincng_bignum_mod_exp(secret, f, *dhctx, p); return 0; } void _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) { _libssh2_wincng_bignum_free(*dhctx); *dhctx = NULL; } #endif /* LIBSSH2_WINCNG */