#pragma once #include "bitstream.h" #include "base32.h" #include "base64.h" #include #ifndef LICENSING_NO_NETWORKING #ifdef _WIN32 #include #include #else #include #endif #endif using namespace std; class KeyGeneratorImpl { public: KeyGeneratorImpl(): m_signingServiceStatusCode(-1) { } KeyGeneratorImpl(const LicenseTemplateImpl * templ): KeyGeneratorImpl() { SetKeyTemplate(templ); } void SetKeyTemplate(const LicenseTemplateImpl * templ) { m_keyTemplate = templ; m_keyData.Create(m_keyTemplate->m_numGroups * m_keyTemplate->m_charsPerGroup * m_keyTemplate->m_keyEncoding); m_keyData.GetBitStream().Clear(); for (const auto& field : m_keyTemplate->m_dataFields) m_keyData.AddField(field.first.c_str(), field.second.type, field.second.size, field.second.offset); if (m_keyTemplate->m_validationDataSize) { m_validationData.Create(m_keyTemplate->m_validationDataSize); m_validationData.GetBitStream().Clear(); for (const auto& field : m_keyTemplate->m_validationFields) m_validationData.AddField(field.first.c_str(), field.second.type, field.second.size, field.second.offset); } } void SetKeyData(const char * fieldName, const void * buf, int len) { m_keyData.Set(fieldName, buf, len); } void SetKeyData(const char * fieldName, const char * data) { m_keyData.Set(fieldName, data); } void SetKeyData(const char * fieldName, int data) { m_keyData.Set(fieldName, data); } void SetKeyData(const char * fieldName, int year, int month, int day) { m_keyData.Set(fieldName, year, month, day); } void SetValidationData(const char * fieldName, const void * buf, int len) { m_validationData.Set(fieldName, buf, len); } void SetValidationData(const char * fieldName, const char * data) { m_validationData.Set(fieldName, data); } void SetValidationData(const char * fieldName, int data) { m_validationData.Set(fieldName, data); } #ifndef _WIN32 static size_t CurlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { KeyGeneratorImpl * pthis = (KeyGeneratorImpl *)userp; size_t realsize = size * nmemb; if (realsize > 1024 || pthis->m_keyBuffer.length() > 1024) return realsize - 1; // signal error if (realsize > 0) pthis->m_keyBuffer.append((char *)contents, realsize); return realsize; } #endif const char * GenerateKey() { BitStream signedData; BitStream signature; ECC::Signer signer; int sigLen, sigLenBits; #ifndef LICENSING_NO_NETWORKING if (m_keyTemplate->m_signingKey == NULL) { m_signingServiceStatusCode = -1; string activationQuery("Activate.ashx?"); //if (activationQuery[m_keyTemplate->m_signingServiceUrl.length() - 1] != '/') // activationQuery.append(1, '/'); //activationQuery.append("Activate.ashx?"); unsigned char * buf = (unsigned char *)_alloca(1 + ((m_validationData.GetSize() + 7) >> 3)); void * enumHandle = NULL; const char * name; BitStruct::FIELD_TYPE type; int size, offset; while (m_keyTemplate->EnumValidationFields(&enumHandle, &name, (int*)&type, &size, &offset)) { m_validationData.GetBitStream().Seek(offset); m_validationData.GetBitStream().Read(buf, size); activationQuery.append(name); activationQuery.append("="); if ((int)type == FIELD_TYPE_STRING) { buf[(size + 7) >> 3] = '\0'; activationQuery.append((char *)buf); } else { BASE64 base64; string base64Value = base64.encode(buf, (size + 7) >> 3, true); replace_all(base64Value, "+", "%2B"); replace_all(base64Value, "/", "%2F"); replace_all(base64Value, "=", "%3D"); activationQuery.append(base64Value.c_str()); } activationQuery.append("&"); } if (m_keyTemplate->m_licensingServiceParameters.length() > 0) { activationQuery.append(m_keyTemplate->m_licensingServiceParameters); activationQuery.append("&"); } activationQuery.erase(activationQuery.length() - 1, 1); m_keyBuffer.clear(); #ifdef _WIN32 URL_COMPONENTS urlComponents; ZeroMemory(&urlComponents, sizeof(urlComponents)); urlComponents.dwStructSize = sizeof(urlComponents); urlComponents.dwHostNameLength = (DWORD)-1; urlComponents.dwUrlPathLength = (DWORD)-1; auto licensingServiceUrl = s2w(m_keyTemplate->m_licensingServiceUrl); if (!WinHttpCrackUrl(licensingServiceUrl.c_str(), m_keyTemplate->m_licensingServiceUrl.length(), 0L, &urlComponents)) throw new LicensingException(STATUS_NET_ERROR, _T("networking error (1)")); std::wstring hostName(urlComponents.lpszHostName, urlComponents.dwHostNameLength); unique_ptr internet_ptr(WinHttpOpen(L"ANSCENTER Licensing SDK 2.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0L), WinHttpCloseHandle); if (!internet_ptr) throw new LicensingException(STATUS_NET_ERROR, _T("networking error (2)")); unique_ptr connection_ptr(WinHttpConnect(internet_ptr.get(), hostName.c_str(), urlComponents.nPort, 0L), WinHttpCloseHandle); if (!connection_ptr) throw new LicensingException(STATUS_NET_ERROR, _T("networking error (3)")); activationQuery.insert(0, w2s(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength)); DWORD requestFlags = WINHTTP_FLAG_BYPASS_PROXY_CACHE; if (urlComponents.nScheme == INTERNET_SCHEME_HTTPS) requestFlags |= WINHTTP_FLAG_SECURE; unique_ptr request_ptr(WinHttpOpenRequest(connection_ptr.get(), L"GET", s2w(activationQuery).c_str(), NULL, NULL, NULL, requestFlags), WinHttpCloseHandle); if (!request_ptr) throw new LicensingException(STATUS_NET_ERROR, _T("networking error (4)")); if (!WinHttpSendRequest(request_ptr.get(), NULL, 0L, NULL, 0L, 0L, 0L)) { DWORD result = GetLastError(); throw new LicensingException(STATUS_NET_ERROR, _T("networking error (5)")); } if (!WinHttpReceiveResponse(request_ptr.get(), NULL)) throw new LicensingException(STATUS_NET_ERROR, _T("networking error (6)")); DWORD status; DWORD statusSize = sizeof(DWORD); if (!WinHttpQueryHeaders(request_ptr.get(), WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status, &statusSize, WINHTTP_NO_HEADER_INDEX)) throw new LicensingException(STATUS_NET_ERROR, _T("networking error (7)")); m_signingServiceStatusCode = status; if (status != HTTP_STATUS_OK && status != HTTP_STATUS_ACCEPTED && status != HTTP_STATUS_CREATED) { WCHAR buffer[1024]; DWORD bufferSize = 1024; if (!WinHttpQueryHeaders(request_ptr.get(), WINHTTP_QUERY_STATUS_TEXT, WINHTTP_HEADER_NAME_BY_INDEX, buffer, &bufferSize, WINHTTP_NO_HEADER_INDEX)) throw new LicensingException(STATUS_NET_ERROR, "networking error (8)"); throw new LicensingException(STATUS_NET_ERROR, w2s(buffer).c_str()); } DWORD dataSize, count; char buffer[0x100]; do { if (!WinHttpQueryDataAvailable(request_ptr.get(), &dataSize)) break; if (size > 0x100) size = 0x100; if (WinHttpReadData(request_ptr.get(), buffer, dataSize, &count)) m_keyBuffer.append(buffer, count); } while (dataSize > 0); #else CURL *curl; CURLcode res; if ((curl = curl_easy_init()) == NULL) throw new LicensingException(STATUS_NET_ERROR, "cURL initalization failed"); if (*(m_keyTemplate->m_licensingServiceUrl.rbegin()) != '/') activationQuery.insert(0, "/"); activationQuery.insert(0, m_keyTemplate->m_licensingServiceUrl); curl_easy_setopt(curl, CURLOPT_URL, activationQuery.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); res = curl_easy_perform(curl); if(res != CURLE_OK) throw new LicensingException(STATUS_NET_ERROR, curl_easy_strerror(res)); curl_easy_cleanup(curl); #endif m_key = m_keyBuffer.c_str(); return m_key.c_str(); } #else if (m_keyTemplate->m_signingKey == NULL) { throw new LicensingException(STATUS_NET_ERROR, "private key not provided"); } #endif // signed data is license key data followed by validation data signedData.Create(m_keyTemplate->m_dataSize + m_keyTemplate->m_validationDataSize); if (m_keyTemplate->m_dataSize) signedData.Write(m_keyData.GetBuffer(), m_keyTemplate->m_dataSize); if (m_keyTemplate->m_validationDataSize) signedData.Write(m_validationData.GetBuffer(), m_keyTemplate->m_validationDataSize); signedData.ZeroPadToNextByte(); // create a bit stream to hold the signature signature.Create(m_keyTemplate->m_signatureSize); // sign data // we use a different algorithm than ECDSA when the signature size must be smaller than twice the key size if (m_keyTemplate->m_signatureSize < (m_keyTemplate->m_signatureKeySize << 1)) signer.SetHashSize(m_keyTemplate->m_signatureSize - m_keyTemplate->m_signatureKeySize); else signer.SetHashSize(0); signer.SetPrivateKey(m_keyTemplate->m_signingKey.get()); signer.Sign(signedData.GetBuffer(), (signedData.GetSize() + 7) >> 3, signature.GetBuffer(), &sigLen, &sigLenBits); signature.ReleaseBuffer(sigLenBits); // write the signature at the end of key data m_keyData.GetBitStream().Seek(m_keyTemplate->m_dataSize); m_keyData.GetBitStream().Write(signature.GetBuffer(), m_keyTemplate->m_signatureSize); // text-encode the license key EncodeKey(); // success return m_key.c_str(); } int GetSigningServiceStatusCode() { return m_signingServiceStatusCode; } void EncodeKey() { string buf; // set all bits to 0 until the next byte boundary //m_rawKey.ZeroPadToNextByte(); // reverse last byte from the stream, // so that we will not lose significant bits in the padless, truncated BAS32/64 encoding unsigned char * lastBytePtr = (unsigned char *)m_keyData.GetBuffer() + ((m_keyData.GetSize() + 7) >> 3) - 1; *lastBytePtr = (unsigned char)(((*lastBytePtr * 0x0802LU & 0x22110LU) | (*lastBytePtr * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); switch (m_keyTemplate->m_keyEncoding) { case ENCODING_BASE32X: { BASE32 base32; buf = base32.encode((unsigned char *)m_keyData.GetBuffer(), (m_keyData.GetSize() + 7) >> 3, true); } break; case ENCODING_BASE64X: { BASE64 base64; buf = base64.encode((unsigned char *)m_keyData.GetBuffer(), (m_keyData.GetSize() + 7) >> 3, false); } break; default: throw new LicensingException(STATUS_INVALID_KEY_ENCODING); } if (buf.empty()) throw new LicensingException(STATUS_OUT_OF_MEMORY); m_key = buf.c_str(); if ((int)m_key.length() > m_keyTemplate->m_numGroups * m_keyTemplate->m_charsPerGroup) m_key.resize(m_keyTemplate->m_numGroups * m_keyTemplate->m_charsPerGroup); int i; int insertPos; // separate character groups for (i = 0, insertPos = m_keyTemplate->m_charsPerGroup; i < m_keyTemplate->m_numGroups - 1; i++) { m_key.insert(insertPos, m_keyTemplate->m_groupSeparator); insertPos += (unsigned)( m_keyTemplate->m_groupSeparator.length() + m_keyTemplate->m_charsPerGroup ); } // add header and footer (if any) if (!m_keyTemplate->m_header.empty()) m_key.insert(0, m_keyTemplate->m_header + "\r\n"); if (!m_keyTemplate->m_footer.empty()) m_key.append("\r\n" + m_keyTemplate->m_footer); } std::size_t replace_all(std::string& inout, std::string_view what, std::string_view with) { std::size_t count{}; for (std::string::size_type pos{}; inout.npos != (pos = inout.find(what.data(), pos, what.length())); pos += with.length(), ++count) { inout.replace(pos, what.length(), with.data(), with.length()); } return count; } void StrReplaceA(std::string& str, const std::string& oldStr, const std::string& newStr) { size_t pos = 0; while((pos = str.find(oldStr, pos)) != std::string::npos) { str.replace(pos, oldStr.length(), newStr); pos += newStr.length(); } } private: const LicenseTemplateImpl* m_keyTemplate; string m_keyBuffer; string m_key; BitStruct m_keyData; BitStruct m_validationData; int m_signingServiceStatusCode; };