Add CNG KEX support routine
CNG KEX support for Group1, Group14 and Group Exchange .
This commit is contained in:
parent
67c7a3c003
commit
02e784565a
|
@ -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">
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
Loading…
Reference in New Issue