GateKeeper.cpp
author Volker Birk <vb@pep-project.org>
Wed, 22 Jun 2016 13:12:26 +0200
changeset 120 5adccc3e3d3c
parent 119 7b1eac29288f
child 121 6ee2155ec27a
permissions -rw-r--r--
encrypt delivery_key
     1 #include "stdafx.h"
     2 
     3 #include "GateKeeper.h"
     4 #include "pEpCOMServerAdapter.h"
     5 
     6 using namespace std;
     7 
     8 namespace pEp {
     9 
    10     const LPCTSTR GateKeeper::plugin_reg_path = _T("Software\\Microsoft\\Office\\Outlook\\Addins\\pEp");
    11     const LPCTSTR GateKeeper::plugin_reg_value_name = _T("LoadBehavior");
    12     const LPCTSTR GateKeeper::updater_reg_path = _T("Software\\pEp\\Updater";)
    13 
    14     const time_t GateKeeper::cycle = 7200;   // 7200 sec is 2 h
    15     const DWORD GateKeeper::waiting = 10000; // 10000 ms is 10 sec
    16 
    17     GateKeeper::GateKeeper(CpEpCOMServerAdapterModule * self)
    18         : _self(self), now(time(NULL)), next(now + time_diff()), hkUpdater(NULL), internet(NULL), hAES(NULL), hRSA(NULL)
    19     {
    20         LONG lResult = RegOpenCurrentUser(KEY_READ, &cu);
    21         assert(lResult == ERROR_SUCCESS);
    22         if (lResult == ERROR_SUCCESS)
    23             cu_open = true;
    24         else
    25             cu_open = false;
    26 
    27         if (cu_open) {
    28             LONG lResult = RegOpenKeyEx(cu, updater_reg_path, 0, KEY_READ, &hkUpdater);
    29             assert(lResult == ERROR_SUCCESS);
    30             if (lResult != ERROR_SUCCESS)
    31                 return;
    32         }
    33     }
    34     
    35     GateKeeper::~GateKeeper()
    36     {
    37         if (cu_open) {
    38             if (hkUpdater)
    39                 RegCloseKey(hkUpdater);
    40             RegCloseKey(cu);
    41         }
    42     }
    43 
    44     time_t GateKeeper::time_diff()
    45     {
    46         try {
    47             static random_device rd;
    48             static mt19937 gen(rd());
    49 
    50             uniform_int_distribution<time_t> dist(0, cycle);
    51 
    52             return dist(gen);
    53         }
    54         catch (exception&) {
    55             assert(0);
    56             return 0;
    57         }
    58     }
    59 
    60     void GateKeeper::keep()
    61     {
    62         if (!cu_open)
    63             return;
    64 
    65         while (1) {
    66             keep_plugin();
    67 
    68             now = time(NULL);
    69             assert(now != -1);
    70 
    71             if (now > next) {
    72                 next = now + GateKeeper::cycle;
    73                 keep_updated();
    74             }
    75 
    76             Sleep(waiting);
    77         }
    78     }
    79 
    80     void GateKeeper::keep_plugin()
    81     {
    82         while (!_self->m_bComInitialized)
    83             Sleep(1);
    84 
    85         MessageBox(NULL, _T("test"), _T("keep_plugin"), MB_ICONINFORMATION | MB_TOPMOST);
    86 
    87         DWORD value;
    88         DWORD size;
    89 
    90         LONG lResult = RegGetValue(cu, plugin_reg_path, plugin_reg_value_name, RRF_RT_REG_DWORD, NULL, &value, &size);
    91         if (lResult != ERROR_SUCCESS)
    92             return;
    93 
    94         if (value != 3) {
    95             lResult = RegSetValue(cu, plugin_reg_path, RRF_RT_REG_DWORD, plugin_reg_value_name, 3);
    96             assert(lResult == ERROR_SUCCESS);
    97         }
    98     }
    99 
   100     string GateKeeper::update_key()
   101     {
   102         static string key;
   103 
   104         if (key.length() == 0) {
   105             HRSRC res = FindResource(_self->hModule(), MAKEINTRESOURCE(IRD_UPDATEKEY), RT_RCDATA);
   106             assert(res);
   107             if (!res)
   108                 throw runtime_error("FindResource: IRD_UPDATEKEY");
   109 
   110             HGLOBAL hRes = LoadResource(_self->hModule(), res);
   111             assert(hRes);
   112             if (!hRes)
   113                 throw runtime_error("LoadResource: IRD_UPDATEKEY");
   114 
   115             key = string((char *)LockResource(hRes), SizeofResource(_self->hModule(), res));
   116             UnlockResource(hRes);
   117         }
   118 
   119         return key;
   120     }
   121 
   122     BCRYPT_KEY_HANDLE GateKeeper::delivery_key()
   123     {
   124         aeskey_t key;
   125 
   126         static random_device rd;
   127         static mt19937 gen(rd());
   128 
   129         uniform_int_distribution<time_t> dist(0, UINT64_MAX);
   130 
   131         key.qw_key[0] = dist(gen);
   132         key.qw_key[1] = dist(gen);
   133 
   134         BCRYPT_KEY_HANDLE hKey;
   135         NTSTATUS status = BCryptGenerateSymmetricKey(hAES, &hKey, NULL, 0, (PUCHAR) &key, (ULONG) sizeof(aeskey_t), 0);
   136         assert(status == 0);
   137         if (status)
   138             throw runtime_error("BCryptGenerateSymmetricKey");
   139 
   140         return hKey;
   141     }
   142 
   143     string GateKeeper::wrapped_delivery_key(BCRYPT_KEY_HANDLE hKey)
   144     {
   145         string result;
   146 
   147         BCRYPT_KEY_HANDLE hUpdateKey;
   148         string _update_key = update_key();
   149 
   150         NTSTATUS status = BCryptImportKeyPair(hRSA, NULL, BCRYPT_RSAPUBLIC_BLOB, &hUpdateKey,
   151                 (PUCHAR) _update_key.data(), _update_key.size(), 0);
   152         if (status)
   153             throw runtime_error("BCryptImportKeyPair: update_key");
   154 
   155         aeskey_t _delivery_key;
   156         ULONG copied;
   157         status = BCryptExportKey(delivery_key(), NULL, BCRYPT_KEY_DATA_BLOB, (PUCHAR) &_delivery_key, sizeof(aeskey_t), &copied, 0);
   158         if (status)
   159             throw runtime_error("BCryptExportKey: delivery_key");
   160 
   161         static random_device rd;
   162         static mt19937 gen(rd());
   163         uniform_int_distribution<time_t> dist(0, UINT64_MAX);
   164         uint64_t r[32];
   165         for (int i = 0; i < 32; i++)
   166             r[i] = dist(gen);
   167 
   168         BCRYPT_OAEP_PADDING_INFO pi;
   169         memset(&pi, 0, sizeof(BCRYPT_OAEP_PADDING_INFO));
   170         pi.pszAlgId = BCRYPT_SHA256_ALGORITHM;
   171         pi.pbLabel = (PUCHAR) r;
   172         pi.cbLabel = sizeof(r);
   173 
   174         ULONG result_size;
   175         PUCHAR _result = NULL;
   176         status = BCryptEncrypt(hUpdateKey, (PUCHAR) &_delivery_key, sizeof(aeskey_t), &pi, NULL, 0, NULL, 0, &result_size, BCRYPT_PAD_OAEP);
   177         if (status) {
   178             BCryptDestroyKey(hUpdateKey);
   179             throw runtime_error("BCryptEncrypt: calculating result size");
   180         }
   181 
   182         _result = new UCHAR[result_size];
   183         status = BCryptEncrypt(hUpdateKey, (PUCHAR) &_delivery_key, sizeof(aeskey_t), &pi, NULL, 0, _result, result_size, &copied, BCRYPT_PAD_OAEP);
   184         if (status) {
   185             BCryptDestroyKey(hUpdateKey);
   186             delete[] _result;
   187             throw runtime_error("BCryptEncrypt: encrypting using update_key");
   188         }
   189 
   190         BCryptDestroyKey(hUpdateKey);
   191 
   192         stringstream s;
   193         s << hex << setw(2) << setfill('0');
   194         for (ULONG i = 0; i < copied; i++)
   195             s << (int) _result[i];
   196         delete[] _result;
   197         s >> result;
   198 
   199         return result;
   200     }
   201 
   202     GateKeeper::product_list& GateKeeper::registered_products()
   203     {
   204         static product_list products;
   205 
   206         // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
   207         static TCHAR value_name[16384];
   208         DWORD value_name_size;
   209         static TCHAR value[L_MAX_URL_LENGTH + 1];
   210         DWORD value_size;
   211 
   212         products.empty();
   213 
   214         LONG lResult = ERROR_SUCCESS;
   215         for (DWORD i = 0; lResult == ERROR_SUCCESS; i++) {
   216             value_size = L_MAX_URL_LENGTH + 1;
   217             lResult = RegEnumValue(hkUpdater, 0, value_name, &value_name_size, NULL, NULL, (LPBYTE) value, &value_size);
   218             if (lResult == ERROR_SUCCESS)
   219                 products.push_back({ value_name, value });
   220         }
   221 
   222         return products;
   223     }
   224 
   225     void GateKeeper::update_product(product p, DWORD context)
   226     {
   227         HINTERNET hUrl = InternetOpenUrl(internet, p.second.c_str(), NULL, 0,
   228                 INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
   229         if (hUrl == NULL)
   230             return;
   231 
   232         // update
   233         PCTSTR rgpszAcceptTypes[] = { _T("text/plain"), NULL };
   234         HINTERNET hRequest = HttpOpenRequest(hUrl, NULL, _T("challenge"), NULL, NULL, rgpszAcceptTypes, INTERNET_FLAG_NO_UI | INTERNET_FLAG_SECURE, context);
   235 
   236 
   237         InternetCloseHandle(hUrl);
   238     }
   239 
   240     void GateKeeper::keep_updated()
   241     {
   242         return; // disabled for now
   243 
   244         NTSTATUS status = BCryptOpenAlgorithmProvider(&hAES, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
   245         assert(status == 0);
   246         if (status)
   247             goto closing;
   248 
   249         status = BCryptOpenAlgorithmProvider(&hRSA, BCRYPT_RSA_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
   250         assert(status == 0);
   251         if (status)
   252             goto closing;
   253 
   254         internet = InternetOpen(_T("pEp"), INTERNET_OPEN_TYPE_PROXY, NULL, NULL, 0);
   255         if (!internet)
   256             goto closing;
   257 
   258         product_list& products = registered_products();
   259         DWORD context = 0;
   260         for (auto i = products.begin(); i != products.end(); i++) {
   261             update_product(*i, context++);
   262         }
   263 
   264     closing:
   265         if (internet)
   266             InternetCloseHandle(internet);
   267         if (hAES)
   268             BCryptCloseAlgorithmProvider(hAES, 0);
   269         if (hRSA)
   270             BCryptCloseAlgorithmProvider(hRSA, 0);
   271         internet = NULL;
   272         hAES = NULL;
   273         hRSA = NULL;
   274     }
   275 
   276 } // namespace pEp