Add CNG KEX support routine

CNG KEX support for Group1, Group14 and Group Exchange .
This commit is contained in:
dkulwin 2016-01-08 13:40:55 -06:00
parent 67c7a3c003
commit 02e784565a
8 changed files with 1485 additions and 0 deletions

View File

@ -159,6 +159,11 @@
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tncon.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kex.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexdhc.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexdhs.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexgexc.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kexgexs.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ansiprsr.h" />
@ -175,6 +180,7 @@
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tncon.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\cng_kex.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -81,6 +81,21 @@
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\win32compat\cng_kex.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\win32compat\cng_kexdhc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\win32compat\cng_kexdhs.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\win32compat\cng_kexgexc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\win32compat\cng_kexgexs.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ansiprsr.h">
@ -125,5 +140,8 @@
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\win32auth.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\win32compat\cng_kex.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,471 @@
#include <cng_kex.h>
#include <dh.h>
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);
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <windows.h>
#include <bcrypt.h>
#include <openssl/bn.h>
#include <VersionHelpers.h>
// 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);

View File

@ -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 <sys/types.h>
#include <openssl/dh.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#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 <bcrypt.h>
#include <cng_kex.h>
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;
}

View File

@ -0,0 +1,198 @@
#include "includes.h"
#ifdef WIN32_FIXME
#undef GSSAPI
#undef KRB5
#endif
#ifdef WITH_OPENSSL
#include <sys/types.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <openssl/dh.h>
#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 <bcrypt.h>
#include <cng_kex.h>
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 */

View File

@ -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 <sys/param.h>
#include <sys/types.h>
#include <openssl/dh.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#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 <bcrypt.h>
#include <cng_kex.h>
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 */

View File

@ -0,0 +1,283 @@
#include "includes.h"
#ifdef WIN32_FIXME
#undef GSSAPI
#undef KRB5
#endif
#ifdef WITH_OPENSSL
#include <sys/param.h> /* MIN MAX */
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <openssl/dh.h>
#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 <cng_kex.h>
#include <pathnames.h>
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 */