diff --git a/contrib/win32/openssh/win32compat.vcxproj b/contrib/win32/openssh/win32compat.vcxproj
index 77bf815..11ac11a 100644
--- a/contrib/win32/openssh/win32compat.vcxproj
+++ b/contrib/win32/openssh/win32compat.vcxproj
@@ -159,6 +159,11 @@
+
+
+
+
+
@@ -175,6 +180,7 @@
+
diff --git a/contrib/win32/openssh/win32compat.vcxproj.filters b/contrib/win32/openssh/win32compat.vcxproj.filters
index 4c3321a..6b28eaa 100644
--- a/contrib/win32/openssh/win32compat.vcxproj.filters
+++ b/contrib/win32/openssh/win32compat.vcxproj.filters
@@ -81,6 +81,21 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -125,5 +140,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/contrib/win32/win32compat/cng_kex.c b/contrib/win32/win32compat/cng_kex.c
new file mode 100644
index 0000000..6661455
--- /dev/null
+++ b/contrib/win32/win32compat/cng_kex.c
@@ -0,0 +1,471 @@
+
+#include
+#include
+
+typedef struct dh_st DH;
+
+char *strdelim(char **);
+char *strsep(char **stringp, const char *delim);
+// Prime in big-endian format. (Group 2 prime from RFC 2409)
+static
+const
+BYTE OakleyGroup2P[] =
+{
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F,
+ 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B,
+ 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67,
+ 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95,
+ 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51,
+ 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF,
+ 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB,
+ 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B,
+ 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+// Generator in big-endian format. (Group 2 generator from RFC 2409)
+static
+const
+BYTE OakleyGroup2G[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
+};
+
+
+// Prime in big-endian format. (Group 14 prime from RFC 3526)
+static
+const
+BYTE OakleyGroup14P[] =
+{
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+// Generator in big-endian format. (Group 14 generator from RFC 3526)
+static
+const
+BYTE OakleyGroup14G[] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+};
+
+
+
+DH *
+cng_dh_new_group_asc(const char *gen, const char *modulus)
+{
+
+ DH *dh;
+ BIGNUM * p = NULL;
+ BIGNUM * g = NULL;
+
+ if (BN_hex2bn(&p, modulus) == 0 ||
+ BN_hex2bn(&g, gen) == 0) {
+ return NULL;
+ }
+ dh = cng_dh_new_group(g, p);
+ BN_free(p);
+ BN_free(g);
+ return dh;
+
+}
+
+
+
+int cng_kex_supported(void)
+{
+ return IsWindows8Point1OrGreater() == FALSE ? 0 : 1;
+}
+
+DH * cng_setup_group1(void)
+{
+ PBYTE pBlob = NULL;
+ DWORD keysize = sizeof(OakleyGroup2P);
+ DWORD cbBlob = 0;
+
+ pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
+ memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
+
+ CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
+
+ pCtx->size = keysize;
+ pCtx->p = pBlob + sizeof(CNG_DH_CTX);
+ pCtx->g = pCtx->p + keysize;
+ pCtx->pub = pCtx->g + keysize;
+
+ memcpy(pCtx->p, OakleyGroup2P, sizeof(OakleyGroup2P));
+ memcpy(pCtx->g, OakleyGroup2G, sizeof(OakleyGroup2G));
+
+ return (DH *)pCtx;
+}
+
+DH * cng_setup_group14(void)
+{
+ PBYTE pBlob = NULL;
+ DWORD keysize = sizeof(OakleyGroup14P);
+ DWORD cbBlob = 0;
+
+ pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
+ memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
+
+ CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
+
+ pCtx->size = keysize;
+ pCtx->p = pBlob + sizeof(CNG_DH_CTX);
+ pCtx->g = pCtx->p + keysize;
+ pCtx->pub = pCtx->g + keysize;
+
+ memcpy(pCtx->p, OakleyGroup14P, sizeof(OakleyGroup14P));
+ memcpy(pCtx->g, OakleyGroup14G, sizeof(OakleyGroup14G));
+
+ return (DH *)pCtx;
+}
+
+
+
+//function to reverse the order of a byte array
+static void
+cng_dh_swap_bytes(void *pv, size_t n)
+{
+ char *p = (char*)pv;
+ size_t lo, hi;
+ for (lo = 0, hi = n - 1; hi>lo; lo++, hi--)
+ {
+ char tmp = p[lo];
+ p[lo] = p[hi];
+ p[hi] = tmp;
+ }
+}
+
+
+// import a bignum public key
+BCRYPT_KEY_HANDLE
+cng_dh_set_remote_pub_key(DH * dh, BIGNUM * b)
+{
+ BCRYPT_KEY_HANDLE hPub = NULL;
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+
+ if (BN_num_bytes(b) > pCtx->size)
+ return NULL;
+
+ DWORD cbBlob = sizeof(BCRYPT_DH_KEY_BLOB) + (pCtx->size * 3);
+
+ PBYTE pBlob = (PBYTE)malloc(cbBlob);
+
+ BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
+ pKeyBlob->cbKey = pCtx->size;
+ pKeyBlob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC;
+ PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
+ PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
+ PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
+
+ memcpy(pModulus, pCtx->p, pCtx->size);
+ memcpy(pGenerator, pCtx->g, pCtx->size);
+ memset(pPublic, 0, pCtx->size);
+
+ BN_bn2bin(b, pPublic + pCtx->size - BN_num_bytes(b));
+
+
+ HRESULT Status = 0;
+
+ if (S_OK != (Status = BCryptImportKeyPair(pCtx->hAlg, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPub, pBlob, cbBlob, 0)))
+ goto cleanup;
+
+cleanup:
+ if (pBlob) free(pBlob);
+
+ return hPub;
+}
+
+
+// generate KEX private/public keys for this side
+int
+cng_dh_gen_key(DH *dh, int need)
+{
+ HRESULT Status;
+ BCRYPT_DH_PARAMETER_HEADER *DhParamHdrPointer = NULL;
+ DWORD DhParamBlobLength = 0;
+ PBYTE DhParamBlob = NULL;
+ BCRYPT_ALG_HANDLE hAlg = NULL;
+ PBYTE pBlob = NULL;
+ BCRYPT_KEY_HANDLE hPriv = NULL;
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+ DWORD cbBlob = 0;
+
+
+ DhParamBlobLength = sizeof(BCRYPT_DH_PARAMETER_HEADER) + (pCtx->size * 2);
+
+ if (NULL == (DhParamBlob = (PBYTE)malloc(DhParamBlobLength)))
+ return -1;
+
+ DhParamHdrPointer = (BCRYPT_DH_PARAMETER_HEADER *)DhParamBlob;
+ DhParamHdrPointer->cbLength = DhParamBlobLength;
+ DhParamHdrPointer->cbKeyLength = pCtx->size;
+ DhParamHdrPointer->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
+
+ memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER), pCtx->p, pCtx->size);
+ memcpy(DhParamBlob + sizeof(BCRYPT_DH_PARAMETER_HEADER) + pCtx->size, pCtx->g, pCtx->size);
+
+ if (S_OK != (Status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DH_ALGORITHM, NULL, 0)))
+ goto error;
+
+
+ if (S_OK != (Status = BCryptGenerateKeyPair(hAlg, &hPriv, pCtx->size * 8, 0)))
+ goto error;
+
+
+ if (S_OK != (Status = BCryptSetProperty(hPriv, BCRYPT_DH_PARAMETERS, DhParamBlob, DhParamBlobLength, 0)))
+ goto error;
+
+ if (S_OK != (Status = BCryptFinalizeKeyPair(hPriv, 0)))
+ goto error;
+
+ if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &cbBlob, 0)))
+ goto error;
+
+ if (NULL == (pBlob = (PBYTE)malloc(cbBlob)))
+ {
+ Status = STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ if (S_OK != (Status = BCryptExportKey(hPriv, NULL, BCRYPT_DH_PUBLIC_BLOB, pBlob, cbBlob, &cbBlob, 0)))
+ goto error;
+
+
+
+ BCRYPT_DH_KEY_BLOB *pKeyBlob = (BCRYPT_DH_KEY_BLOB *)pBlob;
+ PBYTE pModulus = pBlob + sizeof(BCRYPT_DH_KEY_BLOB);
+ PBYTE pGenerator = pModulus + pKeyBlob->cbKey;
+ PBYTE pPublic = pGenerator + pKeyBlob->cbKey;
+
+ pCtx->hAlg = hAlg;
+ pCtx->hPrivate = hPriv;
+ memcpy(pCtx->pub, pPublic, pCtx->size);
+
+ return 0;
+error:
+
+ return -1;
+}
+
+
+
+BIGNUM *
+cng_dh_export_secret(DH * dh, BCRYPT_SECRET_HANDLE hSecret)
+{
+ HRESULT hr = S_OK;
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+ PBYTE pSecret = (PBYTE)malloc(pCtx->size);
+ DWORD pSecret_len = pCtx->size;
+ DWORD size = pCtx->size;
+ BIGNUM * bn = NULL;
+
+ hr = BCryptDeriveKey(hSecret, L"TRUNCATE", NULL, pSecret, pSecret_len, &size, 0);
+ if (S_OK == hr)
+ {
+ cng_dh_swap_bytes(pSecret, size);
+ bn = BN_bin2bn(pSecret, size, NULL);
+ memset(pSecret, 0, size);
+ free(pSecret);
+ }
+ return bn;
+}
+
+// calculate and return secret as a bignum
+BIGNUM *
+cng_dh_get_secret(DH * dh, BIGNUM * remote_pub)
+{
+ HRESULT hr = S_OK;
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+ PBYTE pSecret = (PBYTE)malloc(pCtx->size);
+ DWORD pSecret_len = pCtx->size;
+ DWORD size = pCtx->size;
+ BIGNUM * bn = NULL;
+ BCRYPT_SECRET_HANDLE hSecret;
+
+ BCRYPT_KEY_HANDLE hRemotePub = cng_dh_set_remote_pub_key(dh, remote_pub);
+ if (hRemotePub != NULL)
+ {
+
+ if (S_OK == (hr = BCryptSecretAgreement(pCtx->hPrivate, hRemotePub, &hSecret, 0)))
+ {
+
+ hr = BCryptDeriveKey(hSecret, L"TRUNCATE", NULL, pSecret, pSecret_len, &size, 0);
+ if (S_OK == hr)
+ {
+ cng_dh_swap_bytes(pSecret, size);
+ bn = BN_bin2bn(pSecret, size, NULL);
+ memset(pSecret, 0, size);
+ free(pSecret);
+ }
+ BCryptDestroySecret(hSecret);
+ }
+ }
+ return bn;
+}
+
+
+// return our public key as a bignum (for sending to the other side)
+BIGNUM *
+cng_dh_get_local_pub_key(DH * dh)
+{
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+ BIGNUM * bn = NULL;
+
+ bn = BN_bin2bn(pCtx->pub, pCtx->size, NULL);
+
+ return bn;
+}
+
+BIGNUM *
+cng_dh_get_p(DH * dh)
+{
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+ BIGNUM * bn = NULL;
+
+ bn = BN_bin2bn(pCtx->p, pCtx->size, NULL);
+ return bn;
+}
+
+BIGNUM *
+cng_dh_get_g(DH * dh)
+{
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)dh;
+ BIGNUM * bn = NULL;
+
+ bn = BN_bin2bn(pCtx->g, pCtx->size, NULL);
+ return bn;
+}
+
+
+
+int
+cng_dh_pub_is_valid(DH * dh, BIGNUM *dh_pub)
+{
+ int i;
+ int n = BN_num_bits(dh_pub);
+ int bits_set = 0;
+ BIGNUM *tmp;
+ BIGNUM *p = cng_dh_get_p(dh);
+
+ if (NULL == p) {
+ logit("invalid DH prime value");
+ return 0;
+ }
+
+
+ if (dh_pub->neg) {
+ logit("invalid public DH value: negative");
+ return 0;
+ }
+ if (BN_cmp(dh_pub, BN_value_one()) != 1) { /* pub_exp <= 1 */
+ logit("invalid public DH value: <= 1");
+ return 0;
+ }
+
+ if ((tmp = BN_new()) == NULL) {
+ error("%s: BN_new failed", __FUNCTION__);
+ return 0;
+ }
+ if (!BN_sub(tmp, p, BN_value_one()) ||
+ BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */
+ BN_clear_free(tmp);
+ logit("invalid public DH value: >= p-1");
+ return 0;
+ }
+ BN_clear_free(tmp);
+
+ for (i = 0; i <= n; i++)
+ if (BN_is_bit_set(dh_pub, i))
+ bits_set++;
+ debug2("bits set: %d/%d", bits_set, BN_num_bits(p));
+
+ /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
+ if (bits_set > 1)
+ {
+ BN_clear_free(p);
+ return 1;
+ }
+
+ logit("invalid public DH value (%d/%d)", bits_set, BN_num_bits(p));
+ BN_clear_free(p);
+ return 0;
+}
+
+
+DH *
+cng_dh_new_group(BIGNUM *gen, BIGNUM *modulus)
+{
+
+ PBYTE pBlob = NULL;
+ DWORD keysize = BN_num_bytes(modulus);;
+ DWORD cbBlob = 0;
+
+
+ pBlob = (PBYTE)malloc(sizeof(CNG_DH_CTX) + (3 * keysize));
+ memset(pBlob, 0, sizeof(CNG_DH_CTX) + (3 * keysize));
+
+ CNG_DH_CTX * pCtx = (CNG_DH_CTX *)pBlob;
+
+ pCtx->size = keysize;
+ pCtx->p = pBlob + sizeof(CNG_DH_CTX);
+ pCtx->g = pCtx->p + keysize;
+ pCtx->pub = pCtx->g + keysize;
+
+ BN_bn2bin(gen, pCtx->g + keysize - BN_num_bytes(gen));
+ BN_bn2bin(modulus, pCtx->p + keysize - BN_num_bytes(modulus));
+
+ return (DH *)pCtx;
+}
+
+
+
+void cng_dh_free_context(DH * dh)
+{
+ CNG_DH_CTX * pCtx = (CNG_DH_CTX*)dh;
+
+ if (pCtx->hAlg)
+ BCryptCloseAlgorithmProvider(pCtx->hAlg, 0);
+
+ if (pCtx->hPrivate)
+ BCryptDestroyKey(pCtx->hPrivate);
+
+ free(pCtx);
+
+}
+
diff --git a/contrib/win32/win32compat/cng_kex.h b/contrib/win32/win32compat/cng_kex.h
new file mode 100644
index 0000000..1b97239
--- /dev/null
+++ b/contrib/win32/win32compat/cng_kex.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+// CNG Diffie-hellman Kex context
+typedef struct cng_dh_ctx {
+ int size;
+ PBYTE g;
+ PBYTE p;
+ PBYTE pub;
+ BCRYPT_KEY_HANDLE hPrivate;
+ BCRYPT_ALG_HANDLE hAlg;
+} CNG_DH_CTX;
+
+
+DH * cng_setup_group1(void);
+DH * cng_setup_group14(void);
+DH * cng_dh_new_group(BIGNUM *gen, BIGNUM *modulus);
+BCRYPT_KEY_HANDLE cng_dh_set_remote_pub_key(DH *pCtx, BIGNUM * b);
+int cng_dh_gen_key(DH *dh, int need);
+BIGNUM * cng_dh_get_secret(DH * pCtx, BIGNUM * remote_pub);
+BIGNUM * cng_dh_get_local_pub_key(DH * dh);
+BIGNUM * cng_dh_get_p(DH * dh);
+BIGNUM * cng_dh_get_g(DH * dh);
+int cng_dh_pub_is_valid(DH * dh, BIGNUM *dh_pub);
+void cng_dh_swap_bytes(void *pv, size_t n);
+int cng_kex_supported(void);
+DH * cng_dh_new_group_asc(const char *gen, const char *modulus);
+void cng_dh_free_context(DH * dh);
diff --git a/contrib/win32/win32compat/cng_kexdhc.c b/contrib/win32/win32compat/cng_kexdhc.c
new file mode 100644
index 0000000..b3ef54e
--- /dev/null
+++ b/contrib/win32/win32compat/cng_kexdhc.c
@@ -0,0 +1,195 @@
+/*
+* cng implementation of Diffie-Hellman Key Exchange for Oakley groups 2 and 14 (ssh group1 and group14)
+*
+*/
+
+#include "includes.h"
+
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "sshkey.h"
+#include "cipher.h"
+#include "digest.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#include "dispatch.h"
+#include "compat.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+
+#include
+#include
+
+
+
+
+static int cng_input_kex_dh(int, u_int32_t, void *);
+
+int
+cng_kexdh_client(struct ssh *ssh)
+{
+
+ // switch to standard openssl version if not supported
+ if (!cng_kex_supported())
+ return kexdh_client(ssh);
+
+
+ struct kex *kex = ssh->kex;
+ int r;
+ BIGNUM * pub_key;
+ /* generate and send 'e', client DH public key */
+ switch (kex->kex_type) {
+ case KEX_DH_GRP1_SHA1:
+ kex->dh = cng_setup_group1();
+ break;
+ case KEX_DH_GRP14_SHA1:
+ kex->dh = cng_setup_group14();
+ break;
+ default:
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if (kex->dh == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ debug("sending SSH2_MSG_KEXDH_INIT");
+ if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
+ (pub_key = cng_dh_get_local_pub_key(kex->dh)) == 0 ||
+ (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 ||
+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ goto out;
+#ifdef DEBUG_KEXDH
+ DHparams_print_fp(stderr, kex->dh);
+ fprintf(stderr, "pub= ");
+ BN_print_fp(stderr, kex->dh->pub_key);
+ fprintf(stderr, "\n");
+#endif
+ debug("expecting SSH2_MSG_KEXDH_REPLY");
+ ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &cng_input_kex_dh);
+ r = 0;
+out:
+ if (pub_key)
+ BN_free(pub_key);
+ return r;
+}
+
+static int
+cng_input_kex_dh(int type, u_int32_t seq, void *ctxt)
+{
+ struct ssh *ssh = ctxt;
+ struct kex *kex = ssh->kex;
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)kex->dh;
+ BIGNUM *dh_server_pub = NULL, *shared_secret = NULL,*dh_local_pub=NULL;
+ struct sshkey *server_host_key = NULL;
+ u_char *kbuf = NULL, *server_host_key_blob = NULL, *signature = NULL;
+ u_char hash[SSH_DIGEST_MAX_LENGTH];
+ size_t klen = 0, slen, sbloblen, hashlen;
+ int kout, r;
+
+ if (kex->verify_host_key == NULL) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ /* key, cert */
+ if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
+ &sbloblen)) != 0 ||
+ (r = sshkey_from_blob(server_host_key_blob, sbloblen,
+ &server_host_key)) != 0)
+ goto out;
+ if (server_host_key->type != kex->hostkey_type ||
+ (kex->hostkey_type == KEY_ECDSA &&
+ server_host_key->ecdsa_nid != kex->hostkey_nid)) {
+ r = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
+ }
+ if (kex->verify_host_key(server_host_key, ssh) == -1) {
+ r = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ }
+ /* DH parameter f, server public DH key */
+ if ((dh_server_pub = BN_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ /* signed H */
+ if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
+ (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ goto out;
+
+ if (!cng_dh_pub_is_valid(kex->dh, dh_server_pub)) {
+ sshpkt_disconnect(ssh, "bad server public DH value");
+ r = SSH_ERR_MESSAGE_INCOMPLETE;
+ goto out;
+ }
+
+ shared_secret = cng_dh_get_secret(kex->dh, dh_server_pub);
+ if (shared_secret == NULL)
+ goto out;
+
+ dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
+
+ /* calc and verify H */
+ hashlen = sizeof(hash);
+ if ((r = kex_dh_hash(
+ kex->client_version_string,
+ kex->server_version_string,
+ sshbuf_ptr(kex->my), sshbuf_len(kex->my),
+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
+ server_host_key_blob, sbloblen,
+ dh_local_pub,
+ dh_server_pub,
+ shared_secret,
+ hash, &hashlen)) != 0)
+ goto out;
+
+ if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen,
+ ssh->compat)) != 0)
+ goto out;
+
+ /* save session id */
+ if (kex->session_id == NULL) {
+ kex->session_id_len = hashlen;
+ kex->session_id = malloc(kex->session_id_len);
+ if (kex->session_id == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(kex->session_id, hash, kex->session_id_len);
+ }
+
+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
+ r = kex_send_newkeys(ssh);
+out:
+ explicit_bzero(hash, sizeof(hash));
+ cng_dh_free_context(kex->dh);
+ kex->dh = NULL;
+ if (dh_server_pub)
+ BN_clear_free(dh_server_pub);
+ if (dh_local_pub)
+ BN_clear_free(dh_local_pub);
+ if (kbuf) {
+ explicit_bzero(kbuf, klen);
+ free(kbuf);
+ }
+ if (shared_secret)
+ BN_clear_free(shared_secret);
+ sshkey_free(server_host_key);
+ free(server_host_key_blob);
+ free(signature);
+ return r;
+}
+
diff --git a/contrib/win32/win32compat/cng_kexdhs.c b/contrib/win32/win32compat/cng_kexdhs.c
new file mode 100644
index 0000000..7a1a035
--- /dev/null
+++ b/contrib/win32/win32compat/cng_kexdhs.c
@@ -0,0 +1,198 @@
+
+#include "includes.h"
+
+#ifdef WIN32_FIXME
+#undef GSSAPI
+#undef KRB5
+#endif
+
+#ifdef WITH_OPENSSL
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include "sshkey.h"
+#include "cipher.h"
+#include "digest.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+
+#include "dispatch.h"
+#include "compat.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+#include
+#include
+
+
+static int cng_input_kex_dh_init(int, u_int32_t, void *);
+
+int
+cng_kexdh_server(struct ssh *ssh)
+{
+ if (!cng_kex_supported())
+ return kexdh_server(ssh);
+
+
+ struct kex *kex = ssh->kex;
+ int r;
+
+ /* generate server DH public key */
+ switch (kex->kex_type) {
+ case KEX_DH_GRP1_SHA1:
+ kex->dh = cng_setup_group1();
+ break;
+ case KEX_DH_GRP14_SHA1:
+ kex->dh = cng_setup_group14();
+ break;
+ default:
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if (kex->dh == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
+ goto out;
+
+ debug("expecting SSH2_MSG_KEXDH_INIT");
+ ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &cng_input_kex_dh_init);
+ r = 0;
+out:
+ return r;
+}
+
+int
+cng_input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
+{
+ struct ssh *ssh = ctxt;
+ struct kex *kex = ssh->kex;
+ BIGNUM *shared_secret = NULL, *dh_client_pub = NULL, *dh_local_pub = NULL;
+ struct sshkey *server_host_public, *server_host_private;
+ u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
+ u_char hash[SSH_DIGEST_MAX_LENGTH];
+ size_t sbloblen, slen;
+ size_t klen = 0, hashlen;
+ int kout, r;
+
+ if (kex->load_host_public_key == NULL ||
+ kex->load_host_private_key == NULL) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ server_host_public = kex->load_host_public_key(kex->hostkey_type,
+ kex->hostkey_nid, ssh);
+ server_host_private = kex->load_host_private_key(kex->hostkey_type,
+ kex->hostkey_nid, ssh);
+ if (server_host_public == NULL) {
+ r = SSH_ERR_NO_HOSTKEY_LOADED;
+ goto out;
+ }
+
+ /* key, cert */
+ if ((dh_client_pub = BN_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ goto out;
+
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "dh_client_pub= ");
+ BN_print_fp(stderr, dh_client_pub);
+ fprintf(stderr, "\n");
+ debug("bits %d", BN_num_bits(dh_client_pub));
+#endif
+
+#ifdef DEBUG_KEXDH
+ DHparams_print_fp(stderr, kex->dh);
+ fprintf(stderr, "pub= ");
+ BN_print_fp(stderr, kex->dh->pub_key);
+ fprintf(stderr, "\n");
+#endif
+ if (!cng_dh_pub_is_valid(kex->dh, dh_client_pub)) {
+ sshpkt_disconnect(ssh, "bad client public DH value");
+ r = SSH_ERR_MESSAGE_INCOMPLETE;
+ goto out;
+ }
+
+ shared_secret = cng_dh_get_secret(kex->dh, dh_client_pub);
+ if (shared_secret == NULL)
+ goto out;
+
+#ifdef DEBUG_KEXDH
+ dump_digest("shared secret", kbuf, kout);
+#endif
+ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
+ &sbloblen)) != 0)
+ goto out;
+
+ dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
+
+ /* calc H */
+ hashlen = sizeof(hash);
+ if ((r = kex_dh_hash(
+ kex->client_version_string,
+ kex->server_version_string,
+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
+ sshbuf_ptr(kex->my), sshbuf_len(kex->my),
+ server_host_key_blob, sbloblen,
+ dh_client_pub,
+ dh_local_pub,
+ shared_secret,
+ hash, &hashlen)) != 0)
+ goto out;
+
+ /* save session id := H */
+ if (kex->session_id == NULL) {
+ kex->session_id_len = hashlen;
+ kex->session_id = malloc(kex->session_id_len);
+ if (kex->session_id == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(kex->session_id, hash, kex->session_id_len);
+ }
+
+ /* sign H */
+ if ((r = kex->sign(server_host_private, server_host_public,
+ &signature, &slen, hash, hashlen, ssh->compat)) < 0)
+ goto out;
+
+ /* destroy_sensitive_data(); */
+
+ /* send server hostkey, DH pubkey 'f' and singed H */
+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 ||
+ (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
+ (r = sshpkt_put_bignum2(ssh, dh_local_pub)) != 0 || /* f */
+ (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ goto out;
+
+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
+ r = kex_send_newkeys(ssh);
+out:
+ explicit_bzero(hash, sizeof(hash));
+ cng_dh_free_context(kex->dh);
+ kex->dh = NULL;
+ if (dh_client_pub)
+ BN_clear_free(dh_client_pub);
+ if (dh_local_pub)
+ BN_clear_free(dh_local_pub);
+ if (shared_secret)
+ BN_clear_free(shared_secret);
+ free(server_host_key_blob);
+ free(signature);
+ return r;
+}
+#endif /* WITH_OPENSSL */
diff --git a/contrib/win32/win32compat/cng_kexgexc.c b/contrib/win32/win32compat/cng_kexgexc.c
new file mode 100644
index 0000000..a99ac62
--- /dev/null
+++ b/contrib/win32/win32compat/cng_kexgexc.c
@@ -0,0 +1,283 @@
+/* $OpenBSD: kexgexc.c,v 1.22 2015/05/26 23:23:40 dtucker Exp $ */
+/*
+* Copyright (c) 2000 Niels Provos. All rights reserved.
+* Copyright (c) 2001 Markus Friedl. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "includes.h"
+
+#ifdef WITH_OPENSSL
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "sshkey.h"
+#include "cipher.h"
+#include "digest.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#include "compat.h"
+#include "dispatch.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+#include
+#include
+
+static int cng_input_kex_dh_gex_group(int, u_int32_t, void *);
+static int cng_input_kex_dh_gex_reply(int, u_int32_t, void *);
+
+int
+cng_kexgex_client(struct ssh *ssh)
+{
+ // switch to standard openssl version if not supported
+ if (!cng_kex_supported())
+ return kexgex_client(ssh);
+
+ struct kex *kex = ssh->kex;
+ int r;
+ u_int nbits;
+
+ nbits = dh_estimate(kex->dh_need * 8);
+
+ kex->min = DH_GRP_MIN;
+ kex->max = DH_GRP_MAX;
+ kex->nbits = nbits;
+ if (datafellows & SSH_BUG_DHGEX_LARGE)
+ kex->nbits = MIN(kex->nbits, 4096);
+ /* New GEX request */
+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 ||
+ (r = sshpkt_put_u32(ssh, kex->min)) != 0 ||
+ (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 ||
+ (r = sshpkt_put_u32(ssh, kex->max)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ goto out;
+ debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent",
+ kex->min, kex->nbits, kex->max);
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n",
+ kex->min, kex->nbits, kex->max);
+#endif
+ ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP,
+ &cng_input_kex_dh_gex_group);
+ r = 0;
+out:
+ return r;
+}
+
+static int
+cng_input_kex_dh_gex_group(int type, u_int32_t seq, void *ctxt)
+{
+ struct ssh *ssh = ctxt;
+ struct kex *kex = ssh->kex;
+ BIGNUM *p = NULL, *g = NULL;
+ int r, bits;
+ BIGNUM * pub_key = NULL;
+
+ debug("got SSH2_MSG_KEX_DH_GEX_GROUP");
+
+ if ((p = BN_new()) == NULL ||
+ (g = BN_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshpkt_get_bignum2(ssh, p)) != 0 ||
+ (r = sshpkt_get_bignum2(ssh, g)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ goto out;
+ if ((bits = BN_num_bits(p)) < 0 ||
+ (u_int)bits < kex->min || (u_int)bits > kex->max) {
+ r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
+ goto out;
+ }
+ if ((kex->dh = cng_dh_new_group(g, p)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ p = g = NULL; /* belong to kex->dh now */
+
+ /* generate and send 'e', client DH public key */
+ if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0 ||
+ (pub_key = cng_dh_get_local_pub_key(kex->dh)) == 0 ||
+ (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 ||
+ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ goto out;
+ debug("SSH2_MSG_KEX_DH_GEX_INIT sent");
+#ifdef DEBUG_KEXDH
+ DHparams_print_fp(stderr, kex->dh);
+ fprintf(stderr, "pub= ");
+ BN_print_fp(stderr, kex->dh->pub_key);
+ fprintf(stderr, "\n");
+#endif
+ ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL);
+ ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &cng_input_kex_dh_gex_reply);
+ r = 0;
+out:
+ if (p)
+ BN_clear_free(p);
+ if (g)
+ BN_clear_free(g);
+ return r;
+}
+
+static int
+cng_input_kex_dh_gex_reply(int type, u_int32_t seq, void *ctxt)
+{
+ struct ssh *ssh = ctxt;
+ struct kex *kex = ssh->kex;
+ CNG_DH_CTX *pCtx = (CNG_DH_CTX *)kex->dh;
+ BIGNUM *dh_server_pub = NULL, *shared_secret = NULL,*dh_local_pub = NULL,*p = NULL,*g = NULL;
+ struct sshkey *server_host_key = NULL;
+ u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
+ u_char hash[SSH_DIGEST_MAX_LENGTH];
+ size_t klen = 0, slen, sbloblen, hashlen;
+ int kout, r;
+
+ debug("got SSH2_MSG_KEX_DH_GEX_REPLY");
+ if (kex->verify_host_key == NULL) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ /* key, cert */
+ if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
+ &sbloblen)) != 0 ||
+ (r = sshkey_from_blob(server_host_key_blob, sbloblen,
+ &server_host_key)) != 0)
+ goto out;
+ if (server_host_key->type != kex->hostkey_type) {
+ r = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
+ }
+ if (server_host_key->type != kex->hostkey_type ||
+ (kex->hostkey_type == KEY_ECDSA &&
+ server_host_key->ecdsa_nid != kex->hostkey_nid)) {
+ r = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
+ }
+ if (kex->verify_host_key(server_host_key, ssh) == -1) {
+ r = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ }
+ /* DH parameter f, server public DH key */
+ if ((dh_server_pub = BN_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ /* signed H */
+ if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
+ (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ goto out;
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "dh_server_pub= ");
+ BN_print_fp(stderr, dh_server_pub);
+ fprintf(stderr, "\n");
+ debug("bits %d", BN_num_bits(dh_server_pub));
+#endif
+ if (!cng_dh_pub_is_valid(kex->dh, dh_server_pub)) {
+ sshpkt_disconnect(ssh, "bad server public DH value");
+ r = SSH_ERR_MESSAGE_INCOMPLETE;
+ goto out;
+ }
+
+ shared_secret = cng_dh_get_secret(kex->dh, dh_server_pub);
+ if (shared_secret == NULL)
+ goto out;
+
+ if (ssh->compat & SSH_OLD_DHGEX)
+ kex->min = kex->max = -1;
+
+ dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
+ p = cng_dh_get_p(kex->dh);
+ g = cng_dh_get_g(kex->dh);
+
+
+
+ /* calc and verify H */
+ hashlen = sizeof(hash);
+ if ((r = kexgex_hash(
+ kex->hash_alg,
+ kex->client_version_string,
+ kex->server_version_string,
+ sshbuf_ptr(kex->my), sshbuf_len(kex->my),
+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
+ server_host_key_blob, sbloblen,
+ kex->min, kex->nbits, kex->max,
+ p, g,
+ dh_local_pub,
+ dh_server_pub,
+ shared_secret,
+ hash, &hashlen)) != 0)
+ goto out;
+
+ if ((r = sshkey_verify(server_host_key, signature, slen, hash,
+ hashlen, ssh->compat)) != 0)
+ goto out;
+
+ /* save session id */
+ if (kex->session_id == NULL) {
+ kex->session_id_len = hashlen;
+ kex->session_id = malloc(kex->session_id_len);
+ if (kex->session_id == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(kex->session_id, hash, kex->session_id_len);
+ }
+
+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
+ r = kex_send_newkeys(ssh);
+out:
+ explicit_bzero(hash, sizeof(hash));
+ cng_dh_free_context(kex->dh);
+ kex->dh = NULL;
+ if (dh_server_pub)
+ BN_clear_free(dh_server_pub);
+ if (dh_local_pub)
+ BN_clear_free(dh_local_pub);
+ if (p)
+ BN_clear_free(p);
+ if (g)
+ BN_clear_free(g);
+ if (kbuf) {
+ explicit_bzero(kbuf, klen);
+ free(kbuf);
+ }
+ if (shared_secret)
+ BN_clear_free(shared_secret);
+ sshkey_free(server_host_key);
+ free(server_host_key_blob);
+ free(signature);
+ return r;
+}
+#endif /* WITH_OPENSSL */
diff --git a/contrib/win32/win32compat/cng_kexgexs.c b/contrib/win32/win32compat/cng_kexgexs.c
new file mode 100644
index 0000000..de44859
--- /dev/null
+++ b/contrib/win32/win32compat/cng_kexgexs.c
@@ -0,0 +1,283 @@
+
+#include "includes.h"
+
+#ifdef WIN32_FIXME
+#undef GSSAPI
+#undef KRB5
+#endif
+
+#ifdef WITH_OPENSSL
+
+#include /* MIN MAX */
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "sshkey.h"
+#include "cipher.h"
+#include "digest.h"
+#include "kex.h"
+#include "log.h"
+#include "packet.h"
+#include "dh.h"
+#include "ssh2.h"
+#include "compat.h"
+#include "monitor_wrap.h"
+#include "dispatch.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+#include
+#include
+
+static int cng_input_kex_dh_gex_request(int, u_int32_t, void *);
+static int cng_input_kex_dh_gex_init(int, u_int32_t, void *);
+
+/*
+ let openssl do the choosing of the group. Once it has, we
+ can grab the group parameters and create a cng dh context.
+*/
+static
+DH * cng_context_from_dh(DH * dh)
+{
+ DH *pCtx = NULL;
+
+ pCtx = cng_dh_new_group(dh->g, dh->p);
+
+ return (DH *)pCtx;
+}
+
+
+/*
+let openssl do the choosing of the group. Once it has, we
+can grab the group parameters and create a cng dh context.
+*/
+static DH *
+cng_choose_dh(int min, int wantbits, int max)
+{
+ DH *pCtx = NULL;
+
+ DH * dh = choose_dh(min, wantbits, max);
+ if (dh != NULL)
+ {
+ pCtx = cng_context_from_dh(dh);
+
+ DH_free(dh);
+ }
+ return (DH *)pCtx;
+}
+
+
+int
+cng_kexgex_server(struct ssh *ssh)
+{
+ if (!cng_kex_supported())
+ return kexgex_server(ssh);
+
+ ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST,
+ &cng_input_kex_dh_gex_request);
+ debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST");
+ return 0;
+}
+
+static int
+cng_input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt)
+{
+ struct ssh *ssh = ctxt;
+ struct kex *kex = ssh->kex;
+ int r;
+ u_int min = 0, max = 0, nbits = 0;
+ BIGNUM * p = NULL;;
+ BIGNUM * g = NULL;
+
+
+ debug("SSH2_MSG_KEX_DH_GEX_REQUEST received");
+ if ((r = sshpkt_get_u32(ssh, &min)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
+ (r = sshpkt_get_u32(ssh, &max)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ goto out;
+ kex->nbits = nbits;
+ kex->min = min;
+ kex->max = max;
+ min = MAX(DH_GRP_MIN, min);
+ max = MIN(DH_GRP_MAX, max);
+ nbits = MAX(DH_GRP_MIN, nbits);
+ nbits = MIN(DH_GRP_MAX, nbits);
+
+ if (kex->max < kex->min || kex->nbits < kex->min ||
+ kex->max < kex->nbits) {
+ r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
+ goto out;
+ }
+
+ /* Contact privileged parent */
+ kex->dh = cng_choose_dh(min, nbits, max);
+ if (kex->dh == NULL) {
+ sshpkt_disconnect(ssh, "no matching DH grp found");
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ p = cng_dh_get_p(kex->dh);
+ g = cng_dh_get_g(kex->dh);
+ debug("SSH2_MSG_KEX_DH_GEX_GROUP sent");
+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 ||
+ (r = sshpkt_put_bignum2(ssh,p )) != 0 ||
+ (r = sshpkt_put_bignum2(ssh, g)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ goto out;
+
+ /* Compute our exchange value in parallel with the client */
+ if ((r = cng_dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
+ goto out;
+
+ debug("expecting SSH2_MSG_KEX_DH_GEX_INIT");
+ ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &cng_input_kex_dh_gex_init);
+ r = 0;
+out:
+ if (p != NULL)
+ BN_free(p);
+ if (g != NULL)
+ BN_free(g);
+ return r;
+}
+
+static int
+cng_input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
+{
+ struct ssh *ssh = ctxt;
+ struct kex *kex = ssh->kex;
+ BIGNUM *shared_secret = NULL, *dh_client_pub = NULL, *dh_local_pub=NULL, *p=NULL, *g=NULL;
+ struct sshkey *server_host_public, *server_host_private;
+ u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
+ u_char hash[SSH_DIGEST_MAX_LENGTH];
+ size_t sbloblen, slen;
+ size_t klen = 0, hashlen;
+ int kout, r;
+
+ if (kex->load_host_public_key == NULL ||
+ kex->load_host_private_key == NULL) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ server_host_public = kex->load_host_public_key(kex->hostkey_type,
+ kex->hostkey_nid, ssh);
+ server_host_private = kex->load_host_private_key(kex->hostkey_type,
+ kex->hostkey_nid, ssh);
+ if (server_host_public == NULL) {
+ r = SSH_ERR_NO_HOSTKEY_LOADED;
+ goto out;
+ }
+
+ /* key, cert */
+ if ((dh_client_pub = BN_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
+ (r = sshpkt_get_end(ssh)) != 0)
+ goto out;
+
+#ifdef DEBUG_KEXDH
+ fprintf(stderr, "dh_client_pub= ");
+ BN_print_fp(stderr, dh_client_pub);
+ fprintf(stderr, "\n");
+ debug("bits %d", BN_num_bits(dh_client_pub));
+#endif
+
+#ifdef DEBUG_KEXDH
+ DHparams_print_fp(stderr, kex->dh);
+ fprintf(stderr, "pub= ");
+ BN_print_fp(stderr, kex->dh->pub_key);
+ fprintf(stderr, "\n");
+#endif
+ if (!cng_dh_pub_is_valid(kex->dh, dh_client_pub)) {
+ sshpkt_disconnect(ssh, "bad client public DH value");
+ r = SSH_ERR_MESSAGE_INCOMPLETE;
+ goto out;
+ }
+
+ shared_secret = cng_dh_get_secret(kex->dh, dh_client_pub);
+ if (shared_secret == NULL)
+ goto out;
+#ifdef DEBUG_KEXDH
+ dump_digest("shared secret", kbuf, kout);
+#endif
+ if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
+ &sbloblen)) != 0)
+ goto out;
+
+ p = cng_dh_get_p(kex->dh);
+ g = cng_dh_get_g(kex->dh);
+ dh_local_pub = cng_dh_get_local_pub_key(kex->dh);
+ /* calc H */
+ hashlen = sizeof(hash);
+ if ((r = kexgex_hash(
+ kex->hash_alg,
+ kex->client_version_string,
+ kex->server_version_string,
+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
+ sshbuf_ptr(kex->my), sshbuf_len(kex->my),
+ server_host_key_blob, sbloblen,
+ kex->min, kex->nbits, kex->max,
+ p, g,
+ dh_client_pub,
+ dh_local_pub,
+ shared_secret,
+ hash, &hashlen)) != 0)
+ goto out;
+
+ /* save session id := H */
+ if (kex->session_id == NULL) {
+ kex->session_id_len = hashlen;
+ kex->session_id = malloc(kex->session_id_len);
+ if (kex->session_id == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(kex->session_id, hash, kex->session_id_len);
+ }
+
+ /* sign H */
+ if ((r = kex->sign(server_host_private, server_host_public,
+ &signature, &slen, hash, hashlen, ssh->compat)) < 0)
+ goto out;
+
+
+
+ /* send server hostkey, DH pubkey 'f' and singed H */
+ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 ||
+ (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
+ (r = sshpkt_put_bignum2(ssh, dh_local_pub)) != 0 || /* f */
+ (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
+ (r = sshpkt_send(ssh)) != 0)
+ goto out;
+
+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
+ r = kex_send_newkeys(ssh);
+out:
+ cng_dh_free_context(kex->dh);
+ kex->dh = NULL;
+ if (dh_client_pub)
+ BN_clear_free(dh_client_pub);
+ if (dh_local_pub)
+ BN_clear_free(dh_local_pub);
+ if (p)
+ BN_clear_free(p);
+ if (g)
+ BN_clear_free(g);
+ if (kbuf) {
+ explicit_bzero(kbuf, klen);
+ free(kbuf);
+ }
+ if (shared_secret)
+ BN_clear_free(shared_secret);
+ free(server_host_key_blob);
+ free(signature);
+ return r;
+}
+#endif /* WITH_OPENSSL */