Files
ANSCORE/anslicensing/generator.h

424 lines
12 KiB
C++

#pragma once
#include "bitstream.h"
#include "base32.h"
#include "base64.h"
#include <map>
#ifndef LICENSING_NO_NETWORKING
#ifdef _WIN32
#include <winhttp.h>
#include <tchar.h>
#else
#include <curl/curl.h>
#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<VOID, decltype(&WinHttpCloseHandle)> 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<VOID, decltype(&WinHttpCloseHandle)> 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<VOID, decltype(&WinHttpCloseHandle)> 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;
};