...
3 #include "GateKeeper.h"
4 #include "pEpCOMServerAdapter.h"
5 #include "utf8_helper.h"
9 // https://gist.github.com/mcdurdin/5626617
11 struct PUBLIC_KEY_VALUES {
12 BLOBHEADER blobheader;
17 static void ReverseMemCopy(
19 _In_ BYTE const *pbSource,
23 for (DWORD i = 0; i < cb; i++) {
24 pbDest[cb - 1 - i] = pbSource[i];
28 static NTSTATUS ImportRsaPublicKey(
29 _In_ BCRYPT_ALG_HANDLE hAlg, // CNG provider
30 _In_ PUBLIC_KEY_VALUES *pKey, // Pointer to the RSAPUBKEY blob.
31 _In_ BCRYPT_KEY_HANDLE *phKey // Receives a handle the imported public key.
36 BYTE *pbPublicKey = NULL;
39 // Layout of the RSA public key blob:
41 // +----------------------------------------------------------------+
42 // | BCRYPT_RSAKEY_BLOB | BE( dwExp ) | BE( Modulus ) |
43 // +----------------------------------------------------------------+
45 // sizeof(BCRYPT_RSAKEY_BLOB) cbExp cbModulus
46 // <--------------------------><------------><---------------------->
48 // BE = Big Endian Format
50 DWORD cbModulus = (pKey->rsapubkey.bitlen + 7) / 8;
51 DWORD dwExp = pKey->rsapubkey.pubexp;
52 DWORD cbExp = (dwExp & 0xFF000000) ? 4 :
53 (dwExp & 0x00FF0000) ? 3 :
54 (dwExp & 0x0000FF00) ? 2 : 1;
56 BCRYPT_RSAKEY_BLOB *pRsaBlob;
59 if (!SUCCEEDED(hr = DWordAdd(cbModulus, sizeof(BCRYPT_RSAKEY_BLOB), &cbKey))) {
65 pbPublicKey = (PBYTE) CoTaskMemAlloc(cbKey);
66 if (pbPublicKey == NULL) {
71 ZeroMemory(pbPublicKey, cbKey);
72 pRsaBlob = (BCRYPT_RSAKEY_BLOB *) (pbPublicKey);
75 // Make the Public Key Blob Header
78 pRsaBlob->Magic = BCRYPT_RSAPUBLIC_MAGIC;
79 pRsaBlob->BitLength = pKey->rsapubkey.bitlen;
80 pRsaBlob->cbPublicExp = cbExp;
81 pRsaBlob->cbModulus = cbModulus;
82 pRsaBlob->cbPrime1 = 0;
83 pRsaBlob->cbPrime2 = 0;
85 pbCurrent = (PBYTE) (pRsaBlob + 1);
88 // Copy pubExp Big Endian
91 ReverseMemCopy(pbCurrent, (PBYTE) &dwExp, cbExp);
95 // Copy Modulus Big Endian
98 ReverseMemCopy(pbCurrent, pKey->modulus, cbModulus);
101 // Import the public key
104 hr = BCryptImportKeyPair(hAlg, NULL, BCRYPT_RSAPUBLIC_BLOB, phKey, (PUCHAR) pbPublicKey, cbKey, 0);
107 CoTaskMemFree(pbPublicKey);
113 const LPCTSTR GateKeeper::plugin_reg_path = _T("Software\\Microsoft\\Office\\Outlook\\Addins\\pEp");
114 const LPCTSTR GateKeeper::plugin_reg_value_name = _T("LoadBehavior");
115 const LPCTSTR GateKeeper::updater_reg_path = _T("Software\\pEp\\Updater";)
117 const time_t GateKeeper::cycle = 7200; // 7200 sec is 2 h
118 const DWORD GateKeeper::waiting = 10000; // 10000 ms is 10 sec
120 GateKeeper::GateKeeper(CpEpCOMServerAdapterModule * self)
121 : _self(self), now(time(NULL)), next(now + time_diff()), hkUpdater(NULL), internet(NULL), hAES(NULL), hRSA(NULL)
123 LONG lResult = RegOpenCurrentUser(KEY_READ, &cu);
124 assert(lResult == ERROR_SUCCESS);
125 if (lResult == ERROR_SUCCESS)
131 LONG lResult = RegOpenKeyEx(cu, updater_reg_path, 0, KEY_READ, &hkUpdater);
132 assert(lResult == ERROR_SUCCESS);
133 if (lResult != ERROR_SUCCESS)
138 GateKeeper::~GateKeeper()
142 RegCloseKey(hkUpdater);
147 time_t GateKeeper::time_diff()
150 static random_device rd;
151 static mt19937 gen(rd());
153 uniform_int_distribution<time_t> dist(0, cycle);
163 void GateKeeper::keep()
175 next = now + GateKeeper::cycle;
183 void GateKeeper::keep_plugin()
185 while (!_self->m_bComInitialized)
188 //MessageBox(NULL, _T("test"), _T("keep_plugin"), MB_ICONINFORMATION | MB_TOPMOST);
193 LONG lResult = RegGetValue(cu, plugin_reg_path, plugin_reg_value_name, RRF_RT_REG_DWORD, NULL, &value, &size);
194 if (lResult != ERROR_SUCCESS)
198 lResult = RegSetValue(cu, plugin_reg_path, RRF_RT_REG_DWORD, plugin_reg_value_name, 3);
199 assert(lResult == ERROR_SUCCESS);
203 string GateKeeper::update_key()
207 if (key.length() == 0) {
208 HRSRC res = FindResource(_self->hModule(), MAKEINTRESOURCE(IRD_UPDATEKEY), RT_RCDATA);
211 throw runtime_error("FindResource: IRD_UPDATEKEY");
213 HGLOBAL hRes = LoadResource(_self->hModule(), res);
216 throw runtime_error("LoadResource: IRD_UPDATEKEY");
218 key = string((char *)LockResource(hRes), SizeofResource(_self->hModule(), res));
219 UnlockResource(hRes);
225 BCRYPT_KEY_HANDLE GateKeeper::delivery_key()
229 static random_device rd;
230 static mt19937 gen(rd());
232 uniform_int_distribution<int64_t> dist(0, UINT32_MAX);
234 for (int i = 0; i < 8; i++)
235 key.dw_key[i] = (uint32_t) dist(gen);
237 BCRYPT_KEY_HANDLE hKey;
238 NTSTATUS status = BCryptGenerateSymmetricKey(hAES, &hKey, NULL, 0, (PUCHAR) &key, (ULONG) sizeof(aeskey_t), 0);
241 throw runtime_error("BCryptGenerateSymmetricKey");
246 string GateKeeper::wrapped_delivery_key(BCRYPT_KEY_HANDLE hDeliveryKey)
250 BCRYPT_KEY_HANDLE hUpdateKey;
251 string _update_key = update_key();
253 PCERT_PUBLIC_KEY_INFO uk;
255 BOOL bResult = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
256 (const BYTE *) _update_key.data(), _update_key.size(), CRYPT_DECODE_ALLOC_FLAG, NULL, &uk, &uk_size);
258 throw runtime_error("CryptDecodeObjectEx: X509_PUBLIC_KEY_INFO");
260 PUBLIC_KEY_VALUES *_uk;
262 bResult = CryptDecodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB,
263 uk->PublicKey.pbData, uk->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &_uk, &_uk_size);
266 throw runtime_error("CryptDecodeObjectEx: X509_PUBLIC_KEY_INFO");
268 HRESULT hResult = ImportRsaPublicKey(hRSA, _uk, &hUpdateKey);
271 throw runtime_error("ImportRsaPublicKey");
273 aeskey_t _delivery_key;
275 NTSTATUS status = BCryptExportKey(hDeliveryKey, NULL, BCRYPT_KEY_DATA_BLOB, (PUCHAR) &_delivery_key, sizeof(aeskey_t),
278 throw runtime_error("BCryptExportKey: delivery_key");
280 static random_device rd;
281 static mt19937 gen(rd());
282 uniform_int_distribution<int64_t> dist(0, UINT32_MAX);
284 for (int i = 0; i < 64; i++)
285 r[i] = (uint32_t) dist(gen);
287 BCRYPT_OAEP_PADDING_INFO pi;
288 memset(&pi, 0, sizeof(BCRYPT_OAEP_PADDING_INFO));
289 pi.pszAlgId = BCRYPT_SHA256_ALGORITHM;
290 pi.pbLabel = (PUCHAR) r;
291 pi.cbLabel = sizeof(r);
294 PUCHAR _result = NULL;
295 status = BCryptEncrypt(hUpdateKey, (PUCHAR) &_delivery_key, sizeof(aeskey_t), &pi, NULL, 0, NULL, 0, &result_size, BCRYPT_PAD_OAEP);
297 BCryptDestroyKey(hUpdateKey);
298 throw runtime_error("BCryptEncrypt: calculating result size");
301 _result = new UCHAR[result_size];
302 status = BCryptEncrypt(hUpdateKey, (PUCHAR) &_delivery_key, sizeof(aeskey_t), &pi, NULL, 0, _result, result_size, &copied, BCRYPT_PAD_OAEP);
304 BCryptDestroyKey(hUpdateKey);
306 throw runtime_error("BCryptEncrypt: encrypting using update_key");
309 BCryptDestroyKey(hUpdateKey);
312 s << hex << setw(2) << setfill('0');
313 for (ULONG i = 0; i < copied; i++)
314 s << (int) _result[i];
321 GateKeeper::product_list& GateKeeper::registered_products()
323 static product_list products;
325 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
326 static TCHAR value_name[16384];
327 DWORD value_name_size;
328 static TCHAR value[L_MAX_URL_LENGTH + 1];
333 LONG lResult = ERROR_SUCCESS;
334 for (DWORD i = 0; lResult == ERROR_SUCCESS; i++) {
335 value_size = L_MAX_URL_LENGTH + 1;
336 lResult = RegEnumValue(hkUpdater, 0, value_name, &value_name_size, NULL, NULL, (LPBYTE) value, &value_size);
337 if (lResult == ERROR_SUCCESS)
338 products.push_back({ value_name, value });
344 void GateKeeper::update_product(product p, DWORD context)
346 string delivery = wrapped_delivery_key(delivery_key());
347 tstring url = p.second;
348 url += _T("&challenge=");
350 url += utility::utf16_string(delivery);
355 HINTERNET hUrl = InternetOpenUrl(internet, url.c_str(), headers.c_str(), headers.length(),
356 INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
361 PCTSTR rgpszAcceptTypes[] = { _T("text/plain"), NULL };
362 HINTERNET hRequest = HttpOpenRequest(hUrl, NULL, _T("challenge"), NULL, NULL, rgpszAcceptTypes, INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
365 InternetCloseHandle(hUrl);
368 void GateKeeper::keep_updated()
370 return; // disabled for now
372 NTSTATUS status = BCryptOpenAlgorithmProvider(&hAES, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
377 status = BCryptOpenAlgorithmProvider(&hRSA, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
382 internet = InternetOpen(_T("pEp"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
386 product_list& products = registered_products();
388 for (auto i = products.begin(); i != products.end(); i++) {
389 update_product(*i, context++);
394 InternetCloseHandle(internet);
396 BCryptCloseAlgorithmProvider(hAES, 0);
398 BCryptCloseAlgorithmProvider(hRSA, 0);