diff options
Diffstat (limited to 'libs/libssh2/src/wincng.c')
-rw-r--r-- | libs/libssh2/src/wincng.c | 4185 |
1 files changed, 4185 insertions, 0 deletions
diff --git a/libs/libssh2/src/wincng.c b/libs/libssh2/src/wincng.c new file mode 100644 index 0000000000..5d4b36b70b --- /dev/null +++ b/libs/libssh2/src/wincng.c @@ -0,0 +1,4185 @@ +/* + * Copyright (C) Marc Hoersken <info@marc-hoersken.de> + * 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. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifdef LIBSSH2_CRYPTO_C /* Compile this via crypto.c */ + +/* 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 + +#if !defined(LIBSSH2_WINCNG_DISABLE_WINCRYPT) && !defined(HAVE_LIBCRYPT32) +#define HAVE_LIBCRYPT32 +#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 <windows.h> +#include <bcrypt.h> +#include <math.h> + +#include <stdlib.h> + +#ifdef HAVE_LIBCRYPT32 +#include <wincrypt.h> /* for CryptDecodeObjectEx() */ +#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-----" +#define PEM_ECDSA_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----" +#define PEM_ECDSA_FOOTER "-----END OPENSSH PRIVATE KEY-----" + +#define OPENSSL_PRIVATEKEY_AUTH_MAGIC "openssh-key-v1" + +/* Define these manually to avoid including <ntstatus.h> and thus + clashing with <windows.h> symbols. */ +#ifndef STATUS_NOT_SUPPORTED +#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB) +#endif + +#ifndef STATUS_INVALID_SIGNATURE +#define STATUS_INVALID_SIGNATURE ((NTSTATUS)0xC000A000) +#endif + +/*******************************************************************/ +/* + * 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 + +#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM +#ifndef BCRYPT_MD5_ALGORITHM +#define BCRYPT_MD5_ALGORITHM L"MD5" +#endif +#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_SHA384_ALGORITHM +#define BCRYPT_SHA384_ALGORITHM L"SHA384" +#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_DH_ALGORITHM +#define BCRYPT_DH_ALGORITHM L"DH" +#endif + +/* BCRYPT_KDF_RAW_SECRET is available from Windows 8.1 and onwards */ +#ifndef BCRYPT_KDF_RAW_SECRET +#define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" +#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 + +#if defined(_MSC_VER) && _MSC_VER < 1700 +/* Workaround for warning C4306: + 'type cast' : conversion from 'int' to 'LPCSTR' of greater size */ +#undef X509_SEQUENCE_OF_ANY +#undef X509_MULTI_BYTE_UINT +#undef PKCS_RSA_PRIVATE_KEY +#define X509_SEQUENCE_OF_ANY ((LPCSTR)(size_t)34) +#define X509_MULTI_BYTE_UINT ((LPCSTR)(size_t)38) +#define PKCS_RSA_PRIVATE_KEY ((LPCSTR)(size_t)43) +#endif + +static int +_libssh2_wincng_bignum_resize(_libssh2_bn* bn, ULONG length); + +/*******************************************************************/ +/* + * Windows CNG backend: ECDSA-specific declarations. + */ +#if LIBSSH2_ECDSA + +typedef enum { + WINCNG_ECC_KEYTYPE_ECDSA = 0, + WINCNG_ECC_KEYTYPE_ECDH = 1, +} _libssh2_wincng_ecc_keytype; + +typedef struct __libssh2_wincng_ecdsa_algorithm { + /* Algorithm name */ + const char *name; + + /* Key length, in bits */ + ULONG key_length; + + /* Length of each point, in bytes */ + ULONG point_length; + + /* Name of CNG algorithm provider, */ + /* indexed by _libssh2_wincng_ecc_keytype */ + LPCWSTR provider[2]; + + /* Magic for public key import, indexed by _libssh2_wincng_ecc_keytype */ + ULONG public_import_magic[2]; + + /* Magic for private key import, indexed by _libssh2_wincng_ecc_keytype */ + ULONG private_import_magic[2]; +} _libssh2_wincng_ecdsa_algorithm; + +/* Supported algorithms, indexed by libssh2_curve_type */ +static _libssh2_wincng_ecdsa_algorithm _wincng_ecdsa_algorithms[] = { + { + "ecdsa-sha2-nistp256", + 256, + 256 / 8, + { BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_ECDH_P256_ALGORITHM }, + { BCRYPT_ECDSA_PUBLIC_P256_MAGIC, BCRYPT_ECDH_PUBLIC_P256_MAGIC }, + { BCRYPT_ECDSA_PRIVATE_P256_MAGIC, BCRYPT_ECDH_PRIVATE_P256_MAGIC } + }, + { + "ecdsa-sha2-nistp384", + 384, + 384 / 8, + { BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_ECDH_P384_ALGORITHM }, + { BCRYPT_ECDSA_PUBLIC_P384_MAGIC, BCRYPT_ECDH_PUBLIC_P384_MAGIC }, + { BCRYPT_ECDSA_PRIVATE_P384_MAGIC, BCRYPT_ECDH_PRIVATE_P384_MAGIC } + }, + { + "ecdsa-sha2-nistp521", + 521, + ((521 + 7) & ~7) / 8, + { BCRYPT_ECDSA_P521_ALGORITHM, BCRYPT_ECDH_P521_ALGORITHM }, + { BCRYPT_ECDSA_PUBLIC_P521_MAGIC, BCRYPT_ECDH_PUBLIC_P521_MAGIC }, + { BCRYPT_ECDSA_PRIVATE_P521_MAGIC, BCRYPT_ECDH_PRIVATE_P521_MAGIC } + }, +}; + +/* An encoded point */ +typedef struct __libssh2_ecdsa_point { + libssh2_curve_type curve; + + const unsigned char *x; + ULONG x_len; + + const unsigned char *y; + ULONG y_len; +} _libssh2_ecdsa_point; + +/* Lookup libssh2_curve_type by name */ +static int +_libssh2_wincng_ecdsa_curve_type_from_name(IN const char *name, + OUT libssh2_curve_type *out_curve); + +/* Parse an OpenSSL-formatted ECDSA private key */ +static int +_libssh2_wincng_parse_ecdsa_privatekey(OUT _libssh2_wincng_ecdsa_key **key, + IN unsigned char *privatekey, + IN size_t privatekey_len); + +#endif + +/*******************************************************************/ +/* + * Windows CNG backend: Generic functions + */ + +struct _libssh2_wincng_ctx _libssh2_wincng; + +void +_libssh2_wincng_init(void) +{ + int ret; + +#if LIBSSH2_ECDSA + unsigned int curve; +#endif + + memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng)); + + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRNG, + BCRYPT_RNG_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgRNG = NULL; + } + +#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashMD5, + BCRYPT_MD5_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHashMD5 = NULL; + } +#endif + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA1, + BCRYPT_SHA1_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHashSHA1 = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA256, + BCRYPT_SHA256_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHashSHA256 = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA384, + BCRYPT_SHA384_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHashSHA384 = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA512, + BCRYPT_SHA512_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHashSHA512 = NULL; + } + +#if LIBSSH2_MD5 + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacMD5, + BCRYPT_MD5_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHmacMD5 = NULL; + } +#endif + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA1, + BCRYPT_SHA1_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHmacSHA1 = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA256, + BCRYPT_SHA256_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHmacSHA256 = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA384, + BCRYPT_SHA384_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHmacSHA384 = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA512, + BCRYPT_SHA512_ALGORITHM, NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgHmacSHA512 = NULL; + } + + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRSA, + BCRYPT_RSA_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgRSA = NULL; + } + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDSA, + BCRYPT_DSA_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgDSA = NULL; + } + + 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)) { + ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); + if(BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgAES_CBC = NULL; + } + } + } + + 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)) { + ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_ECB, 0); + if(BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgAES_ECB = NULL; + } + } + } + + 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)) { + ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); + if(BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgRC4_NA = NULL; + } + } + } + + 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)) { + ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, + 0); + if(BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlg3DES_CBC = NULL; + } + } + } + + ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDH, + BCRYPT_DH_ALGORITHM, NULL, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgDH = NULL; + } + +#if LIBSSH2_ECDSA + for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) { + BCRYPT_ALG_HANDLE alg_handle_ecdsa; + BCRYPT_ALG_HANDLE alg_handle_ecdh; + + ret = BCryptOpenAlgorithmProvider( + &alg_handle_ecdsa, + _wincng_ecdsa_algorithms[curve].provider[WINCNG_ECC_KEYTYPE_ECDSA], + NULL, + 0); + if(BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgECDSA[curve] = alg_handle_ecdsa; + } + + ret = BCryptOpenAlgorithmProvider( + &alg_handle_ecdh, + _wincng_ecdsa_algorithms[curve].provider[WINCNG_ECC_KEYTYPE_ECDH], + NULL, + 0); + if(BCRYPT_SUCCESS(ret)) { + _libssh2_wincng.hAlgECDH[curve] = alg_handle_ecdh; + } + } +#endif +} + +void +_libssh2_wincng_free(void) +{ +#if LIBSSH2_ECDSA + unsigned int curve; +#endif + + if(_libssh2_wincng.hAlgRNG) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRNG, 0); +#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM + if(_libssh2_wincng.hAlgHashMD5) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashMD5, 0); +#endif + if(_libssh2_wincng.hAlgHashSHA1) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA1, 0); + if(_libssh2_wincng.hAlgHashSHA256) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA256, 0); + if(_libssh2_wincng.hAlgHashSHA384) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA384, 0); + if(_libssh2_wincng.hAlgHashSHA512) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA512, 0); +#if LIBSSH2_MD5 + if(_libssh2_wincng.hAlgHmacMD5) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacMD5, 0); +#endif + if(_libssh2_wincng.hAlgHmacSHA1) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA1, 0); + if(_libssh2_wincng.hAlgHmacSHA256) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA256, 0); + if(_libssh2_wincng.hAlgHmacSHA384) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA384, 0); + if(_libssh2_wincng.hAlgHmacSHA512) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA512, 0); + if(_libssh2_wincng.hAlgRSA) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRSA, 0); + if(_libssh2_wincng.hAlgDSA) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDSA, 0); + if(_libssh2_wincng.hAlgAES_CBC) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); + if(_libssh2_wincng.hAlgRC4_NA) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); + if(_libssh2_wincng.hAlg3DES_CBC) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0); + if(_libssh2_wincng.hAlgDH) + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDH, 0); + +#if LIBSSH2_ECDSA + for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) { + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgECDSA[curve], + 0); + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgECDH[curve], + 0); + } +#endif + + memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng)); +} + +int +_libssh2_wincng_random(void *buf, size_t len) +{ + int ret; + + if(len > ULONG_MAX) { + return -1; + } + + ret = BCryptGenRandom(_libssh2_wincng.hAlgRNG, buf, (ULONG)len, 0); + + return BCRYPT_SUCCESS(ret) ? 0 : -1; +} + +static void +_libssh2_wincng_safe_free(void *buf, size_t len) +{ + if(!buf) + return; + + if(len > 0) + _libssh2_explicit_zero(buf, len); + + free(buf); +} + +/* Copy a big endian set of bits from src to dest. + * if the size of src is smaller than dest then pad the "left" (MSB) + * end with zeroes and copy the bits into the "right" (LSB) end. */ +static void +memcpy_with_be_padding(unsigned char *dest, ULONG dest_len, + unsigned char *src, ULONG src_len) +{ + if(dest_len > src_len) { + memset(dest, 0, dest_len - src_len); + } + memcpy((dest + dest_len) - src_len, src, src_len); +} + +/*******************************************************************/ +/* + * Windows CNG backend: Hash functions + */ + +int +_libssh2_wincng_hash_init(_libssh2_wincng_hash_ctx *ctx, + BCRYPT_ALG_HANDLE hAlg, ULONG hashlen, + unsigned char *key, ULONG keylen) +{ + BCRYPT_HASH_HANDLE hHash; + unsigned char *pbHashObject; + ULONG 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 void *data, ULONG datalen) +{ + int ret; + + ret = BCryptHashData(ctx->hHash, (PUCHAR)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(const unsigned char *data, ULONG datalen, + BCRYPT_ALG_HANDLE hAlg, + unsigned char *hash, ULONG 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_hmac_ctx_init(libssh2_hmac_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + return 1; +} + +#if LIBSSH2_MD5 +int _libssh2_hmac_md5_init(libssh2_hmac_ctx *ctx, + void *key, size_t keylen) +{ + int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacMD5, + MD5_DIGEST_LENGTH, + key, (ULONG) keylen); + + return ret == 0 ? 1 : 0; +} +#endif + +int _libssh2_hmac_sha1_init(libssh2_hmac_ctx *ctx, + void *key, size_t keylen) +{ + int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA1, + SHA_DIGEST_LENGTH, + key, (ULONG) keylen); + + return ret == 0 ? 1 : 0; +} + +int _libssh2_hmac_sha256_init(libssh2_hmac_ctx *ctx, + void *key, size_t keylen) +{ + int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA256, + SHA256_DIGEST_LENGTH, + key, (ULONG) keylen); + + return ret == 0 ? 1 : 0; +} + +int _libssh2_hmac_sha512_init(libssh2_hmac_ctx *ctx, + void *key, size_t keylen) +{ + int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA512, + SHA512_DIGEST_LENGTH, + key, (ULONG) keylen); + + return ret == 0 ? 1 : 0; +} + +int _libssh2_hmac_update(libssh2_hmac_ctx *ctx, + const void *data, size_t datalen) +{ + int ret = _libssh2_wincng_hash_update(ctx, data, (ULONG) datalen); + + return ret == 0 ? 1 : 0; +} + +int _libssh2_hmac_final(libssh2_hmac_ctx *ctx, void *data) +{ + int ret = BCryptFinishHash(ctx->hHash, data, ctx->cbHash, 0); + + return BCRYPT_SUCCESS(ret) ? 1 : 0; +} + +void _libssh2_hmac_cleanup(libssh2_hmac_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 + */ + +static int +_libssh2_wincng_key_sha_verify(_libssh2_wincng_key_ctx *ctx, + ULONG hashlen, + const unsigned char *sig, + ULONG sig_len, + const unsigned char *m, + ULONG m_len, + ULONG flags) +{ + BCRYPT_PKCS1_PADDING_INFO paddingInfoPKCS1; + BCRYPT_ALG_HANDLE hAlgHash; + void *pPaddingInfo; + unsigned char *data, *hash; + ULONG datalen; + int ret; + + if(hashlen == SHA_DIGEST_LENGTH) { + hAlgHash = _libssh2_wincng.hAlgHashSHA1; + paddingInfoPKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM; + } + else if(hashlen == SHA256_DIGEST_LENGTH) { + hAlgHash = _libssh2_wincng.hAlgHashSHA256; + paddingInfoPKCS1.pszAlgId = BCRYPT_SHA256_ALGORITHM; + } + else if(hashlen == SHA384_DIGEST_LENGTH) { + hAlgHash = _libssh2_wincng.hAlgHashSHA384; + paddingInfoPKCS1.pszAlgId = BCRYPT_SHA384_ALGORITHM; + } + else if(hashlen == SHA512_DIGEST_LENGTH) { + hAlgHash = _libssh2_wincng.hAlgHashSHA512; + paddingInfoPKCS1.pszAlgId = BCRYPT_SHA512_ALGORITHM; + } + else { + return -1; + } + + datalen = m_len; + data = malloc(datalen); + if(!data) { + return -1; + } + + hash = malloc(hashlen); + if(!hash) { + free(data); + return -1; + } + memcpy(data, m, datalen); + + ret = _libssh2_wincng_hash(data, datalen, + hAlgHash, + 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) { + 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 unsigned char *passphrase, + const char *headerbegin, + const char *headerend, + unsigned char **data, + size_t *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 unsigned char *passphrase, + unsigned char **ppbEncoded, + size_t *pcbEncoded, + int tryLoadRSA, int tryLoadDSA) +{ + unsigned char *data = NULL; + size_t 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 unsigned char *passphrase, + unsigned char **ppbEncoded, + size_t *pcbEncoded, + int tryLoadRSA, int tryLoadDSA) +{ + unsigned char *data = NULL; + size_t 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, + DWORD cbEncoded, + LPCSTR lpszStructType, + unsigned char **ppbDecoded, + DWORD *pcbDecoded) +{ + unsigned char *pbDecoded = NULL; + DWORD 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, + DWORD cbInput, + unsigned char **ppbOutput, + DWORD *pcbOutput) +{ + unsigned char *pbOutput; + DWORD 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, + DWORD cbEncoded, + unsigned char **ppbDecoded, + DWORD *pcbDecoded) +{ + unsigned char *pbDecoded = NULL; + PCRYPT_DATA_BLOB pbInteger; + DWORD cbDecoded = 0, cbInteger; + int ret; + + ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded, + X509_MULTI_BYTE_UINT, + (void *)&pbInteger, &cbInteger); + if(!ret) { + ret = _libssh2_wincng_bn_ltob(pbInteger->pbData, + 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, + DWORD cbEncoded, + unsigned char ***prpbDecoded, + DWORD **prcbDecoded, + DWORD *pcbCount) +{ + PCRYPT_DER_BLOB pBlob; + unsigned char **rpbDecoded; + PCRYPT_SEQUENCE_OF_ANY pbDecoded; + DWORD cbDecoded, *rcbDecoded, index, length; + int ret; + + ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded, + X509_SEQUENCE_OF_ANY, + (void *)&pbDecoded, &cbDecoded); + if(!ret) { + length = pbDecoded->cValue; + + rpbDecoded = malloc(sizeof(PBYTE) * length); + if(rpbDecoded) { + rcbDecoded = malloc(sizeof(DWORD) * length); + if(rcbDecoded) { + for(index = 0; index < length; index++) { + pBlob = &pbDecoded->rgValue[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 */ + +#if LIBSSH2_RSA || LIBSSH2_DSA +static ULONG +_libssh2_wincng_bn_size(const unsigned char *bignum, ULONG length) +{ + ULONG offset; + + if(!bignum) + return 0; + + length--; + + offset = 0; + while(!(*(bignum + offset)) && (offset < length)) + offset++; + + length++; + + return length - offset; +} +#endif + + +#if LIBSSH2_RSA +/*******************************************************************/ +/* + * 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; + ULONG 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; + } + + rsakey = (BCRYPT_RSAKEY_BLOB *)malloc(keylen); + if(!rsakey) { + return -1; + } + + memset(rsakey, 0, keylen); + + + /* https://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */ + rsakey->BitLength = mlen * 8; + rsakey->cbPublicExp = elen; + rsakey->cbModulus = mlen; + + memcpy((unsigned char *)rsakey + offset, edata, elen); + offset += elen; + + if(nlen < mlen) + memcpy((unsigned char *)rsakey + offset + mlen - nlen, ndata, nlen); + else + memcpy((unsigned char *)rsakey + offset, ndata + nlen - mlen, mlen); + + if(ddata && dlen > 0) { + offset += mlen; + + if(plen < p1len) + memcpy((unsigned char *)rsakey + offset + p1len - plen, + pdata, plen); + else + memcpy((unsigned char *)rsakey + offset, + pdata + plen - p1len, p1len); + offset += p1len; + + if(qlen < p2len) + memcpy((unsigned char *)rsakey + offset + p2len - qlen, + qdata, qlen); + else + memcpy((unsigned char *)rsakey + offset, + qdata + qlen - p2len, p2len); + offset += p2len; + + if(e1len < p1len) + memcpy((unsigned char *)rsakey + offset + p1len - e1len, + e1data, e1len); + else + memcpy((unsigned char *)rsakey + offset, + e1data + e1len - p1len, p1len); + offset += p1len; + + if(e2len < p2len) + memcpy((unsigned char *)rsakey + offset + p2len - e2len, + e2data, e2len); + else + memcpy((unsigned char *)rsakey + offset, + e2data + e2len - p2len, p2len); + offset += p2len; + + if(coefflen < p1len) + memcpy((unsigned char *)rsakey + offset + p1len - coefflen, + coeffdata, coefflen); + else + memcpy((unsigned char *)rsakey + offset, + coeffdata + coefflen - p1len, p1len); + offset += p1len; + + if(dlen < mlen) + memcpy((unsigned char *)rsakey + offset + mlen - dlen, + ddata, dlen); + else + memcpy((unsigned char *)rsakey + 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, (PUCHAR)rsakey, keylen, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng_safe_free(rsakey, keylen); + return -1; + } + + + *rsa = malloc(sizeof(libssh2_rsa_ctx)); + if(!(*rsa)) { + BCryptDestroyKey(hKey); + _libssh2_wincng_safe_free(rsakey, keylen); + return -1; + } + + (*rsa)->hKey = hKey; + (*rsa)->pbKeyObject = rsakey; + (*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, + size_t cbEncoded) +{ + BCRYPT_KEY_HANDLE hKey; + unsigned char *pbStructInfo; + DWORD cbStructInfo; + int ret; + + (void)session; + + ret = _libssh2_wincng_asn_decode(pbEncoded, (DWORD)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; + size_t cbEncoded; + int ret; + + (void)session; + + ret = _libssh2_wincng_load_private(session, filename, 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, + const unsigned char *passphrase) +{ +#ifdef HAVE_LIBCRYPT32 + unsigned char *pbEncoded; + size_t cbEncoded; + int ret; + + (void)session; + + ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len, + 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 */ +} + +#if LIBSSH2_RSA_SHA1 +int +_libssh2_wincng_rsa_sha1_verify(libssh2_rsa_ctx *rsa, + const unsigned char *sig, + size_t sig_len, + const unsigned char *m, + size_t m_len) +{ + return _libssh2_wincng_key_sha_verify(rsa, SHA_DIGEST_LENGTH, + sig, (ULONG)sig_len, + m, (ULONG)m_len, + BCRYPT_PAD_PKCS1); +} +#endif + +#if LIBSSH2_RSA_SHA2 +int +_libssh2_wincng_rsa_sha2_verify(libssh2_rsa_ctx *rsa, + size_t hash_len, + const unsigned char *sig, + size_t sig_len, + const unsigned char *m, + size_t m_len) +{ + return _libssh2_wincng_key_sha_verify(rsa, (ULONG)hash_len, + sig, (ULONG)sig_len, + m, (ULONG)m_len, + BCRYPT_PAD_PKCS1); +} +#endif + +static int +_libssh2_wincng_rsa_sha_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; + ULONG cbData, datalen, siglen; + NTSTATUS ret; + + if(hash_len == SHA_DIGEST_LENGTH) + paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM; + else if(hash_len == SHA256_DIGEST_LENGTH) + paddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM; + else if(hash_len == SHA384_DIGEST_LENGTH) + paddingInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM; + else if(hash_len == SHA512_DIGEST_LENGTH) + paddingInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM; + else { + _libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Unsupported hash digest length"); + return -1; + } + + datalen = (ULONG)hash_len; + data = malloc(datalen); + if(!data) { + return -1; + } + 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 = (NTSTATUS)STATUS_NO_MEMORY; + } + + _libssh2_wincng_safe_free(data, datalen); + + return BCRYPT_SUCCESS(ret) ? 0 : -1; +} + +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) +{ + return _libssh2_wincng_rsa_sha_sign(session, rsa, + hash, hash_len, + signature, signature_len); +} + +int +_libssh2_wincng_rsa_sha2_sign(LIBSSH2_SESSION *session, + libssh2_rsa_ctx *rsa, + const unsigned char *hash, + size_t hash_len, + unsigned char **signature, + size_t *signature_len) +{ + return _libssh2_wincng_rsa_sha_sign(session, rsa, + hash, hash_len, + signature, signature_len); +} + +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)); +} +#endif + +/*******************************************************************/ +/* + * 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; + ULONG 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; + + dsakey = (BCRYPT_DSA_KEY_BLOB *)malloc(keylen); + if(!dsakey) { + return -1; + } + + memset(dsakey, 0, keylen); + + + /* https://msdn.microsoft.com/library/windows/desktop/aa833126.aspx */ + 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((unsigned char *)dsakey + offset + length - plen, + pdata, plen); + else + memcpy((unsigned char *)dsakey + offset, + pdata + plen - length, length); + offset += length; + + if(glen < length) + memcpy((unsigned char *)dsakey + offset + length - glen, + gdata, glen); + else + memcpy((unsigned char *)dsakey + offset, + gdata + glen - length, length); + offset += length; + + if(ylen < length) + memcpy((unsigned char *)dsakey + offset + length - ylen, + ydata, ylen); + else + memcpy((unsigned char *)dsakey + offset, + ydata + ylen - length, length); + + if(xdata && xlen > 0) { + offset += length; + + if(xlen < 20) + memcpy((unsigned char *)dsakey + offset + 20 - xlen, xdata, xlen); + else + memcpy((unsigned char *)dsakey + 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, (PUCHAR)dsakey, keylen, 0); + if(!BCRYPT_SUCCESS(ret)) { + _libssh2_wincng_safe_free(dsakey, keylen); + return -1; + } + + + *dsa = malloc(sizeof(libssh2_dsa_ctx)); + if(!(*dsa)) { + BCryptDestroyKey(hKey); + _libssh2_wincng_safe_free(dsakey, keylen); + return -1; + } + + (*dsa)->hKey = hKey; + (*dsa)->pbKeyObject = dsakey; + (*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, + size_t cbEncoded) +{ + unsigned char **rpbDecoded; + DWORD *rcbDecoded, index, length; + int ret; + + (void)session; + + ret = _libssh2_wincng_asn_decode_bns(pbEncoded, (DWORD)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; + size_t cbEncoded; + int ret; + + ret = _libssh2_wincng_load_private(session, filename, 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, + const unsigned char *passphrase) +{ +#ifdef HAVE_LIBCRYPT32 + unsigned char *pbEncoded; + size_t cbEncoded; + int ret; + + ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len, + 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, + size_t m_len) +{ + return _libssh2_wincng_key_sha_verify(dsa, SHA_DIGEST_LENGTH, sig_fixed, + 40, m, (ULONG)m_len, 0); +} + +int +_libssh2_wincng_dsa_sha1_sign(libssh2_dsa_ctx *dsa, + const unsigned char *hash, + size_t hash_len, + unsigned char *sig_fixed) +{ + unsigned char *data, *sig; + ULONG cbData, datalen, siglen; + NTSTATUS ret; + + datalen = (ULONG)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 = (NTSTATUS)STATUS_NO_MEMORY; + } + else + ret = (NTSTATUS)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: ECDSA helper functions + */ + +#if LIBSSH2_ECDSA + +/* + * Decode an uncompressed point. + */ +static int +_libssh2_wincng_ecdsa_decode_uncompressed_point( + IN const unsigned char *encoded_point, + IN size_t encoded_point_len, + OUT _libssh2_ecdsa_point *point) +{ + unsigned int curve; + + if(!point) { + return LIBSSH2_ERROR_INVAL; + } + + /* Verify that the point uses uncompressed format */ + if(encoded_point_len == 0 || encoded_point[0] != 4) { + return LIBSSH2_ERROR_INVAL; + } + + for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) { + if(_wincng_ecdsa_algorithms[curve].point_length == + (encoded_point_len - 1) / 2) { + + point->curve = curve; + + point->x = encoded_point + 1; + point->x_len = _wincng_ecdsa_algorithms[curve].point_length; + + point->y = point->x + point->x_len; + point->y_len = _wincng_ecdsa_algorithms[curve].point_length; + + return LIBSSH2_ERROR_NONE; + } + } + + return LIBSSH2_ERROR_INVAL; +} + +/* + * Create a IEEE P-1363 signature from a point. + * + * The IEEE P-1363 format is defined as r || s, + * where r and s are of the same length. + */ +static int +_libssh2_wincng_p1363signature_from_point(IN const unsigned char *r, + IN size_t r_len, + IN const unsigned char *s, + IN size_t s_len, + IN libssh2_curve_type curve, + OUT PUCHAR *signature, + OUT size_t *signature_length) +{ + const unsigned char *r_trimmed; + const unsigned char *s_trimmed; + size_t r_trimmed_len; + size_t s_trimmed_len; + + /* Validate parameters */ + if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) { + return LIBSSH2_ERROR_INVAL; + } + + *signature = NULL; + *signature_length = (size_t) + _wincng_ecdsa_algorithms[curve].point_length * 2; + + /* Trim leading zero, if any */ + r_trimmed = r; + r_trimmed_len = r_len; + if(r_len > 0 && r[0] == '\0') { + r_trimmed++; + r_trimmed_len--; + } + + s_trimmed = s; + s_trimmed_len = s_len; + if(s_len > 0 && s[0] == '\0') { + s_trimmed++; + s_trimmed_len--; + } + + /* Concatenate into zero-filled buffer and zero-pad if necessary */ + *signature = calloc(1, *signature_length); + if(!*signature) { + return LIBSSH2_ERROR_ALLOC; + } + + memcpy( + *signature + (*signature_length / 2) - r_trimmed_len, + r_trimmed, + r_trimmed_len); + memcpy( + *signature + (*signature_length) - s_trimmed_len, + s_trimmed, + s_trimmed_len); + + return LIBSSH2_ERROR_NONE; +} + +/* + * Create a CNG public key from an ECC point. + */ +static int +_libssh2_wincng_publickey_from_point(IN _libssh2_wincng_ecc_keytype keytype, + IN _libssh2_ecdsa_point *point, + OUT BCRYPT_KEY_HANDLE *key) +{ + + int result = LIBSSH2_ERROR_NONE; + NTSTATUS status; + + PBCRYPT_ECCKEY_BLOB ecc_blob; + size_t ecc_blob_len; + + /* Validate parameters */ + if(!key) { + return LIBSSH2_ERROR_INVAL; + } + + if(point->x_len != point->y_len) { + return LIBSSH2_ERROR_INVAL; + } + + *key = NULL; + + /* Initialize a blob to import */ + ecc_blob_len = sizeof(BCRYPT_ECCKEY_BLOB) + point->x_len + point->y_len; + ecc_blob = malloc(ecc_blob_len); + if(!ecc_blob) { + return LIBSSH2_ERROR_ALLOC; + } + + ecc_blob->cbKey = point->x_len; + ecc_blob->dwMagic = + _wincng_ecdsa_algorithms[point->curve].public_import_magic[keytype]; + + /** Copy x, y */ + memcpy( + (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB), + point->x, + point->x_len); + memcpy( + (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB) + point->x_len, + point->y, + point->y_len); + + status = BCryptImportKeyPair( + keytype == WINCNG_ECC_KEYTYPE_ECDSA + ? _libssh2_wincng.hAlgECDSA[point->curve] + : _libssh2_wincng.hAlgECDH[point->curve], + NULL, + BCRYPT_ECCPUBLIC_BLOB, + key, + (PUCHAR)ecc_blob, + (ULONG)ecc_blob_len, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + result = LIBSSH2_ERROR_NONE; + +cleanup: + free(ecc_blob); + return result; +} + +/* + * Create a CNG private key from an ECC point. + */ +static int +_libssh2_wincng_privatekey_from_point(IN _libssh2_wincng_ecc_keytype keytype, + IN _libssh2_ecdsa_point *q, + IN unsigned char *d, + IN size_t d_len, + OUT BCRYPT_KEY_HANDLE *key) +{ + int result = LIBSSH2_ERROR_NONE; + NTSTATUS status; + + PBCRYPT_ECCKEY_BLOB ecc_blob; + size_t ecc_blob_len; + + /* Validate parameters */ + if(!key) { + return LIBSSH2_ERROR_INVAL; + } + + if(q->x_len != q->y_len) { + return LIBSSH2_ERROR_INVAL; + } + + *key = NULL; + + /* Initialize a blob to import */ + ecc_blob_len = + sizeof(BCRYPT_ECCPRIVATE_BLOB) + q->x_len + q->y_len + d_len; + ecc_blob = malloc(ecc_blob_len); + if(!ecc_blob) { + return LIBSSH2_ERROR_ALLOC; + } + + ecc_blob->cbKey = q->x_len; + ecc_blob->dwMagic = + _wincng_ecdsa_algorithms[q->curve].private_import_magic[keytype]; + + /* Copy x, y, d */ + memcpy( + (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB), + q->x, + q->x_len); + memcpy( + (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB) + q->x_len, + q->y, + q->y_len); + memcpy( + (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB) + q->x_len + q->y_len, + d, + d_len); + + status = BCryptImportKeyPair( + keytype == WINCNG_ECC_KEYTYPE_ECDSA + ? _libssh2_wincng.hAlgECDSA[q->curve] + : _libssh2_wincng.hAlgECDH[q->curve], + NULL, + BCRYPT_ECCPRIVATE_BLOB, + key, + (PUCHAR)ecc_blob, + (ULONG)ecc_blob_len, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + result = LIBSSH2_ERROR_NONE; + +cleanup: + free(ecc_blob); + return result; +} + +/* + * Get the uncompressed point encoding for a CNG key. + */ +static int +_libssh2_wincng_uncompressed_point_from_publickey( + IN LIBSSH2_SESSION *session, + IN libssh2_curve_type curve, + IN BCRYPT_KEY_HANDLE key, + OUT PUCHAR *encoded_point, + OUT size_t *encoded_point_len) +{ + int result = LIBSSH2_ERROR_NONE; + NTSTATUS status; + + PBCRYPT_ECCKEY_BLOB ecc_blob = NULL; + ULONG ecc_blob_len; + PUCHAR point_x; + PUCHAR point_y; + + /* Validate parameters */ + if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) { + return LIBSSH2_ERROR_INVAL; + } + + if(!encoded_point || !encoded_point_len) { + return LIBSSH2_ERROR_INVAL; + } + + *encoded_point = NULL; + *encoded_point_len = 0; + + /* + * Export point as BCRYPT_ECCKEY_BLOB, a dynamically-sized structure. + */ + status = BCryptExportKey(key, + NULL, + BCRYPT_ECCPUBLIC_BLOB, + NULL, + 0, + &ecc_blob_len, + 0); + if(BCRYPT_SUCCESS(status) && ecc_blob_len > 0) { + ecc_blob = LIBSSH2_ALLOC(session, ecc_blob_len); + if(!ecc_blob) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + status = BCryptExportKey(key, + NULL, + BCRYPT_ECCPUBLIC_BLOB, + (PUCHAR)ecc_blob, + ecc_blob_len, + &ecc_blob_len, + 0); + } + + if(!BCRYPT_SUCCESS(status)) { + result = _libssh2_error(session, + LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Decoding the ECC public key failed"); + goto cleanup; + } + + point_x = (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB); + point_y = (PUCHAR)ecc_blob + ecc_blob->cbKey + sizeof(BCRYPT_ECCKEY_BLOB); + + /* + * Create uncompressed point, which needs to look like the following: + * + * struct uncompressed_point { + * UCHAR tag = 4; // uncompressed + * PUCHAR[size] x; + * PUCHAR[size] y; + * } + */ + + *encoded_point_len = (size_t)ecc_blob->cbKey * 2 + 1; + *encoded_point = LIBSSH2_ALLOC(session, *encoded_point_len); + if(!*encoded_point) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + **encoded_point = 4; /* Uncompressed tag */ + memcpy((*encoded_point) + 1, point_x, ecc_blob->cbKey); + memcpy((*encoded_point) + 1 + ecc_blob->cbKey, point_y, ecc_blob->cbKey); + +cleanup: + if(ecc_blob) { + LIBSSH2_FREE(session, ecc_blob); + } + + return result; +} + +static void +_libssh_wincng_reverse_bytes(IN PUCHAR buffer, + IN size_t buffer_len) +{ + PUCHAR start = buffer; + PUCHAR end = buffer + buffer_len - 1; + while(start < end) { + unsigned char tmp = *end; + *end = *start; + *start = tmp; + start++; + end--; + } +} + +/*******************************************************************/ +/* + * Windows CNG backend: ECDSA functions + */ + +void +_libssh2_wincng_ecdsa_free(IN _libssh2_wincng_ecdsa_key *key) +{ + if(!key) { + return; + } + + (void)BCryptDestroyKey(key->handle); + free(key); +} + + +/* + * _libssh2_ecdsa_create_key + * + * Creates a local private ECDH key based on input curve + * and returns the public key in uncompressed point encoding. + */ + +int +_libssh2_wincng_ecdh_create_key(IN LIBSSH2_SESSION *session, + OUT _libssh2_wincng_ecdsa_key **privatekey, + OUT unsigned char **encoded_publickey, + OUT size_t *encoded_publickey_len, + IN libssh2_curve_type curve) +{ + int result = LIBSSH2_ERROR_NONE; + NTSTATUS status; + + BCRYPT_KEY_HANDLE key_handle = NULL; + + /* Validate parameters */ + if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) { + return LIBSSH2_ERROR_INVAL; + } + + if(!_libssh2_wincng.hAlgECDH[curve]) { + return LIBSSH2_ERROR_INVAL; + } + + if(!privatekey || !encoded_publickey || !encoded_publickey_len) { + return LIBSSH2_ERROR_INVAL; + } + + *privatekey = NULL; + *encoded_publickey = NULL; + *encoded_publickey_len = 0; + + /* Create an ECDH key pair using the requested curve */ + status = BCryptGenerateKeyPair( + _libssh2_wincng.hAlgECDH[curve], + &key_handle, + _wincng_ecdsa_algorithms[curve].key_length, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = _libssh2_error( + session, + LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Creating ECC key pair failed"); + goto cleanup; + } + + status = BCryptFinalizeKeyPair(key_handle, 0); + if(!BCRYPT_SUCCESS(status)) { + result = _libssh2_error( + session, + LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Creating ECDH key pair failed"); + goto cleanup; + } + + result = _libssh2_wincng_uncompressed_point_from_publickey( + session, + curve, + key_handle, + encoded_publickey, + encoded_publickey_len); + if(result != LIBSSH2_ERROR_NONE) { + result = _libssh2_error( + session, + LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Exporting ECDH key pair failed"); + } + + *privatekey = malloc(sizeof(_libssh2_wincng_ecdsa_key)); + if(!*privatekey) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + (*privatekey)->curve = curve; + (*privatekey)->handle = key_handle; + +cleanup: + if(result != LIBSSH2_ERROR_NONE && key_handle) { + (void)BCryptDestroyKey(key_handle); + } + + if(result != LIBSSH2_ERROR_NONE && *privatekey) { + free(*privatekey); + } + + return result; +} + +/* + * _libssh2_ecdsa_curve_name_with_octal_new + * + * Creates an ECDSA public key from an uncompressed point. + */ + +int +_libssh2_wincng_ecdsa_curve_name_with_octal_new( + OUT _libssh2_wincng_ecdsa_key **key, + IN const unsigned char *publickey_encoded, + IN size_t publickey_encoded_len, + IN libssh2_curve_type curve) +{ + int result = LIBSSH2_ERROR_NONE; + + BCRYPT_KEY_HANDLE publickey_handle; + _libssh2_ecdsa_point publickey; + + /* Validate parameters */ + if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) { + return LIBSSH2_ERROR_INVAL; + } + + if(!key) { + return LIBSSH2_ERROR_INVAL; + } + + *key = NULL; + + result = _libssh2_wincng_ecdsa_decode_uncompressed_point( + publickey_encoded, + publickey_encoded_len, + &publickey); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + result = _libssh2_wincng_publickey_from_point( + WINCNG_ECC_KEYTYPE_ECDSA, + &publickey, + &publickey_handle); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + *key = malloc(sizeof(_libssh2_wincng_ecdsa_key)); + if(!*key) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + (*key)->handle = publickey_handle; + (*key)->curve = curve; + +cleanup: + + return result; +} + +/* + * _libssh2_ecdh_gen_k + * + * Computes the shared secret K given a local private key, + * remote public key and length + */ + +int +_libssh2_wincng_ecdh_gen_k(OUT _libssh2_bn **secret, + IN _libssh2_wincng_ecdsa_key *privatekey, + IN const unsigned char *server_publickey_encoded, + IN size_t server_publickey_encoded_len) +{ + int result = LIBSSH2_ERROR_NONE; + NTSTATUS status; + + BCRYPT_KEY_HANDLE publickey_handle; + BCRYPT_SECRET_HANDLE agreed_secret_handle = NULL; + ULONG secret_len; + _libssh2_ecdsa_point server_publickey; + + /* Validate parameters */ + if(!secret) { + return LIBSSH2_ERROR_INVAL; + } + + *secret = NULL; + + /* Decode the public key */ + result = _libssh2_wincng_ecdsa_decode_uncompressed_point( + server_publickey_encoded, + server_publickey_encoded_len, + &server_publickey); + if(result != LIBSSH2_ERROR_NONE) { + return result; + } + + result = _libssh2_wincng_publickey_from_point( + WINCNG_ECC_KEYTYPE_ECDH, + &server_publickey, + &publickey_handle); + if(result != LIBSSH2_ERROR_NONE) { + return result; + } + + /* Establish the shared secret between ourselves and the peer */ + status = BCryptSecretAgreement( + privatekey->handle, + publickey_handle, + &agreed_secret_handle, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + /* Compute the size of the buffer that is needed to hold the derived + * shared secret. + * + * NB. The use of BCRYPT_KDF_RAW_SECRET requires Windows 10 or newer. + * On older versions, the BCryptDeriveKey returns STATUS_NOT_SUPPORTED. + */ + status = BCryptDeriveKey( + agreed_secret_handle, + BCRYPT_KDF_RAW_SECRET, + NULL, + NULL, + 0, + &secret_len, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + /* Allocate a secret bignum to be ready to receive the derived secret */ + *secret = _libssh2_wincng_bignum_init(); + if(!*secret) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + if(_libssh2_wincng_bignum_resize(*secret, secret_len)) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + /* And populate the secret bignum */ + status = BCryptDeriveKey( + agreed_secret_handle, + BCRYPT_KDF_RAW_SECRET, + NULL, + (*secret)->bignum, + secret_len, + &secret_len, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + /* BCRYPT_KDF_RAW_SECRET returns the little-endian representation of the + * raw secret, so we need to swap it to big endian order. + */ + + _libssh_wincng_reverse_bytes((*secret)->bignum, secret_len); + + result = LIBSSH2_ERROR_NONE; + +cleanup: + if(result != LIBSSH2_ERROR_NONE && agreed_secret_handle) { + _libssh2_wincng_bignum_free(*secret); + } + + if(result != LIBSSH2_ERROR_NONE && agreed_secret_handle) { + BCryptDestroySecret(agreed_secret_handle); + } + + return result; +} + +/* + * _libssh2_ecdsa_curve_type_from_name + * + */ +int +_libssh2_wincng_ecdsa_curve_type_from_name(IN const char *name, + OUT libssh2_curve_type *out_curve) +{ + unsigned int curve; + + /* Validate parameters */ + if(!out_curve) { + return LIBSSH2_ERROR_INVAL; + } + + for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) { + if(strcmp(name, _wincng_ecdsa_algorithms[curve].name) == 0) { + *out_curve = curve; + return LIBSSH2_ERROR_NONE; + } + } + + return LIBSSH2_ERROR_INVAL; +} + +/* + * _libssh2_ecdsa_verify + * + * Verifies the ECDSA signature of a hashed message + * + */ + +int +_libssh2_wincng_ecdsa_verify(IN _libssh2_wincng_ecdsa_key *key, + IN const unsigned char *r, + IN size_t r_len, + IN const unsigned char *s, + IN size_t s_len, + IN const unsigned char *m, + IN size_t m_len) +{ + int result = LIBSSH2_ERROR_NONE; + NTSTATUS status; + + PUCHAR signature_p1363 = NULL; + size_t signature_p1363_len; + ULONG hash_len; + PUCHAR hash = NULL; + BCRYPT_ALG_HANDLE hash_alg; + + /* CNG expects signatures in IEEE P-1363 format. */ + result = _libssh2_wincng_p1363signature_from_point( + r, + r_len, + s, + s_len, + _libssh2_wincng_ecdsa_get_curve_type(key), + &signature_p1363, + &signature_p1363_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Create hash over m */ + switch(_libssh2_wincng_ecdsa_get_curve_type(key)) { + case LIBSSH2_EC_CURVE_NISTP256: + hash_len = 256/8; + hash_alg = _libssh2_wincng.hAlgHashSHA256; + break; + + case LIBSSH2_EC_CURVE_NISTP384: + hash_len = 384/8; + hash_alg = _libssh2_wincng.hAlgHashSHA384; + break; + + case LIBSSH2_EC_CURVE_NISTP521: + hash_len = 512/8; + hash_alg = _libssh2_wincng.hAlgHashSHA512; + break; + + default: + return LIBSSH2_ERROR_INVAL; + } + + hash = malloc(hash_len); + result = _libssh2_wincng_hash( + m, + (ULONG)m_len, + hash_alg, + hash, + hash_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Verify signature over hash */ + status = BCryptVerifySignature( + key->handle, + NULL, + hash, + hash_len, + signature_p1363, + (ULONG)signature_p1363_len, + 0); + + if(status == STATUS_INVALID_SIGNATURE) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + else if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + result = LIBSSH2_ERROR_NONE; + +cleanup: + if(hash) { + free(hash); + } + + if(signature_p1363) { + free(signature_p1363); + } + + return result; +} + +/* + *_libssh2_ecdsa_new_private + * + * Creates a new private key given a file path and password + * + */ + +int +_libssh2_wincng_ecdsa_new_private(OUT _libssh2_wincng_ecdsa_key **key, + IN LIBSSH2_SESSION *session, + IN const char *filename, + IN const unsigned char *passphrase) +{ + int result; + + FILE *file_handle = NULL; + unsigned char *data = NULL; + size_t datalen = 0; + + /* Validate parameters */ + if(!key || !session || !filename) { + return LIBSSH2_ERROR_INVAL; + } + + *key = NULL; + + if(passphrase && strlen((const char *)passphrase) > 0) { + return _libssh2_error( + session, + LIBSSH2_ERROR_INVAL, + "Passphrase-protected ECDSA private key files are unsupported"); + } + + file_handle = fopen(filename, FOPEN_READTEXT); + if(!file_handle) { + result = _libssh2_error( + session, + LIBSSH2_ERROR_INVAL, + "Opening the private key file failed"); + goto cleanup; + } + + result = _libssh2_pem_parse(session, + PEM_ECDSA_HEADER, + PEM_ECDSA_FOOTER, + passphrase, + file_handle, + &data, + &datalen); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + result = _libssh2_wincng_ecdsa_new_private_frommemory( + key, + session, + (const char *)data, + datalen, + passphrase); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + +cleanup: + if(file_handle) { + fclose(file_handle); + } + + if(data) { + LIBSSH2_FREE(session, data); + } + + return result; +} + +int +_libssh2_wincng_parse_ecdsa_privatekey(OUT _libssh2_wincng_ecdsa_key **key, + IN unsigned char *privatekey, + IN size_t privatekey_len) +{ + char *keytype = NULL; + size_t keytype_len; + + unsigned char *ignore; + size_t ignore_len; + + unsigned char *publickey; + size_t publickey_len; + + libssh2_curve_type curve_type; + int result; + uint32_t check1, check2; + struct string_buf data_buffer; + + _libssh2_ecdsa_point q; + unsigned char *d; + size_t d_len; + + BCRYPT_KEY_HANDLE key_handle = NULL; + + *key = NULL; + + data_buffer.data = privatekey; + data_buffer.dataptr = privatekey; + data_buffer.len = privatekey_len; + + /* Read the 2 checkints and check that they match */ + result = _libssh2_get_u32(&data_buffer, &check1); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + result = _libssh2_get_u32(&data_buffer, &check2); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + if(check1 != check2) { + result = LIBSSH2_ERROR_FILE; + goto cleanup; + } + + /* What follows is a key as defined in */ + /* draft-miller-ssh-agent, section-3.2.2 */ + + /* Read the key type */ + result = _libssh2_get_string(&data_buffer, + (unsigned char **)&keytype, + &keytype_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + result = _libssh2_wincng_ecdsa_curve_type_from_name(keytype, &curve_type); + if(result < 0) { + goto cleanup; + } + + /* Read the curve */ + result = _libssh2_get_string(&data_buffer, &ignore, &ignore_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Read Q */ + result = _libssh2_get_string(&data_buffer, &publickey, &publickey_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + result = _libssh2_wincng_ecdsa_decode_uncompressed_point( + publickey, + publickey_len, + &q); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Read d */ + result = _libssh2_get_bignum_bytes(&data_buffer, &d, &d_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Ignore the rest (comment, etc) */ + + /* Use Q and d to create a key handle */ + result = _libssh2_wincng_privatekey_from_point( + WINCNG_ECC_KEYTYPE_ECDSA, + &q, + d, + d_len, + &key_handle); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + *key = malloc(sizeof(_libssh2_wincng_ecdsa_key)); + if(!*key) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + (*key)->curve = q.curve; + (*key)->handle = key_handle; + + result = LIBSSH2_ERROR_NONE; + +cleanup: + if(result != LIBSSH2_ERROR_NONE && key_handle) { + (void)BCryptDestroyKey(key_handle); + } + + return result; +} + +/* + * _libssh2_ecdsa_new_private + * + * Creates a new private key given a file data and password. + * ECDSA private key files use the decoding defined in PROTOCOL.key + * in the OpenSSL source tree. + */ +int +_libssh2_wincng_ecdsa_new_private_frommemory( + OUT _libssh2_wincng_ecdsa_key **key, + IN LIBSSH2_SESSION *session, + IN const char *data, + IN size_t data_len, + IN const unsigned char *passphrase) +{ + int result; + + struct string_buf data_buffer; + uint32_t index; + uint32_t key_count; + unsigned char *privatekey; + size_t privatekey_len; + + /* Validate parameters */ + if(!key || !session || !data) { + return LIBSSH2_ERROR_INVAL; + } + + *key = NULL; + + if(passphrase && strlen((const char *)passphrase) > 0) { + return _libssh2_error( + session, + LIBSSH2_ERROR_INVAL, + "Passphrase-protected ECDSA private key files are unsupported"); + } + + /* Read OPENSSL_PRIVATEKEY_AUTH_MAGIC */ + if(strncmp(data, OPENSSL_PRIVATEKEY_AUTH_MAGIC, data_len) != 0) { + result = -1; + goto cleanup; + } + + data_buffer.len = data_len; + data_buffer.data = (unsigned char *)data; + data_buffer.dataptr = + (unsigned char *)data + strlen(OPENSSL_PRIVATEKEY_AUTH_MAGIC) + 1; + + /* Read ciphername, should be 'none' as we don't support passphrases */ + result = _libssh2_match_string(&data_buffer, "none"); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Read kdfname, should be 'none' as we don't support passphrases */ + result = _libssh2_match_string(&data_buffer, "none"); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Read kdfoptions, should be empty */ + result = _libssh2_match_string(&data_buffer, ""); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + /* Read number of keys N */ + result = _libssh2_get_u32(&data_buffer, &key_count); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + if(key_count == 0) { + result = LIBSSH2_ERROR_FILE; + goto cleanup; + } + + /* Skip all public keys */ + for(index = 0; index < key_count; index++) { + unsigned char *publickey; + size_t publickey_len; + + result = _libssh2_get_string(&data_buffer, &publickey, &publickey_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + } + + /* Read first private key */ + result = _libssh2_get_string(&data_buffer, &privatekey, &privatekey_len); + if(result != LIBSSH2_ERROR_NONE) { + goto cleanup; + } + + result = _libssh2_wincng_parse_ecdsa_privatekey( + key, + privatekey, + privatekey_len); + +cleanup: + if(result != LIBSSH2_ERROR_NONE) { + return _libssh2_error( + session, + result, + "The key is malformed"); + } + + return result; +} + +/* + * _libssh2_ecdsa_sign + * + * Computes the ECDSA signature of a previously-hashed message + * + */ + +int +_libssh2_wincng_ecdsa_sign(IN LIBSSH2_SESSION *session, + IN _libssh2_wincng_ecdsa_key *key, + IN const unsigned char *hash, + IN size_t hash_len, + OUT unsigned char **signature, + OUT size_t *signature_len) +{ + NTSTATUS status; + int result = LIBSSH2_ERROR_NONE; + + unsigned char *hash_buffer; + + unsigned char *cng_signature = NULL; + ULONG cng_signature_len; + + ULONG signature_maxlen; + unsigned char *signature_ptr; + + *signature = NULL; + *signature_len = 0; + + /* CNG expects a mutable buffer */ + hash_buffer = malloc(hash_len); + if(!hash_buffer) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + memcpy(hash_buffer, hash, hash_len); + + status = BCryptSignHash( + key->handle, + NULL, + hash_buffer, + (ULONG)hash_len, + NULL, + 0, + &cng_signature_len, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + cng_signature = malloc(cng_signature_len); + if(!cng_signature) { + result = LIBSSH2_ERROR_ALLOC; + goto cleanup; + } + + status = BCryptSignHash( + key->handle, + NULL, + hash_buffer, + (ULONG)hash_len, + cng_signature, + cng_signature_len, + &cng_signature_len, + 0); + if(!BCRYPT_SUCCESS(status)) { + result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL; + goto cleanup; + } + + /* + cng_signature is in IEEE P-1163 format: r || s. + Convert to ecdsa_signature_blob: mpint(r) || mpint(s) + */ + + signature_maxlen = + cng_signature_len / 2 + 5 + /* mpint(r) */ + cng_signature_len / 2 + 5; /* mpint(s) */ + + *signature = LIBSSH2_ALLOC(session, signature_maxlen); + signature_ptr = *signature; + + _libssh2_store_bignum2_bytes( + &signature_ptr, + cng_signature, + cng_signature_len / 2); + + _libssh2_store_bignum2_bytes( + &signature_ptr, + cng_signature + (cng_signature_len / 2), + cng_signature_len / 2); + + *signature_len = signature_ptr - *signature; + +cleanup: + if(cng_signature) { + free(cng_signature); + } + + if(hash_buffer) { + free(hash_buffer); + } + + return result; +} + +/* + * _libssh2_ecdsa_get_curve_type + * + * returns key curve type that maps to libssh2_curve_type + * + */ + +libssh2_curve_type +_libssh2_wincng_ecdsa_get_curve_type(IN _libssh2_wincng_ecdsa_key *key) +{ + return key->curve; +} + +#endif + +/*******************************************************************/ +/* + * Windows CNG backend: Key functions + */ + +#ifdef HAVE_LIBCRYPT32 +static DWORD +_libssh2_wincng_pub_priv_write(unsigned char *key, + DWORD offset, + const unsigned char *bignum, + const DWORD 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, + size_t cbEncoded) +{ + unsigned char **rpbDecoded = NULL; + DWORD *rcbDecoded = NULL; + unsigned char *key = NULL, *mth = NULL; + DWORD keylen = 0, mthlen = 0; + DWORD index, offset, length = 0; + int ret; + + ret = _libssh2_wincng_asn_decode_bns(pbEncoded, (DWORD)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; + size_t cbEncoded; + int ret; + + ret = _libssh2_wincng_load_private(session, privatekey, + (const unsigned char *)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; + size_t cbEncoded; + int ret; + + ret = _libssh2_wincng_load_private_memory(session, privatekeydata, + privatekeydata_len, + (const unsigned char *) + 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 */ +} + +int +_libssh2_wincng_sk_pub_keyfilememory(LIBSSH2_SESSION *session, + unsigned char **method, + size_t *method_len, + unsigned char **pubkeydata, + size_t *pubkeydata_len, + int *algorithm, + unsigned char *flags, + const char **application, + const unsigned char **key_handle, + size_t *handle_len, + const char *privatekeydata, + size_t privatekeydata_len, + const char *passphrase) +{ + (void)method; + (void)method_len; + (void)pubkeydata; + (void)pubkeydata_len; + (void)algorithm; + (void)flags; + (void)application; + (void)key_handle; + (void)handle_len; + (void)privatekeydata; + (void)privatekeydata_len; + (void)passphrase; + + return _libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to extract public SK key from private key file: " + "Method unimplemented in Windows CNG backend"); +} + +/*******************************************************************/ +/* + * 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, *pbCtr, *pbIVCopy; + ULONG 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 = (ULONG)sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + + type.dwKeyLength; + header = (BCRYPT_KEY_DATA_BLOB_HEADER *)malloc(keylen); + if(!header) { + free(pbKeyObject); + return -1; + } + + + header->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC; + header->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1; + header->cbKeyData = type.dwKeyLength; + + memcpy((unsigned char *)header + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER), + secret, type.dwKeyLength); + + ret = BCryptImportKey(*type.phAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey, + pbKeyObject, dwKeyObject, + (PUCHAR)header, keylen, 0); + + _libssh2_wincng_safe_free(header, 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, int firstlast) +{ + unsigned char *pbOutput, *pbInput; + ULONG cbOutput, cbInput; + NTSTATUS ret; + + (void)type; + (void)firstlast; + + cbInput = (ULONG)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 = (NTSTATUS)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, ULONG length) +{ + unsigned char *bignum; + + if(!bn) + return -1; + + if(length == bn->length) + return 0; + + if(bn->bignum && bn->length > 0 && length < bn->length) { + _libssh2_explicit_zero(bn->bignum + length, bn->length - length); + } + + 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; + ULONG length; + + if(!rnd) + return -1; + + length = (ULONG) (ceil(((double)bits) / 8.0) * 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; + if(bits == 0) + bits = 8; + + /* fill most significant byte with zero padding */ + bignum[0] &= (unsigned char)((1 << bits) - 1); + + /* set most significant bits in most significant byte */ + if(top == 0) + bignum[0] |= (unsigned char)(1 << (bits - 1)); + else if(top == 1) + bignum[0] |= (unsigned char)(3 << (bits - 2)); + + /* 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 *bignum; + ULONG keylen, offset, length; + NTSTATUS ret; + + if(!r || !a || !p || !m) + return -1; + + offset = sizeof(BCRYPT_RSAKEY_BLOB); + keylen = offset + p->length + m->length; + + rsakey = (BCRYPT_RSAKEY_BLOB *)malloc(keylen); + if(!rsakey) + return -1; + + + /* https://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */ + 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((unsigned char *)rsakey + offset, p->bignum, p->length); + offset += p->length; + + memcpy((unsigned char *)rsakey + offset, m->bignum, m->length); + offset = 0; + + ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, + BCRYPT_RSAPUBLIC_BLOB, &hKey, + (PUCHAR)rsakey, keylen, 0); + 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) { + memcpy_with_be_padding(bignum, length, + 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 = (NTSTATUS)STATUS_NO_MEMORY; + } + else + ret = (NTSTATUS)STATUS_NO_MEMORY; + } + + BCryptDestroyKey(hKey); + } + + _libssh2_wincng_safe_free(rsakey, keylen); + + return BCRYPT_SUCCESS(ret) ? 0 : -1; +} + +int +_libssh2_wincng_bignum_set_word(_libssh2_bn *bn, ULONG word) +{ + ULONG offset, number, bits, length; + + if(!bn) + return -1; + + bits = 0; + number = word; + while(number >>= 1) + bits++; + bits++; + + length = (ULONG) (ceil(((double)bits) / 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; +} + +ULONG +_libssh2_wincng_bignum_bits(const _libssh2_bn *bn) +{ + unsigned char number; + ULONG offset, length, bits; + + if(!bn || !bn->bignum || !bn->length) + return 0; + + offset = 0; + length = bn->length - 1; + while(!bn->bignum[offset] && offset < length) + offset++; + + bits = (length - offset) * 8; + number = bn->bignum[offset]; + while(number >>= 1) + bits++; + bits++; + + return bits; +} + +int +_libssh2_wincng_bignum_from_bin(_libssh2_bn *bn, ULONG len, + const unsigned char *bin) +{ + unsigned char *bignum; + ULONG offset, length, bits; + + if(!bn || !bin || !len) + return -1; + + if(_libssh2_wincng_bignum_resize(bn, len)) + return -1; + + memcpy(bn->bignum, bin, len); + + bits = _libssh2_wincng_bignum_bits(bn); + length = (ULONG) (ceil(((double)bits) / 8.0) * sizeof(unsigned char)); + + offset = bn->length - length; + if(offset > 0) { + memmove(bn->bignum, bn->bignum + offset, length); + + _libssh2_explicit_zero(bn->bignum + length, offset); + + bignum = realloc(bn->bignum, length); + if(bignum) { + bn->bignum = bignum; + bn->length = length; + } + else { + return -1; + } + } + + return 0; +} + +int +_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); + return 0; + } + + return -1; +} + +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) +{ + /* Random from client */ + dhctx->dh_handle = NULL; + dhctx->dh_params = NULL; + dhctx->dh_privbn = NULL; +} + +void +_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) +{ + if(dhctx->dh_handle) { + BCryptDestroyKey(dhctx->dh_handle); + dhctx->dh_handle = NULL; + } + if(dhctx->dh_params) { + /* Since public dh_params are shared in clear text, + * we don't need to securely zero them out here */ + free(dhctx->dh_params); + dhctx->dh_params = NULL; + } + if(dhctx->dh_privbn) { + _libssh2_wincng_bignum_free(dhctx->dh_privbn); + dhctx->dh_privbn = NULL; + } +} + +static int +round_down(int number, int multiple) +{ + return (number / multiple) * multiple; +} + +/* Generates a Diffie-Hellman key pair using base `g', prime `p' and the given + * `group_order'. Can use the given big number context `bnctx' if needed. The + * private key is stored as opaque in the Diffie-Hellman context `*dhctx' and + * the public key is returned in `public'. 0 is returned upon success, else + * -1. */ +int +_libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, + _libssh2_bn *g, _libssh2_bn *p, int group_order) +{ + const int hasAlgDHwithKDF = _libssh2_wincng.hasAlgDHwithKDF; + + if(group_order < 0) + return -1; + + while(_libssh2_wincng.hAlgDH && hasAlgDHwithKDF != -1) { + BCRYPT_DH_PARAMETER_HEADER *dh_params; + ULONG dh_params_len; + int status; + /* Note that the DH provider requires that keys be multiples of 64 bits + * in length. At the time of writing a practical observed group_order + * value is 257, so we need to round down to 8 bytes of length (64/8) + * in order for kex to succeed */ + ULONG key_length_bytes = max((ULONG)round_down(group_order, 8), + max(g->length, p->length)); + BCRYPT_DH_KEY_BLOB *dh_key_blob; + LPCWSTR key_type; + + /* Prepare a key pair; pass the in the bit length of the key, + * but the key is not ready for consumption until it is finalized. */ + status = BCryptGenerateKeyPair(_libssh2_wincng.hAlgDH, + &dhctx->dh_handle, + key_length_bytes * 8, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + dh_params_len = (ULONG)sizeof(*dh_params) + + 2 * key_length_bytes; + dh_params = (BCRYPT_DH_PARAMETER_HEADER *)malloc(dh_params_len); + if(!dh_params) { + return -1; + } + + /* Populate DH parameters blob; after the header follows the `p` + * value and the `g` value. */ + dh_params->cbLength = dh_params_len; + dh_params->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + dh_params->cbKeyLength = key_length_bytes; + memcpy_with_be_padding((unsigned char *)dh_params + + sizeof(*dh_params), + key_length_bytes, p->bignum, p->length); + memcpy_with_be_padding((unsigned char *)dh_params + + sizeof(*dh_params) + key_length_bytes, + key_length_bytes, g->bignum, g->length); + + status = BCryptSetProperty(dhctx->dh_handle, BCRYPT_DH_PARAMETERS, + (PUCHAR)dh_params, dh_params_len, 0); + if(hasAlgDHwithKDF == -1) { + /* We know that the raw KDF is not supported, so discard this. */ + free(dh_params); + } + else { + /* Pass ownership to dhctx; these parameters will be freed when + * the context is destroyed. We need to keep the parameters more + * easily available so that we have access to the `g` value when + * _libssh2_dh_secret() is called later. */ + dhctx->dh_params = dh_params; + } + dh_params = NULL; + + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + status = BCryptFinalizeKeyPair(dhctx->dh_handle, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + key_length_bytes = 0; + if(hasAlgDHwithKDF == 1) { + /* Now we need to extract the public portion of the key so that we + * set it in the `public` bignum to satisfy our caller. + * First measure up the size of the required buffer. */ + key_type = BCRYPT_DH_PUBLIC_BLOB; + } + else { + /* We also need to extract the private portion of the key to + * set it in the `*dhctx' bignum if the raw KDF is not supported. + * First measure up the size of the required buffer. */ + key_type = BCRYPT_DH_PRIVATE_BLOB; + } + status = BCryptExportKey(dhctx->dh_handle, NULL, key_type, + NULL, 0, &key_length_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)malloc(key_length_bytes); + if(!dh_key_blob) { + return -1; + } + + status = BCryptExportKey(dhctx->dh_handle, NULL, key_type, + (PUCHAR)dh_key_blob, key_length_bytes, + &key_length_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + if(hasAlgDHwithKDF == 1) { + /* We have no private data, because raw KDF is supported */ + free(dh_key_blob); + } + else { /* we may have potentially private data, use secure free */ + _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes); + } + return -1; + } + + if(hasAlgDHwithKDF == -1) { + /* We know that the raw KDF is not supported, so discard this */ + BCryptDestroyKey(dhctx->dh_handle); + dhctx->dh_handle = NULL; + } + + /* BCRYPT_DH_PUBLIC_BLOB corresponds to a BCRYPT_DH_KEY_BLOB header + * followed by the Modulus, Generator and Public data. Those components + * each have equal size, specified by dh_key_blob->cbKey. */ + if(_libssh2_wincng_bignum_resize(public, dh_key_blob->cbKey)) { + if(hasAlgDHwithKDF == 1) { + /* We have no private data, because raw KDF is supported */ + free(dh_key_blob); + } + else { /* we may have potentially private data, use secure free */ + _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes); + } + return -1; + } + + /* Copy the public key data into the public bignum data buffer */ + memcpy(public->bignum, (unsigned char *)dh_key_blob + + sizeof(*dh_key_blob) + + 2 * dh_key_blob->cbKey, + dh_key_blob->cbKey); + + if(dh_key_blob->dwMagic == BCRYPT_DH_PRIVATE_MAGIC) { + /* BCRYPT_DH_PRIVATE_BLOB additionally contains the Private data */ + dhctx->dh_privbn = _libssh2_wincng_bignum_init(); + if(!dhctx->dh_privbn) { + _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes); + return -1; + } + if(_libssh2_wincng_bignum_resize(dhctx->dh_privbn, + dh_key_blob->cbKey)) { + _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes); + return -1; + } + + /* Copy the private key data into the dhctx bignum data buffer */ + memcpy(dhctx->dh_privbn->bignum, (unsigned char *)dh_key_blob + + sizeof(*dh_key_blob) + + 3 * dh_key_blob->cbKey, + dh_key_blob->cbKey); + + /* Make sure the private key is an odd number, because only + * odd primes can be used with the RSA-based fallback while + * DH itself does not seem to care about it being odd or not. */ + if(!(dhctx->dh_privbn->bignum[dhctx->dh_privbn->length-1] % 2)) { + _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes); + /* discard everything first, then try again */ + _libssh2_dh_dtor(dhctx); + _libssh2_dh_init(dhctx); + continue; + } + } + + _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes); + + return 0; + } + + /* Generate x and e */ + dhctx->dh_privbn = _libssh2_wincng_bignum_init(); + if(!dhctx->dh_privbn) + return -1; + if(_libssh2_wincng_bignum_rand(dhctx->dh_privbn, (group_order*8)-1, 0, -1)) + return -1; + if(_libssh2_wincng_bignum_mod_exp(public, g, dhctx->dh_privbn, p)) + return -1; + + return 0; +} + +/* Computes the Diffie-Hellman secret from the previously created context + * `*dhctx', the public key `f' from the other party and the same prime `p' + * used at context creation. The result is stored in `secret'. 0 is returned + * upon success, else -1. */ +int +_libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, + _libssh2_bn *f, _libssh2_bn *p) +{ + if(_libssh2_wincng.hAlgDH && _libssh2_wincng.hasAlgDHwithKDF != -1 && + dhctx->dh_handle && dhctx->dh_params && f) { + BCRYPT_KEY_HANDLE peer_public = NULL; + BCRYPT_SECRET_HANDLE agreement = NULL; + ULONG secret_len_bytes = 0; + NTSTATUS status; + unsigned char *start, *end; + BCRYPT_DH_KEY_BLOB *public_blob; + ULONG key_length_bytes = max(f->length, dhctx->dh_params->cbKeyLength); + ULONG public_blob_len = (ULONG)(sizeof(*public_blob) + + 3 * key_length_bytes); + + { + /* Populate a BCRYPT_DH_KEY_BLOB; after the header follows the + * Modulus, Generator and Public data. Those components must have + * equal size in this representation. */ + unsigned char *dest; + unsigned char *src; + + public_blob = (BCRYPT_DH_KEY_BLOB *)malloc(public_blob_len); + if(!public_blob) { + return -1; + } + public_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + public_blob->cbKey = key_length_bytes; + + dest = (unsigned char *)(public_blob + 1); + src = (unsigned char *)(dhctx->dh_params + 1); + + /* Modulus (the p-value from the first call) */ + memcpy_with_be_padding(dest, key_length_bytes, src, + dhctx->dh_params->cbKeyLength); + /* Generator (the g-value from the first call) */ + memcpy_with_be_padding(dest + key_length_bytes, key_length_bytes, + src + dhctx->dh_params->cbKeyLength, + dhctx->dh_params->cbKeyLength); + /* Public from the peer */ + memcpy_with_be_padding(dest + 2*key_length_bytes, key_length_bytes, + f->bignum, f->length); + } + + /* Import the peer public key information */ + status = BCryptImportKeyPair(_libssh2_wincng.hAlgDH, NULL, + BCRYPT_DH_PUBLIC_BLOB, &peer_public, + (PUCHAR)public_blob, public_blob_len, 0); + if(!BCRYPT_SUCCESS(status)) { + goto out; + } + + /* Set up a handle that we can use to establish the shared secret + * between ourselves (our saved dh_handle) and the peer. */ + status = BCryptSecretAgreement(dhctx->dh_handle, peer_public, + &agreement, 0); + if(!BCRYPT_SUCCESS(status)) { + goto out; + } + + /* Compute the size of the buffer that is needed to hold the derived + * shared secret. */ + status = BCryptDeriveKey(agreement, BCRYPT_KDF_RAW_SECRET, NULL, NULL, + 0, &secret_len_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + if(status == STATUS_NOT_SUPPORTED) { + _libssh2_wincng.hasAlgDHwithKDF = -1; + } + goto out; + } + + /* Expand the secret bignum to be ready to receive the derived secret + * */ + if(_libssh2_wincng_bignum_resize(secret, secret_len_bytes)) { + status = (NTSTATUS)STATUS_NO_MEMORY; + goto out; + } + + /* And populate the secret bignum */ + status = BCryptDeriveKey(agreement, BCRYPT_KDF_RAW_SECRET, NULL, + secret->bignum, secret_len_bytes, + &secret_len_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + if(status == STATUS_NOT_SUPPORTED) { + _libssh2_wincng.hasAlgDHwithKDF = -1; + } + goto out; + } + + /* Counter to all the other data in the BCrypt APIs, the raw secret is + * returned to us in host byte order, so we need to swap it to big + * endian order. */ + start = secret->bignum; + end = secret->bignum + secret->length - 1; + while(start < end) { + unsigned char tmp = *end; + *end = *start; + *start = tmp; + start++; + end--; + } + + status = 0; + _libssh2_wincng.hasAlgDHwithKDF = 1; + +out: + if(peer_public) { + BCryptDestroyKey(peer_public); + } + if(agreement) { + BCryptDestroySecret(agreement); + } + + free(public_blob); + + if(status == STATUS_NOT_SUPPORTED && + _libssh2_wincng.hasAlgDHwithKDF == -1) { + goto fb; /* fallback to RSA-based implementation */ + } + return BCRYPT_SUCCESS(status) ? 0 : -1; + } + +fb: + /* Compute the shared secret */ + return _libssh2_wincng_bignum_mod_exp(secret, f, dhctx->dh_privbn, p); +} + +/* _libssh2_supported_key_sign_algorithms + * + * Return supported key hash algo upgrades, see crypto.h + * + */ + +const char * +_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session, + unsigned char *key_method, + size_t key_method_len) +{ + (void)session; + +#if LIBSSH2_RSA_SHA2 + if(key_method_len == 7 && + memcmp(key_method, "ssh-rsa", key_method_len) == 0) { + return "rsa-sha2-512,rsa-sha2-256" +#if LIBSSH2_RSA_SHA1 + ",ssh-rsa" +#endif + ; + } +#else + (void)key_method; + (void)key_method_len; +#endif + + return NULL; +} + +#endif /* LIBSSH2_CRYPTO_C */ |