#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); }