1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-06 00:13:33 +00:00
Files
MeshAgent/microstack/ILibCrypto.c
2021-01-21 20:13:15 -08:00

1139 lines
32 KiB
C

/*
Copyright 2006 - 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#if defined(WIN32) && !defined(_WIN32_WCE)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
#include "ILibCrypto.h"
#ifndef MICROSTACK_NOTLS
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#else
#ifdef WIN32
void BCRYPT_INIT(BCRYPT_CTX* ctx, void* alg)
{
memset(ctx, 0, sizeof(BCRYPT_CTX));
BCryptOpenAlgorithmProvider(&(ctx->hAlg), (LPCWSTR)alg, NULL, 0);
BCryptGetProperty(ctx->hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&(ctx->cbHashObject), sizeof(DWORD), &(ctx->cbData), 0);
ctx->pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, ctx->cbHashObject);
BCryptGetProperty(ctx->hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&(ctx->cbHash), sizeof(DWORD), &(ctx->cbData), 0);
BCryptCreateHash(ctx->hAlg, &(ctx->hHash), ctx->pbHashObject, ctx->cbHashObject, NULL, 0, 0);
}
void BCRYPT_UPDATE(BCRYPT_CTX* ctx, void* data, size_t dataLen)
{
BCryptHashData(ctx->hHash, (PBYTE)data, (ULONG)dataLen, 0);
}
void BCRYPT_FINAL(char *h, BCRYPT_CTX* ctx)
{
BCryptFinishHash(ctx->hHash, (PUCHAR)h, ctx->cbHash, 0);
BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
BCryptDestroyHash(ctx->hHash);
HeapFree(GetProcessHeap(), 0, ctx->pbHashObject);
}
#else
#include "microstack/nossl/md5.h"
#include "microstack/nossl/sha1.h"
#include "microstack/nossl/sha.h"
#include <time.h>
#endif
#endif
char utils_HexTable[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
char utils_HexTable2[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
void __fastcall util_md5(char* data, size_t datalen, char* result)
{
MD5_CTX c;
MD5_Init(&c);
MD5_Update(&c, data, datalen);
MD5_Final((unsigned char*)result, &c);
}
void __fastcall util_md5hex(char* data, size_t datalen, char *out)
{
int i = 0;
unsigned char *temp = (unsigned char*)out;
MD5_CTX mdContext;
unsigned char digest[16];
MD5_Init(&mdContext);
MD5_Update(&mdContext, (unsigned char *)data, datalen);
MD5_Final(digest, &mdContext);
for (i = 0; i < sizeof(digest); i++)
{
*(temp++) = utils_HexTable2[(unsigned char)digest[i] >> 4];
*(temp++) = utils_HexTable2[(unsigned char)digest[i] & 0x0F];
}
*temp = '\0';
}
void __fastcall util_sha1(char* data, size_t datalen, char* result)
{
SHA_CTX c;
SHA1_Init(&c);
SHA1_Update(&c, data, datalen);
SHA1_Final((unsigned char*)result, &c);
result[20] = 0;
}
void __fastcall util_sha256(char* data, size_t datalen, char* result)
{
SHA256_CTX c;
SHA256_Init(&c);
SHA256_Update(&c, data, datalen);
SHA256_Final((unsigned char*)result, &c);
}
void __fastcall util_sha384(char* data, size_t datalen, char* result)
{
SHA512_CTX c;
SHA384_Init(&c);
SHA384_Update(&c, data, datalen);
SHA384_Final((unsigned char*)result, &c);
}
int __fastcall util_sha384file(char* filename, char* result)
{
FILE *pFile = NULL;
SHA512_CTX c;
size_t len = 0;
char *buf = NULL;
if (filename == NULL) return -1;
#ifdef WIN32
_wfopen_s(&pFile, ILibUTF8ToWide(filename, -1), L"rbN");
#else
pFile = fopen(filename, "rb");
#endif
if (pFile == NULL) goto error;
SHA384_Init(&c);
if ((buf = (char*)malloc(4096)) == NULL) goto error;
while ((len = fread(buf, 1, 4096, pFile)) > 0) SHA384_Update(&c, buf, len);
free(buf);
buf = NULL;
fclose(pFile);
pFile = NULL;
SHA384_Final((unsigned char*)result, &c);
return 0;
error:
if (buf != NULL) free(buf);
if (pFile != NULL) fclose(pFile);
return -1;
}
// Frees a block of memory returned from this module.
void __fastcall util_free(char* ptr)
{
free(ptr);
//ptr = NULL;
}
char* __fastcall util_tohex(char* data, size_t len, char* out)
{
size_t i;
char *p = out;
if (data == NULL || len == 0) { *p = 0; return NULL; }
for (i = 0; i < len; i++)
{
*(p++) = utils_HexTable[((unsigned char)data[i]) >> 4];
*(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F];
}
*p = 0;
return out;
}
char* __fastcall util_tohex_lower(char* data, size_t len, char* out)
{
size_t i;
char *p = out;
if (data == NULL || len == 0) { *p = 0; return NULL; }
for (i = 0; i < len; i++)
{
*(p++) = utils_HexTable2[((unsigned char)data[i]) >> 4];
*(p++) = utils_HexTable2[((unsigned char)data[i]) & 0x0F];
}
*p = 0;
return out;
}
char* __fastcall util_tohex2(char* data, size_t len, char* out)
{
size_t i;
char *p = out;
if (data == NULL || len == 0) { *p = 0; return NULL; }
for (i = 0; i < len; i++)
{
*(p++) = utils_HexTable[((unsigned char)data[i]) >> 4];
*(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F];
if (i + 1<len)
{
*(p++) = ':';
}
}
*p = 0;
return out;
}
// Convert hex string to int
int __fastcall util_hexToint(char *hexString, size_t hexStringLength)
{
size_t i;
int res = 0;
// Ignore the leading zeroes
while (*hexString == '0' && hexStringLength > 0) { hexString++; hexStringLength--; }
// Process the rest of the string
for (i = 0; i < hexStringLength; i++)
{
if (hexString[i] >= '0' && hexString[i] <= '9') { res = (res << 4) + (hexString[i] - '0'); }
else if (hexString[i] >= 'a' && hexString[i] <= 'f') { res = (res << 4) + (hexString[i] - 'a' + 10); }
else if (hexString[i] >= 'A' && hexString[i] <= 'F') { res = (res << 4) + (hexString[i] - 'A' + 10); }
}
return res;
}
// Convert hex string to int
size_t __fastcall util_hexToBuf(char *hexString, size_t hexStringLength, char* output)
{
size_t i, x = hexStringLength / 2;
for (i = 0; i < x; i++) { output[i] = (char)util_hexToint(hexString + (i * 2), 2); }
return i;
}
// Generates a random string of data. TODO: Use Hardware RNG if possible
#ifdef MICROSTACK_NOTLS
int util_random_seeded = 0;
#endif
void __fastcall util_random(int length, char* result)
{
#ifndef MICROSTACK_NOTLS
RAND_bytes((unsigned char*)result, length);
#else
short val;
int i;
if (util_random_seeded == 0)
{
time_t t;
srand((unsigned int)time(&t));
util_random_seeded = 1;
}
for (i = 0; i < length; i += 2)
{
val = rand();
memcpy_s(result + i, length - i, &val, (length - i) >= 2 ? 2 : (length - i));
}
#endif
}
// Generates a random text string, useful for HTTP nonces.
void __fastcall util_randomtext(int length, char* result)
{
int l;
util_random(length, result);
for (l = 0; l<length; l++) result[l] = (unsigned char)((((unsigned char)result[l]) % 10) + '0');
}
size_t __fastcall util_writefile(char* filename, char* data, int datalen)
{
FILE * pFile = NULL;
size_t count = 0;
#ifdef WIN32
_wfopen_s(&pFile, ILibUTF8ToWide(filename, -1), L"wbN");
#else
pFile = fopen(filename, "wb");
#endif
if (pFile != NULL)
{
count = fwrite(data, datalen, 1, pFile);
fclose(pFile);
}
return count;
}
size_t __fastcall util_appendfile(char* filename, char* data, int datalen)
{
FILE * pFile = NULL;
size_t count = 0;
#ifdef WIN32
_wfopen_s(&pFile, ILibUTF8ToWide(filename, -1), L"abN");
#else
pFile = fopen(filename, "ab");
#endif
if (pFile != NULL)
{
count = fwrite(data, datalen, 1, pFile);
fclose(pFile);
}
return count;
}
// Read a file into memory up to maxlen. If *data is NULL, a new buffer is allocated otherwise, the given one is used.
size_t __fastcall util_readfile(char* filename, char** data, size_t maxlen)
{
FILE *pFile = NULL;
size_t count = 0;
size_t len = 0;
size_t r = 1;
if (filename == NULL) return 0;
#ifdef WIN32
_wfopen_s(&pFile, ILibUTF8ToWide(filename, -1), L"rbN");
#else
pFile = fopen(filename, "rb");
#endif
if (pFile != NULL)
{
// If *data is null, we need to allocate memory to read the data. Start by getting the size of the file.
if (*data == NULL)
{
fseek(pFile, 0, SEEK_END);
count = ftell(pFile);
if (count > maxlen) count = maxlen;
fseek(pFile, 0, SEEK_SET);
*data = (char*)malloc(count + 1);
if (*data == NULL) { fclose(pFile); return 0; }
}
else { count = maxlen - 1; }
while (r != 0 && len < count)
{
r = fread(*data, 1, count - len, pFile);
len += r;
}
(*data)[len] = 0;
fclose(pFile);
}
return len;
}
#ifdef _POSIX
// This method reads a stream where the length of the file can't be determined. Useful in POSIX only
int __fastcall util_readfile2(char* filename, char** data)
{
FILE * pFile;
int count = 0;
int len = 0;
*data = NULL;
if (filename == NULL) return 0;
pFile = fopen(filename, "rb");
if (pFile != NULL)
{
*data = malloc(1024);
if (*data == NULL) { fclose(pFile); return 0; }
do
{
len = (int)fread((*data) + count, 1, 1023, pFile);
count += len;
if (len == 1023) *data = realloc(*data, count + 1024);
} while (len == 100);
(*data)[count] = 0;
fclose(pFile);
}
return count;
}
#endif
int __fastcall util_deletefile(char* filename)
{
if (filename == NULL) return 0;
#ifdef WIN32
return(_wremove(ILibUTF8ToWide(filename, -1)));
#else
return(remove(filename));
#endif
}
#ifdef WIN32
// Really fast CRC-like method. Used for the KVM.
int __fastcall util_crc(unsigned char *buffer, int len, int initial_value)
{
int hval = initial_value;
int *bp = (int*)buffer;
int *be = bp + (len >> 2);
while (bp < be)
{
//hval *= 0x01000193;
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
hval ^= *bp++;
}
// TODO: Handle left over bytes (len % 4)
return hval;
}
#ifdef _MINCORE
BOOL util_MoveFile(_In_ LPCTSTR lpExistingFileName, _In_ LPCTSTR lpNewFileName)
{
size_t convertedChars = 0;
wchar_t lpExistingFileNameW[MAX_PATH];
wchar_t lpNewFileNameW[MAX_PATH];
mbstowcs_s(&convertedChars, lpExistingFileNameW, MAX_PATH, (const char*)lpExistingFileName, MAX_PATH);
mbstowcs_s(&convertedChars, lpNewFileNameW, MAX_PATH, (const char*)lpNewFileName, MAX_PATH);
return MoveFileExW(lpExistingFileNameW, lpNewFileNameW, 0);
}
BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _In_ BOOL bFailIfExists)
{
size_t convertedChars = 0;
wchar_t lpExistingFileNameW[MAX_PATH];
wchar_t lpNewFileNameW[MAX_PATH];
mbstowcs_s(&convertedChars, lpExistingFileNameW, MAX_PATH, (const char*)lpExistingFileName, MAX_PATH);
mbstowcs_s(&convertedChars, lpNewFileNameW, MAX_PATH, (const char*)lpNewFileName, MAX_PATH);
return (CopyFile2(lpExistingFileNameW, lpNewFileNameW, NULL) == S_OK);
}
#else
BOOL util_MoveFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName)
{
WCHAR wExisting[4096];
WCHAR wNew[4096];
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)lpExistingFileName, -1, (LPWSTR)wExisting, (int)sizeof(wExisting) / 2);
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)lpNewFileName, -1, (LPWSTR)wNew, (int)sizeof(wNew) / 2);
return MoveFileW(wExisting, wNew);
}
BOOL util_CopyFile(_In_ LPCSTR lpExistingFileName, _In_ LPCSTR lpNewFileName, _In_ BOOL bFailIfExists)
{
WCHAR wExisting[4096];
WCHAR wNew[4096];
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)lpExistingFileName, -1, (LPWSTR)wExisting, (int)sizeof(wExisting) / 2);
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)lpNewFileName, -1, (LPWSTR)wNew, (int)sizeof(wNew) / 2);
return CopyFileW(wExisting, wNew, bFailIfExists);
}
#endif
#endif
#ifndef MICROSTACK_NOTLS
// Setup OpenSSL
int InitCounter = 0;
void __fastcall util_openssl_init()
{
char* tbuf[64];
#if defined(WIN32)
HMODULE g_hAdvLib = NULL;
BOOLEAN(APIENTRY *g_CryptGenRandomPtr)(void*, ULONG) = NULL;
#endif
#ifdef _POSIX
int l;
#endif
++InitCounter;
if (InitCounter > 1) { return; }
SSL_library_init(); // TWO LEAKS COMING FROM THIS LINE. Seems to be a well known OpenSSL problem.
SSL_load_error_strings();
ERR_load_crypto_strings(); // ONE LEAK IN LINUX
OpenSSL_add_all_algorithms(); // OpenSSL 1.1
OpenSSL_add_all_ciphers(); // OpenSSL 1.1
OpenSSL_add_all_digests(); // OpenSSL 1.1
#ifdef FIPSMODE
if (FIPS_mode() || FIPS_mode_set(1))
{
printf("ENTERED FIPS mode\n");
}
else
{
ILIBCRITICALEXITMSG(200, "FAILED to enter FIPS mode");
}
#endif
// Add more random seeding in Windows (This is probably useful since OpenSSL in Windows has weaker seeding)
#if defined(WIN32) && !defined(_MINCORE)
//RAND_screen(); // On Windows, add more random seeding using a screen dump (this is very expensive).
if ((g_hAdvLib = LoadLibraryExA((LPCSTR)"ADVAPI32.DLL", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32)) != 0) g_CryptGenRandomPtr = (BOOLEAN(APIENTRY *)(void*, ULONG))GetProcAddress(g_hAdvLib, "SystemFunction036");
if (g_CryptGenRandomPtr != 0 && g_CryptGenRandomPtr(tbuf, 64) != 0) RAND_add(tbuf, 64, 64); // Use this high quality random as added seeding
if (g_hAdvLib != NULL) FreeLibrary(g_hAdvLib);
#endif
// Add more random seeding in Linux (May be overkill since OpenSSL already uses /dev/urandom)
#ifdef _POSIX
// Under Linux we use "/dev/urandom" if available. This is the best source of random on Linux & variants
FILE *pFile = fopen("/dev/urandom", "rb");
if (pFile != NULL)
{
l = (int)fread(tbuf, 1, 64, pFile);
fclose(pFile);
if (l > 0) RAND_add(tbuf, l, l);
}
#endif
}
// Cleanup OpenSSL
void __fastcall util_openssl_uninit()
{
--InitCounter;
if (InitCounter > 0) { return; }
//RAND_cleanup(); // Does nothing.
//CRYPTO_set_dynlock_create_callback(NULL); // Does nothing.
//CRYPTO_set_dynlock_destroy_callback(NULL); // Does nothing.
//CRYPTO_set_dynlock_lock_callback(NULL); // Does nothing.
//CRYPTO_set_locking_callback(NULL); // Does nothing.
//CRYPTO_set_id_callback(NULL); // Does nothing.
CRYPTO_cleanup_all_ex_data();
//sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); // Does something, but it causes heap corruption...
//CONF_modules_unload(1); // Does nothing.
CONF_modules_free();
EVP_cleanup();
ERR_free_strings();
//ERR_remove_state(0); // Deprecated in OpenSSL/1.1.x
#ifndef OLDSSL
OPENSSL_cleanup();
#endif
}
// Add extension using V3 code: we can set the config file as NULL because we wont reference any other sections.
int __fastcall util_add_ext(X509 *cert, int nid, char *value)
{
X509_EXTENSION *ex;
X509V3_CTX ctx;
// This sets the 'context' of the extensions. No configuration database
X509V3_set_ctx_nodb(&ctx);
// Issuer and subject certs: both the target since it is self signed, no request and no CRL
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ex) return 0;
X509_add_ext(cert, ex, -1);
X509_EXTENSION_free(ex);
return 1;
}
void __fastcall util_freecert(struct util_cert* cert)
{
if ((cert->flags & ILibCrypto_Cert_Ownership_Other) == 0)
{
if (cert->x509 != NULL) X509_free(cert->x509);
if (cert->pkey != NULL) EVP_PKEY_free(cert->pkey);
}
cert->x509 = NULL;
cert->pkey = NULL;
cert->flags = 0;
}
int __fastcall util_to_cer(struct util_cert cert, char** data)
{
*data = NULL;
return i2d_X509(cert.x509, (unsigned char**)data);
}
int __fastcall util_from_cer(char* data, int datalen, struct util_cert* cert)
{
cert->pkey = NULL;
cert->x509 = d2i_X509(NULL, (const unsigned char**)&data, datalen);
return ((cert->x509) != NULL);
}
int __fastcall util_from_pkcs7b_string(char *data, int datalen, char *out, int outLen)
{
BIO* bio = BIO_new_mem_buf((void*)data, datalen);
BIO* bio_out = BIO_new(BIO_s_mem());
BUF_MEM *outBuffer;
int retVal = 0, i = 0;
PKCS7 *p7 = NULL;
STACK_OF(X509) *certs = NULL;
STACK_OF(X509_ALGOR) *md_algs = NULL;
STACK_OF(PKCS7_SIGNER_INFO) *sinfo = NULL;
BIO_get_mem_ptr(bio_out, &outBuffer);
p7 = d2i_PKCS7_bio(bio, NULL);
md_algs = p7->d.sign->md_algs;
sinfo = p7->d.sign->signer_info;
for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfo); i++)
{
printf("Hash Algorithm: %s\n", OBJ_nid2ln(OBJ_obj2nid(sk_PKCS7_SIGNER_INFO_value(sinfo, i)->digest_alg->algorithm)));
printf("Enc Algorithm: %s\n", OBJ_nid2ln(OBJ_obj2nid(sk_PKCS7_SIGNER_INFO_value(sinfo, i)->digest_enc_alg->algorithm)));
}
for (i = 0; i < sk_X509_ALGOR_num(md_algs); i++)
{
printf("Algorithm: %s\n", OBJ_nid2ln(OBJ_obj2nid(sk_X509_ALGOR_value(md_algs, i)->algorithm)));
}
certs = p7->d.sign->cert;
for (i = 0; certs && i < sk_X509_num(certs); i++)
{
PEM_write_bio_X509(bio_out, sk_X509_value(certs, i));
}
if (outLen >= (int)outBuffer->length)
{
memcpy_s(out, outLen, outBuffer->data, outBuffer->length);
out[outBuffer->length] = 0;
}
else
{
retVal = (int)(outBuffer->length) + 1;
}
BIO_free(bio);
BIO_free(bio_out);
return(retVal);
}
int __fastcall util_from_pem_string(char *data, int datalen, struct util_cert* cert)
{
BIO* bio = BIO_new_mem_buf((void*)data, datalen);
int retVal = 0;
if ((cert->pkey = PEM_read_bio_PrivateKey(bio, NULL, 0, NULL)) == NULL) { retVal = -1; }
if ((cert->x509 = PEM_read_bio_X509(bio, NULL, 0, NULL)) == NULL) { retVal = -1; }
BIO_free(bio);
return retVal;
}
int __fastcall util_from_pem(char* filename, struct util_cert* cert)
{
FILE *pFile = NULL;
if (filename == NULL) return -1;
#ifdef WIN32
_wfopen_s(&pFile, ILibUTF8ToWide(filename, -1), L"rbN");
#else
pFile = fopen(filename, "rb");
#endif
if (pFile == NULL) goto error;
if ((cert->pkey = PEM_read_PrivateKey(pFile, NULL, 0, NULL)) == NULL) goto error;
if ((cert->x509 = PEM_read_X509(pFile, NULL, 0, NULL)) == NULL) goto error;
fclose(pFile);
return 0;
error:
if (pFile != NULL) fclose(pFile);
return -1;
}
int __fastcall util_to_p12(struct util_cert cert, char *password, char** data)
{
PKCS12 *p12;
int len;
p12 = PKCS12_create(password, "Certificate", cert.pkey, cert.x509, NULL, 0, 0, 0, 0, 0);
*data = NULL;
len = i2d_PKCS12(p12, (unsigned char**)data);
PKCS12_free(p12);
return len;
}
int __fastcall util_from_p12(char* data, int datalen, char* password, struct util_cert* cert)
{
int r = 0;
PKCS12 *p12 = NULL;
if (data == NULL || datalen == 0) return 0;
cert->x509 = NULL;
cert->pkey = NULL;
p12 = d2i_PKCS12(&p12, (const unsigned char**)&data, datalen);
r = PKCS12_parse(p12, password, &(cert->pkey), &(cert->x509), NULL);
PKCS12_free(p12);
return r;
}
void __fastcall util_printcert(struct util_cert cert)
{
if (cert.x509 == NULL) return;
X509_print_fp(stdout, cert.x509);
}
void __fastcall util_printcert_pk(struct util_cert cert)
{
if (cert.pkey == NULL) return;
//RSA_print_fp(stdout, cert.pkey->pkey.rsa, 0);
RSA_print_fp(stdout, EVP_PKEY_get1_RSA(cert.pkey), 0);
}
// Creates a X509 certificate, if rootcert is NULL this creates a root (self-signed) certificate.
// Is the name parameter is NULL, the hex value of the hash of the public key will be the subject name.
int __fastcall util_mkCert(struct util_cert *rootcert, struct util_cert* cert, int bits, int days, char* name, enum CERTIFICATE_TYPES certtype, struct util_cert* initialcert)
{
X509 *x = NULL;
X509_EXTENSION *ex = NULL;
EVP_PKEY *pk = NULL;
RSA *rsa = NULL;
X509_NAME *cname = NULL;
X509 **x509p = NULL;
EVP_PKEY **pkeyp = NULL;
int hashlen = UTIL_SHA384_HASHSIZE;
char hash[UTIL_SHA384_HASHSIZE];
char serial[8];
char nameStr[(UTIL_SHA384_HASHSIZE * 2) + 2];
BIGNUM *oBigNbr;
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
if (initialcert != NULL)
{
pk = X509_get_pubkey(initialcert->x509);
rsa = EVP_PKEY_get1_RSA(initialcert->pkey);
if ((x = X509_new()) == NULL) goto err;
}
else
{
if ((pkeyp == NULL) || (*pkeyp == NULL)) { if ((pk = EVP_PKEY_new()) == NULL) return 0; }
else pk = *pkeyp;
if ((x509p == NULL) || (*x509p == NULL)) { if ((x = X509_new()) == NULL) goto err; }
else x = *x509p;
oBigNbr = BN_new();
rsa = RSA_new();
BN_set_word(oBigNbr, RSA_F4);
if (RSA_generate_key_ex(rsa, bits, oBigNbr, NULL) == -1)
{
RSA_free(rsa);
BN_free(oBigNbr);
abort();
}
BN_free(oBigNbr);
}
if (!EVP_PKEY_assign_RSA(pk, rsa))
{
RSA_free(rsa);
abort();
}
rsa = NULL;
util_randomtext(8, serial);
X509_set_version(x, 2);
ASN1_STRING_set(X509_get_serialNumber(x), serial, 8);
X509_gmtime_adj(X509_get_notBefore(x), (long)60 * 60 * 24 * -10);
X509_gmtime_adj(X509_get_notAfter(x), (long)60 * 60 * 24 * days);
X509_set_pubkey(x, pk);
// Set the subject name
cname = X509_get_subject_name(x);
if (name == NULL)
{
// Computer the hash of the public key
//util_sha256((char*)x->cert_info->key->public_key->data, x->cert_info->key->public_key->length, hash); // OpenSSL 1.0
X509_pubkey_digest(x, EVP_sha384(), (unsigned char*)hash, (unsigned int*)&hashlen); // OpenSSL 1.1
util_tohex(hash, UTIL_SHA384_HASHSIZE, nameStr);
X509_NAME_add_entry_by_txt(cname, "CN", MBSTRING_ASC, (unsigned char*)nameStr, -1, -1, 0);
}
else
{
// This function creates and adds the entry, working out the correct string type and performing checks on its length. Normally we'd check the return value for errors...
X509_NAME_add_entry_by_txt(cname, "CN", MBSTRING_ASC, (unsigned char*)name, -1, -1, 0);
}
if (rootcert == NULL)
{
// Its self signed so set the issuer name to be the same as the subject.
X509_set_issuer_name(x, cname);
// Add various extensions: standard extensions
util_add_ext(x, NID_basic_constraints, "critical,CA:TRUE");
util_add_ext(x, NID_key_usage, "critical,keyCertSign,cRLSign");
util_add_ext(x, NID_subject_key_identifier, "hash");
//util_add_ext(x, NID_netscape_cert_type, "sslCA");
//util_add_ext(x, NID_netscape_comment, "example comment extension");
if (!X509_sign(x, pk, EVP_sha384())) goto err;
}
else
{
// This is a sub-certificate
cname = X509_get_subject_name(rootcert->x509);
X509_set_issuer_name(x, cname);
// Add usual cert stuff
ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, "digitalSignature, keyEncipherment, keyAgreement");
X509_add_ext(x, ex, -1);
X509_EXTENSION_free(ex);
// Add usages: TLS server, TLS client, Intel(R) AMT Console
//ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, "TLS Web Server Authentication, TLS Web Client Authentication, 2.16.840.1.113741.1.2.1, 2.16.840.1.113741.1.2.2");
if (certtype == CERTIFICATE_TLS_SERVER)
{
// TLS server
ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, "TLS Web Server Authentication");
X509_add_ext(x, ex, -1);
X509_EXTENSION_free(ex);
}
else if (certtype == CERTIFICATE_TLS_CLIENT)
{
// TLS client
ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, "TLS Web Client Authentication");
X509_add_ext(x, ex, -1);
X509_EXTENSION_free(ex);
}
if (!X509_sign(x, rootcert->pkey, EVP_sha384())) goto err;
}
cert->x509 = x;
cert->pkey = pk;
cert->flags = 0;
return 1;
err:
return 0;
}
int __fastcall util_certhash(struct util_cert cert, char* result) {
int hashlen = UTIL_SHA384_HASHSIZE;
if (cert.x509 == NULL) return -1;
X509_digest(cert.x509, EVP_sha384(), (unsigned char*)result, (unsigned int *)&hashlen); // OpenSSL 1.1
return 0;
}
int __fastcall util_certhash2(X509* cert, char* result) {
int hashlen = UTIL_SHA384_HASHSIZE;
if (cert == NULL) return -1;
X509_digest(cert, EVP_sha384(), (unsigned char*)result, (unsigned int*)&hashlen); // OpenSSL 1.1
return 0;
}
int __fastcall util_keyhash(struct util_cert cert, char* result)
{
int hashlen = UTIL_SHA384_HASHSIZE;
if (cert.x509 == NULL) return -1;
X509_pubkey_digest(cert.x509, EVP_sha384(), (unsigned char*)result,(unsigned int *) &hashlen); // OpenSSL 1.1
return 0;
}
int __fastcall util_keyhash2(X509* cert, char* result)
{
int hashlen = UTIL_SHA384_HASHSIZE;
if (cert == NULL) return -1;
X509_pubkey_digest(cert, EVP_sha384(), (unsigned char*)result, (unsigned int*)&hashlen); // OpenSSL 1.1
return 0;
}
// Sign this block of data, the first 32 bytes of the block must be avaialble to add the certificate hash.
int __fastcall util_sign(struct util_cert cert, char* data, int datalen, char** signature)
{
int size = 0;
unsigned int hashsize = UTIL_SHA384_HASHSIZE;
BIO *in = NULL;
PKCS7 *message = NULL;
*signature = NULL;
if (datalen <= UTIL_SHA384_HASHSIZE) return 0;
// Add hash of the certificate to start of data
X509_digest(cert.x509, EVP_sha384(), (unsigned char*)data, &hashsize);
// Sign the block
in = BIO_new_mem_buf(data, datalen);
message = PKCS7_sign(cert.x509, cert.pkey, NULL, in, PKCS7_BINARY);
if (message == NULL) goto error;
size = i2d_PKCS7(message, (unsigned char**)signature);
error:
if (message != NULL) PKCS7_free(message);
if (in != NULL) BIO_free(in);
return size;
}
// Verify the signed block, the first 32 bytes of the data must be the certificate hash to work.
int __fastcall util_verify(char* signature, int signlen, struct util_cert* cert, char** data)
{
unsigned int size, r;
BIO *out = NULL;
PKCS7 *message = NULL;
char* data2 = NULL;
char hash[UTIL_SHA256_HASHSIZE];
STACK_OF(X509) *st = NULL;
cert->x509 = NULL;
cert->pkey = NULL;
*data = NULL;
message = d2i_PKCS7(NULL, (const unsigned char**)&signature, signlen);
if (message == NULL) goto error;
out = BIO_new(BIO_s_mem());
// Lets rebuild the original message and check the size
size = i2d_PKCS7(message, NULL);
if (size < (unsigned int)signlen) goto error;
// Check the PKCS7 signature, but not the certificate chain.
r = PKCS7_verify(message, NULL, NULL, NULL, out, PKCS7_NOVERIFY);
if (r == 0) goto error;
// If data block contains less than 32 bytes, fail.
size = (unsigned int)BIO_get_mem_data(out, &data2);
if (size <= UTIL_SHA256_HASHSIZE) goto error;
// Copy the data block
*data = (char*)malloc(size + 1);
if (*data == NULL) goto error;
memcpy_s(*data, size + 1, data2, size);
(*data)[size] = 0;
// Get the certificate signer
st = PKCS7_get0_signers(message, NULL, PKCS7_NOVERIFY);
cert->x509 = X509_dup(sk_X509_value(st, 0));
sk_X509_free(st);
// Get a full certificate hash of the signer
r = UTIL_SHA256_HASHSIZE;
X509_digest(cert->x509, EVP_sha256(), (unsigned char*)hash, &r);
// Check certificate hash with first 32 bytes of data.
if (memcmp(hash, *data, UTIL_SHA256_HASHSIZE) != 0) goto error;
// Approved, cleanup and return.
BIO_free(out);
PKCS7_free(message);
return size;
error:
if (out != NULL) BIO_free(out);
if (message != NULL) PKCS7_free(message);
if (*data != NULL) free(*data);
if (cert->x509 != NULL) { X509_free(cert->x509); cert->x509 = NULL; }
return 0;
}
// Encrypt a block of data for a target certificate
int __fastcall util_encrypt(struct util_cert cert, char* data, int datalen, char** encdata)
{
int size = 0;
BIO *in = NULL;
PKCS7 *message = NULL;
STACK_OF(X509) *encerts = NULL;
*encdata = NULL;
if (datalen == 0) return 0;
// Setup certificates
encerts = sk_X509_new_null();
sk_X509_push(encerts, cert.x509);
// Encrypt the block
*encdata = NULL;
in = BIO_new_mem_buf(data, datalen);
message = PKCS7_encrypt(encerts, in, EVP_aes_128_cbc(), PKCS7_BINARY);
if (message == NULL) return 0;
size = i2d_PKCS7(message, (unsigned char**)encdata);
BIO_free(in);
PKCS7_free(message);
sk_X509_free(encerts);
return size;
}
// Encrypt a block of data using multiple target certificates
int __fastcall util_encrypt2(STACK_OF(X509) *certs, char* data, int datalen, char** encdata)
{
int size = 0;
BIO *in = NULL;
PKCS7 *message = NULL;
*encdata = NULL;
if (datalen == 0) return 0;
// Encrypt the block
*encdata = NULL;
in = BIO_new_mem_buf(data, datalen);
message = PKCS7_encrypt(certs, in, EVP_aes_128_cbc(), PKCS7_BINARY);
if (message == NULL) return 0;
size = i2d_PKCS7(message, (unsigned char**)encdata);
BIO_free(in);
PKCS7_free(message);
return size;
}
// Decrypt a block of data using the specified certificate. The certificate must have a private key.
int __fastcall util_decrypt(char* encdata, int encdatalen, struct util_cert cert, char** data)
{
unsigned int size, r;
BIO *out = NULL;
PKCS7 *message = NULL;
char* data2 = NULL;
*data = NULL;
if (cert.pkey == NULL) return 0;
message = d2i_PKCS7(NULL, (const unsigned char**)&encdata, encdatalen);
if (message == NULL) goto error;
out = BIO_new(BIO_s_mem());
// Lets rebuild the original message and check the size
size = i2d_PKCS7(message, NULL);
if (size < (unsigned int)encdatalen) goto error;
// Decrypt the PKCS7
r = PKCS7_decrypt(message, cert.pkey, cert.x509, out, 0);
if (r == 0) goto error;
// If data block contains 0 bytes, fail.
size = (unsigned int)BIO_get_mem_data(out, &data2);
if (size == 0) goto error;
// Copy the data block
*data = (char*)malloc(size + 1);
if (*data == NULL) goto error;
memcpy_s(*data, size + 1, data2, size);
(*data)[size] = 0;
// Cleanup and return.
BIO_free(out);
PKCS7_free(message);
return size;
error:
if (out != NULL) BIO_free(out);
if (message != NULL) PKCS7_free(message);
if (*data != NULL) free(*data);
if (data2 != NULL) free(data2);
return 0;
}
// Encrypt a block of data using raw RSA. This is used to handle data in the most compact possible way.
int __fastcall util_rsaencrypt(X509 *cert, char* data, int datalen, char** encdata)
{
int len;
RSA *rsa;
EVP_PKEY *pkey;
pkey = X509_get_pubkey(cert);
rsa = EVP_PKEY_get1_RSA(pkey);
if (datalen > RSA_size(rsa)) { EVP_PKEY_free(pkey); RSA_free(rsa); return 0; }
*encdata = (char*)malloc(RSA_size(rsa));
len = RSA_public_encrypt(datalen, (const unsigned char*)data, (unsigned char*)*encdata, rsa, RSA_PKCS1_OAEP_PADDING);
EVP_PKEY_free(pkey);
RSA_free(rsa);
if (len == RSA_size(rsa)) return len;
free(*encdata);
*encdata = NULL;
return 0;
}
// Decrypt a block of data using raw RSA. This is used to handle data in the most compact possible way.
int __fastcall util_rsadecrypt(struct util_cert cert, char* data, int datalen, char** decdata)
{
int len;
RSA *rsa;
rsa = EVP_PKEY_get1_RSA(cert.pkey);
*decdata = (char*)malloc(RSA_size(rsa));
len = RSA_private_decrypt(datalen, (const unsigned char*)data, (unsigned char*)*decdata, rsa, RSA_PKCS1_OAEP_PADDING);
RSA_free(rsa);
if (len != 0) return len;
free(*decdata);
*decdata = NULL;
return 0;
}
// Verify the RSA signature of a block using SHA1 hash
int __fastcall util_rsaverify(X509 *cert, char* data, int datalen, char* sign, int signlen)
{
int r;
RSA *rsa = NULL;
EVP_PKEY *pkey = NULL;
SHA_CTX c;
char hash[20];
SHA1_Init(&c);
SHA1_Update(&c, data, datalen);
SHA1_Final((unsigned char*)hash, &c);
pkey = X509_get_pubkey(cert);
rsa = EVP_PKEY_get1_RSA(pkey);
//rsa->pad = RSA_PKCS1_PADDING; // OPENSSL 1.0
#ifdef WIN32
r = RSA_verify(NID_sha1, (const unsigned char*)hash, 20, (const unsigned char*)sign, signlen, rsa);
#else
r = RSA_verify(NID_sha1, (const unsigned char*)hash, 20, (unsigned char*)sign, signlen, rsa);
#endif
EVP_PKEY_free(pkey);
RSA_free(rsa);
return r;
}
#ifdef _SSL_KEYS_EXPORTABLE
int __fastcall util_exportkeys(SSL* ssl, char *buffer, size_t bufferSize)
{
int len = 0;
char clientRandom[32], serverRandom[32], sessionSecret[48], clientRandomHex[65], serverRandomHex[65], sessionSecretHex[97];
// Get the client random and session key.
if (ssl == NULL) return(0);
if (SSL_get_client_random(ssl, (unsigned char*)clientRandom, 32) != 32) return(0);
if (SSL_get_server_random(ssl, (unsigned char*)serverRandom, 32) != 32) return(0);
if (SSL_SESSION_get_master_key(SSL_get_session(ssl), (unsigned char*)sessionSecret, 48) != 48) return(0);
// Convert the randoms and key into hex
util_tohex(clientRandom, 32, clientRandomHex);
util_tohex(serverRandom, 32, serverRandomHex);
util_tohex(sessionSecret, 48, sessionSecretHex);
// Append the client random and key to the log file.
if (buffer != NULL && bufferSize > 0)
{
len = sprintf_s(buffer, bufferSize, "CLIENT_RANDOM %s %s\r\nCLIENT_RANDOM %s %s\r\n", clientRandomHex, sessionSecretHex, serverRandomHex, sessionSecretHex);
}
return(len);
}
#endif
#ifdef _DEBUG
// Saves the SSL/TLS session private keys to file.
// Because we do lots of DTLS, we will be saving both client and server randoms pointing to the same key.
// WARNING: THIS IS FOR DEBUG ONLY, NEVER RELEASE THIS. For safety, comment this out.
void __fastcall util_savekeys(SSL* ssl) {
/*
int len;
char clientRandom[32], serverRandom[32], sessionSecret[48], clientRandomHex[65], serverRandomHex[65], sessionSecretHex[97], text[2000];
// Get the client random and session key.
if (ssl == NULL) return;
if (SSL_get_client_random(ssl, clientRandom, 32) != 32) return;
if (SSL_get_server_random(ssl, serverRandom, 32) != 32) return;
len = SSL_SESSION_get_master_key(SSL_get_session(ssl), sessionSecret, 48);
if (len <= 0) return;
// Convert the randoms and key into hex
util_tohex(clientRandom, 32, clientRandomHex);
util_tohex(serverRandom, 32, serverRandomHex);
util_tohex(sessionSecret, len, sessionSecretHex);
// Append the client random and key to the log file.
len = snprintf(text, 1000, "CLIENT_RANDOM %s %s\r\nCLIENT_RANDOM %s %s\r\n", clientRandomHex, sessionSecretHex, serverRandomHex, sessionSecretHex);
util_appendfile("meshagentkeys.log", text, len);
*/
}
#endif
#endif