#ifdef LIBSSH2_CRYPTO_C
#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
#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>
#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"
#ifndef STATUS_NOT_SUPPORTED
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
#endif
#ifndef STATUS_INVALID_SIGNATURE
#define STATUS_INVALID_SIGNATURE ((NTSTATUS)0xC000A000)
#endif
#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
#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
#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
#endif
#ifndef BCRYPT_RSAPUBLIC_BLOB
#define BCRYPT_RSAPUBLIC_BLOB L"RSAPUBLICBLOB"
#endif
#ifndef BCRYPT_RSAPUBLIC_MAGIC
#define BCRYPT_RSAPUBLIC_MAGIC 0x31415352
#endif
#ifndef BCRYPT_RSAFULLPRIVATE_BLOB
#define BCRYPT_RSAFULLPRIVATE_BLOB L"RSAFULLPRIVATEBLOB"
#endif
#ifndef BCRYPT_RSAFULLPRIVATE_MAGIC
#define BCRYPT_RSAFULLPRIVATE_MAGIC 0x33415352
#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
#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);
#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 {
const char *name;
ULONG key_length;
ULONG point_length;
LPCWSTR provider[2];
ULONG public_import_magic[2];
ULONG private_import_magic[2];
} _libssh2_wincng_ecdsa_algorithm;
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 }
},
};
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;
static int
_libssh2_wincng_ecdsa_curve_type_from_name(IN const char *name,
OUT libssh2_curve_type *out_curve);
static int
_libssh2_wincng_parse_ecdsa_privatekey(OUT _libssh2_wincng_ecdsa_key **key,
IN unsigned char *privatekey,
IN size_t privatekey_len);
#endif
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);
}
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);
}
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;
}
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;
}
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
#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
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);
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
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
}
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
}
#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
#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);
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
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
}
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
}
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
#if LIBSSH2_ECDSA
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;
}
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;
}
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;
if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) {
return LIBSSH2_ERROR_INVAL;
}
*signature = NULL;
*signature_length = (size_t)
_wincng_ecdsa_algorithms[curve].point_length * 2;
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--;
}
*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;
}
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;
if(!key) {
return LIBSSH2_ERROR_INVAL;
}
if(point->x_len != point->y_len) {
return LIBSSH2_ERROR_INVAL;
}
*key = NULL;
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];
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;
}
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;
if(!key) {
return LIBSSH2_ERROR_INVAL;
}
if(q->x_len != q->y_len) {
return LIBSSH2_ERROR_INVAL;
}
*key = NULL;
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];
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;
}
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;
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;
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);
*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;
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--;
}
}
void
_libssh2_wincng_ecdsa_free(IN _libssh2_wincng_ecdsa_key *key)
{
if(!key) {
return;
}
(void)BCryptDestroyKey(key->handle);
free(key);
}
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;
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;
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;
}
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;
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;
}
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;
if(!secret) {
return LIBSSH2_ERROR_INVAL;
}
*secret = NULL;
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;
}
status = BCryptSecretAgreement(
privatekey->handle,
publickey_handle,
&agreed_secret_handle,
0);
if(!BCRYPT_SUCCESS(status)) {
result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
goto cleanup;
}
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;
}
*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;
}
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;
}
_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;
}
int
_libssh2_wincng_ecdsa_curve_type_from_name(IN const char *name,
OUT libssh2_curve_type *out_curve)
{
unsigned int curve;
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;
}
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;
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;
}
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;
}
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;
}
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;
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;
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;
}
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;
}
result = _libssh2_get_string(&data_buffer, &ignore, &ignore_len);
if(result != LIBSSH2_ERROR_NONE) {
goto cleanup;
}
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;
}
result = _libssh2_get_bignum_bytes(&data_buffer, &d, &d_len);
if(result != LIBSSH2_ERROR_NONE) {
goto cleanup;
}
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;
}
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;
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");
}
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;
result = _libssh2_match_string(&data_buffer, "none");
if(result != LIBSSH2_ERROR_NONE) {
goto cleanup;
}
result = _libssh2_match_string(&data_buffer, "none");
if(result != LIBSSH2_ERROR_NONE) {
goto cleanup;
}
result = _libssh2_match_string(&data_buffer, "");
if(result != LIBSSH2_ERROR_NONE) {
goto cleanup;
}
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;
}
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;
}
}
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;
}
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;
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;
}
signature_maxlen =
cng_signature_len / 2 + 5 +
cng_signature_len / 2 + 5;
*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_curve_type
_libssh2_wincng_ecdsa_get_curve_type(IN _libssh2_wincng_ecdsa_key *key)
{
return key->curve;
}
#endif
#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) {
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) {
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
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
}
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
}
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");
}
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;
}
_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;
bits %= 8;
if(bits == 0)
bits = 8;
bignum[0] &= (unsigned char)((1 << bits) - 1);
if(top == 0)
bignum[0] |= (unsigned char)(1 << (bits - 1));
else if(top == 1)
bignum[0] |= (unsigned char)(3 << (bits - 2));
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;
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));
}
}
void
_libssh2_dh_init(_libssh2_dh_ctx *dhctx)
{
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) {
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;
}
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;
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;
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;
}
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) {
free(dh_params);
}
else {
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) {
key_type = BCRYPT_DH_PUBLIC_BLOB;
}
else {
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) {
free(dh_key_blob);
}
else {
_libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
}
return -1;
}
if(hasAlgDHwithKDF == -1) {
BCryptDestroyKey(dhctx->dh_handle);
dhctx->dh_handle = NULL;
}
if(_libssh2_wincng_bignum_resize(public, dh_key_blob->cbKey)) {
if(hasAlgDHwithKDF == 1) {
free(dh_key_blob);
}
else {
_libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
}
return -1;
}
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) {
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;
}
memcpy(dhctx->dh_privbn->bignum, (unsigned char *)dh_key_blob +
sizeof(*dh_key_blob) +
3 * dh_key_blob->cbKey,
dh_key_blob->cbKey);
if(!(dhctx->dh_privbn->bignum[dhctx->dh_privbn->length-1] % 2)) {
_libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
_libssh2_dh_dtor(dhctx);
_libssh2_dh_init(dhctx);
continue;
}
}
_libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
return 0;
}
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;
}
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);
{
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);
memcpy_with_be_padding(dest, key_length_bytes, src,
dhctx->dh_params->cbKeyLength);
memcpy_with_be_padding(dest + key_length_bytes, key_length_bytes,
src + dhctx->dh_params->cbKeyLength,
dhctx->dh_params->cbKeyLength);
memcpy_with_be_padding(dest + 2*key_length_bytes, key_length_bytes,
f->bignum, f->length);
}
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;
}
status = BCryptSecretAgreement(dhctx->dh_handle, peer_public,
&agreement, 0);
if(!BCRYPT_SUCCESS(status)) {
goto out;
}
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;
}
if(_libssh2_wincng_bignum_resize(secret, secret_len_bytes)) {
status = (NTSTATUS)STATUS_NO_MEMORY;
goto out;
}
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;
}
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;
}
return BCRYPT_SUCCESS(status) ? 0 : -1;
}
fb:
return _libssh2_wincng_bignum_mod_exp(secret, f, dhctx->dh_privbn, p);
}
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