mirror of
https://github.com/SoftEtherVPN/SoftEtherVPN.git
synced 2025-06-28 03:45:08 +03:00
7361 lines
142 KiB
C
7361 lines
142 KiB
C
// SoftEther VPN Source Code - Stable Edition Repository
|
|
// Mayaqua Kernel
|
|
//
|
|
// SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
|
|
//
|
|
// Copyright (c) Daiyuu Nobori.
|
|
// Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
|
|
// Copyright (c) SoftEther Corporation.
|
|
// Copyright (c) all contributors on SoftEther VPN project in GitHub.
|
|
//
|
|
// All Rights Reserved.
|
|
//
|
|
// http://www.softether.org/
|
|
//
|
|
// This stable branch is officially managed by Daiyuu Nobori, the owner of SoftEther VPN Project.
|
|
// Pull requests should be sent to the Developer Edition Master Repository on https://github.com/SoftEtherVPN/SoftEtherVPN
|
|
//
|
|
// License: The Apache License, Version 2.0
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// DISCLAIMER
|
|
// ==========
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
//
|
|
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
|
|
// JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
|
|
// DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
|
|
// JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
|
|
// AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
|
|
// SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
|
|
// OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
|
|
// AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
|
|
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
|
|
// JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
|
|
// ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
|
|
// PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
|
|
// LAW OR COURT RULE.
|
|
//
|
|
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
|
|
// A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
|
|
// RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
|
|
// COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
|
|
// DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
|
|
// CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
|
|
// COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
|
|
// WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
|
|
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
|
|
// AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
|
|
// DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
|
|
// AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
|
|
// PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
|
|
// PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
|
|
// LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
|
|
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
|
|
// STATEMENT FOR WARNING AND DISCLAIMER.
|
|
//
|
|
// READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
|
|
// SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
|
|
// LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
|
|
//
|
|
//
|
|
// SOURCE CODE CONTRIBUTION
|
|
// ------------------------
|
|
//
|
|
// Your contribution to SoftEther VPN Project is much appreciated.
|
|
// Please send patches to us through GitHub.
|
|
// Read the SoftEther VPN Patch Acceptance Policy in advance:
|
|
// http://www.softether.org/5-download/src/9.patch
|
|
//
|
|
//
|
|
// DEAR SECURITY EXPERTS
|
|
// ---------------------
|
|
//
|
|
// If you find a bug or a security vulnerability please kindly inform us
|
|
// about the problem immediately so that we can fix the security problem
|
|
// to protect a lot of users around the world as soon as possible.
|
|
//
|
|
// Our e-mail address for security reports is:
|
|
// softether-vpn-security [at] softether.org
|
|
//
|
|
// Please note that the above e-mail address is not a technical support
|
|
// inquiry address. If you need technical assistance, please visit
|
|
// http://www.softether.org/ and ask your question on the users forum.
|
|
//
|
|
// Thank you for your cooperation.
|
|
//
|
|
//
|
|
// NO MEMORY OR RESOURCE LEAKS
|
|
// ---------------------------
|
|
//
|
|
// The memory-leaks and resource-leaks verification under the stress
|
|
// test has been passed before release this source code.
|
|
|
|
|
|
// Encrypt.c
|
|
// Encryption and digital certification routine
|
|
|
|
#include <GlobalConst.h>
|
|
|
|
#define ENCRYPT_C
|
|
|
|
#define __WINCRYPT_H__
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/engine.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/pkcs7.h>
|
|
#include <openssl/pkcs12.h>
|
|
#include <openssl/rc4.h>
|
|
#include <openssl/md5.h>
|
|
#include <openssl/md4.h>
|
|
#include <openssl/hmac.h>
|
|
#include <openssl/sha.h>
|
|
#include <openssl/des.h>
|
|
#include <openssl/aes.h>
|
|
#include <openssl/dh.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/conf.h>
|
|
#include <openssl/x509v3.h>
|
|
#include <openssl/ocsp.h>
|
|
#include <openssl/ocsperr.h>
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
#include <openssl/provider.h>
|
|
#endif // OPENSSL_VERSION_NUMBER
|
|
|
|
#include <Mayaqua/Mayaqua.h>
|
|
|
|
#ifdef USE_INTEL_AESNI_LIBRARY
|
|
#include <intelaes/iaesni.h>
|
|
#endif // USE_INTEL_AESNI_LIBRARY
|
|
|
|
LOCK *openssl_lock = NULL;
|
|
|
|
int ssl_clientcert_index = 0;
|
|
|
|
LOCK **ssl_lock_obj = NULL;
|
|
UINT ssl_lock_num;
|
|
static bool openssl_inited = false;
|
|
static bool is_intel_aes_supported = false;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
static OSSL_PROVIDER* ossl_provider_legacy = NULL;
|
|
static OSSL_PROVIDER* ossl_provider_default = NULL;
|
|
#endif
|
|
|
|
static unsigned char *Internal_SHA0(const unsigned char *d, size_t n, unsigned char *md);
|
|
|
|
// For the callback function
|
|
typedef struct CB_PARAM
|
|
{
|
|
char *password;
|
|
} CB_PARAM;
|
|
|
|
|
|
LIST* BufToXList(BUF* b)
|
|
{
|
|
LIST* ret;
|
|
UINT mode = 0;
|
|
BUF* current_buf;
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
SeekBufToBegin(b);
|
|
|
|
ret = NewList(NULL);
|
|
|
|
current_buf = NewBuf();
|
|
|
|
while (true)
|
|
{
|
|
char* line = CfgReadNextLine(b);
|
|
|
|
if (line == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (mode == 0 && StrCmpi(line, "-----BEGIN CERTIFICATE-----") == 0)
|
|
{
|
|
mode = 1;
|
|
WriteBuf(current_buf, line, StrLen(line));
|
|
WriteBuf(current_buf, "\n", 1);
|
|
}
|
|
else if (mode == 1)
|
|
{
|
|
if (StrCmpi(line, "-----END CERTIFICATE-----") == 0)
|
|
{
|
|
mode = 0;
|
|
}
|
|
WriteBuf(current_buf, line, StrLen(line));
|
|
WriteBuf(current_buf, "\n", 1);
|
|
|
|
if (mode == 0)
|
|
{
|
|
X* x = BufToX(current_buf, true);
|
|
if (x != NULL)
|
|
{
|
|
Add(ret, x);
|
|
}
|
|
|
|
FreeBuf(current_buf);
|
|
current_buf = NewBuf();
|
|
}
|
|
}
|
|
|
|
Free(line);
|
|
}
|
|
|
|
FreeBuf(current_buf);
|
|
|
|
if (LIST_NUM(ret) == 0)
|
|
{
|
|
ReleaseList(ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void FreeXList(LIST* o)
|
|
{
|
|
UINT i;
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
X* x = LIST_DATA(o, i);
|
|
|
|
FreeX(x);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
bool CheckCertsAndKey(CERTS_AND_KEY* c)
|
|
{
|
|
X* x;
|
|
K* k;
|
|
if (c == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (LIST_NUM(c->CertList) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
x = LIST_DATA(c->CertList, 0);
|
|
k = c->Key;
|
|
|
|
return CheckXandK(x, k);
|
|
}
|
|
|
|
bool CertsAndKeyAlwaysUseCallback(char* sni_name, void* param)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CERTS_AND_KEY* CloneCertsAndKey(CERTS_AND_KEY* c)
|
|
{
|
|
CERTS_AND_KEY* ret;
|
|
if (c == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = NewCertsAndKeyFromObjects(c->CertList, c->Key, false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
UINT64 GetCertsAndKeyListHash(LIST* o)
|
|
{
|
|
UINT i;
|
|
UINT64 ret = 0;
|
|
if (o == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
CERTS_AND_KEY* c = LIST_DATA(o, i);
|
|
|
|
UINT64 hash = GetCertsAndKeyHash(c);
|
|
|
|
ret += hash;
|
|
|
|
ret *= GOLDEN_PRIME_NUMBER;
|
|
}
|
|
|
|
if (ret == 0) ret = 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void FreeCertsAndKeyList(LIST* o)
|
|
{
|
|
UINT i;
|
|
|
|
if (o == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
CERTS_AND_KEY* s = LIST_DATA(o, i);
|
|
|
|
ReleaseCertsAndKey(s);
|
|
}
|
|
|
|
ReleaseList(o);
|
|
}
|
|
|
|
LIST* CloneCertsAndKeyList(LIST* o)
|
|
{
|
|
LIST* ret;
|
|
UINT i;
|
|
|
|
if (o == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = NewList(NULL);
|
|
|
|
for (i = 0;i < LIST_NUM(o);i++)
|
|
{
|
|
CERTS_AND_KEY* s = LIST_DATA(o, i);
|
|
|
|
if (s != NULL)
|
|
{
|
|
CERTS_AND_KEY* d = s;
|
|
|
|
AddRef(d->Ref);
|
|
|
|
Add(ret, d);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
UINT64 GetCertsAndKeyHash(CERTS_AND_KEY* c)
|
|
{
|
|
UINT64 ret;
|
|
|
|
if (c == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ret = c->HashCache;
|
|
|
|
ret += (UINT64)c->DetermineUseCallback;
|
|
|
|
if (ret == 0) ret = 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
UINT64 CalcCertsAndKeyHashCache(CERTS_AND_KEY* c)
|
|
{
|
|
BUF* buf;
|
|
UINT i;
|
|
BUF *key_buf;
|
|
UCHAR hash[SHA1_SIZE] = CLEAN;
|
|
UINT64 ret;
|
|
|
|
if (c == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
buf = NewBuf();
|
|
|
|
for (i = 0;i < LIST_NUM(c->CertList);i++)
|
|
{
|
|
X* x = LIST_DATA(c->CertList, i);
|
|
UCHAR sha1[SHA1_SIZE] = CLEAN;
|
|
|
|
GetXDigest(x, sha1, true);
|
|
|
|
WriteBuf(buf, sha1, SHA1_SIZE);
|
|
}
|
|
|
|
key_buf = KToBuf(c->Key, true, NULL);
|
|
|
|
WriteBufBuf(buf, key_buf);
|
|
|
|
FreeBuf(key_buf);
|
|
|
|
HashSha1(hash, buf->Buf, buf->Size);
|
|
|
|
FreeBuf(buf);
|
|
|
|
ret = READ_UINT64(hash);
|
|
|
|
if (ret == 0) ret = 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void UpdateCertsAndKeyHashCacheAndCheckedState(CERTS_AND_KEY* c)
|
|
{
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
c->HashCache = CalcCertsAndKeyHashCache(c);
|
|
c->HasValidPrivateKey = CheckCertsAndKey(c);
|
|
}
|
|
|
|
CERTS_AND_KEY* NewCertsAndKeyFromDir(wchar_t* dir_name)
|
|
{
|
|
CERTS_AND_KEY* ret = NULL;
|
|
BUF* key_buf = NULL;
|
|
wchar_t key_fn[MAX_PATH] = CLEAN;
|
|
UINT i;
|
|
|
|
if (dir_name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(CERTS_AND_KEY));
|
|
|
|
ret->Ref = NewRef();
|
|
|
|
ret->CertList = NewListFast(NULL);
|
|
|
|
CombinePathW(key_fn, sizeof(key_fn), dir_name, L"cert.key");
|
|
key_buf = ReadDumpW(key_fn);
|
|
|
|
ret->Key = BufToK(key_buf, true, true, NULL);
|
|
if (ret->Key == NULL)
|
|
{
|
|
goto L_ERROR;
|
|
}
|
|
|
|
for (i = 0;;i++)
|
|
{
|
|
wchar_t cert_fn[MAX_PATH] = CLEAN;
|
|
wchar_t tmp[MAX_PATH] = CLEAN;
|
|
BUF* cert_buf;
|
|
X* x;
|
|
|
|
UniFormat(tmp, sizeof(tmp), L"cert_%04u.cer", i);
|
|
CombinePathW(cert_fn, sizeof(cert_fn), dir_name, tmp);
|
|
|
|
cert_buf = ReadDumpW(cert_fn);
|
|
if (cert_buf == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
x = BufToX(cert_buf, true);
|
|
|
|
if (x != NULL)
|
|
{
|
|
Add(ret->CertList, x);
|
|
}
|
|
|
|
FreeBuf(cert_buf);
|
|
}
|
|
|
|
if (LIST_NUM(ret->CertList) == 0)
|
|
{
|
|
goto L_ERROR;
|
|
}
|
|
|
|
FreeBuf(key_buf);
|
|
|
|
UpdateCertsAndKeyHashCacheAndCheckedState(ret);
|
|
|
|
return ret;
|
|
|
|
L_ERROR:
|
|
ReleaseCertsAndKey(ret);
|
|
FreeBuf(key_buf);
|
|
return NULL;
|
|
}
|
|
|
|
bool SaveCertsAndKeyToDir(CERTS_AND_KEY* c, wchar_t* dir)
|
|
{
|
|
wchar_t tmp[MAX_PATH] = CLEAN;
|
|
wchar_t tmp2[MAX_PATH] = CLEAN;
|
|
bool ret = true;
|
|
LIST* filename_list;
|
|
UINT count;
|
|
|
|
if (c == NULL || dir == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
filename_list = NewList(NULL);
|
|
|
|
MakeDirExW(dir);
|
|
|
|
// サーバーから受信した証明書情報の websocket_certs_cache ディレクトリへの書き込み
|
|
count = LIST_NUM(c->CertList);
|
|
|
|
if (count >= 1)
|
|
{
|
|
BUF* key_buf = KToBuf(c->Key, true, NULL);
|
|
if (key_buf != NULL && key_buf->Size >= 1)
|
|
{
|
|
UINT i;
|
|
for (i = 0;i < count;i++)
|
|
{
|
|
X* x = LIST_DATA(c->CertList, i);
|
|
if (x != NULL)
|
|
{
|
|
BUF* cert_buf = XToBuf(x, true);
|
|
if (cert_buf != NULL)
|
|
{
|
|
UniFormat(tmp2, sizeof(tmp2), L"cert_%04u.cer", i);
|
|
CombinePathW(tmp, sizeof(tmp), dir, tmp2);
|
|
|
|
if (DumpBufWIfNecessary(cert_buf, tmp) == false)
|
|
{
|
|
ret = false;
|
|
}
|
|
|
|
AddUniStrToUniStrList(filename_list, tmp2);
|
|
}
|
|
FreeBuf(cert_buf);
|
|
}
|
|
}
|
|
|
|
CombinePathW(tmp, sizeof(tmp), dir, L"cert.key");
|
|
if (DumpBufWIfNecessary(key_buf, tmp) == false)
|
|
{
|
|
ret = false;
|
|
}
|
|
}
|
|
FreeBuf(key_buf);
|
|
}
|
|
else
|
|
{
|
|
ret = false;
|
|
}
|
|
|
|
// websocket_certs_cache ディレクトリにある不要ファイルの削除
|
|
if (LIST_NUM(filename_list) >= 1)
|
|
{
|
|
DIRLIST* dirlist = EnumDirW(dir);
|
|
|
|
if (dirlist != NULL)
|
|
{
|
|
UINT i;
|
|
for (i = 0;i < dirlist->NumFiles;i++)
|
|
{
|
|
DIRENT* f = dirlist->File[i];
|
|
|
|
if (UniStartWith(f->FileNameW, L"cert_") && UniEndWith(f->FileNameW, L".cer"))
|
|
{
|
|
if (IsInListUniStr(filename_list, f->FileNameW) == false)
|
|
{
|
|
CombinePathW(tmp, sizeof(tmp), dir, f->FileNameW);
|
|
FileDeleteW(tmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeDir(dirlist);
|
|
}
|
|
|
|
FreeStrList(filename_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
CERTS_AND_KEY* NewCertsAndKeyFromObjectSingle(X* cert, K* key, bool fast)
|
|
{
|
|
LIST* cert_list;
|
|
CERTS_AND_KEY* ret;
|
|
if (cert == NULL || key == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
cert_list = NewList(NULL);
|
|
Add(cert_list, cert);
|
|
|
|
ret = NewCertsAndKeyFromObjects(cert_list, key, fast);
|
|
|
|
ReleaseList(cert_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
CERTS_AND_KEY* NewCertsAndKeyFromObjects(LIST* cert_list, K* key, bool fast)
|
|
{
|
|
UINT i;
|
|
UINT64 fast_hash = 1;
|
|
CERTS_AND_KEY* ret = NULL;
|
|
if (cert_list == NULL || LIST_NUM(cert_list) == 0 || key == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(CERTS_AND_KEY));
|
|
|
|
ret->Ref = NewRef();
|
|
|
|
ret->CertList = NewListFast(NULL);
|
|
|
|
if (fast == false)
|
|
{
|
|
ret->Key = CloneK(key);
|
|
}
|
|
else
|
|
{
|
|
ret->Key = CloneKFast(key);
|
|
|
|
fast_hash += (UINT64)(key->pkey);
|
|
fast_hash *= GOLDEN_PRIME_NUMBER;
|
|
}
|
|
|
|
if (ret->Key == NULL) goto L_ERROR;
|
|
|
|
for (i = 0;i < LIST_NUM(cert_list);i++)
|
|
{
|
|
X* clone_x;
|
|
X* x = LIST_DATA(cert_list, i);
|
|
if (x == NULL) goto L_ERROR;
|
|
|
|
if (fast == false)
|
|
{
|
|
clone_x = CloneX(x);
|
|
}
|
|
else
|
|
{
|
|
clone_x = CloneXFast(x);
|
|
fast_hash += (UINT64)(x->x509);
|
|
fast_hash *= GOLDEN_PRIME_NUMBER;
|
|
}
|
|
|
|
Add(ret->CertList, clone_x);
|
|
}
|
|
|
|
if (fast == false)
|
|
{
|
|
UpdateCertsAndKeyHashCacheAndCheckedState(ret);
|
|
}
|
|
else
|
|
{
|
|
ret->HashCache = fast_hash;
|
|
ret->HasValidPrivateKey = true;
|
|
}
|
|
|
|
return ret;
|
|
|
|
L_ERROR:
|
|
ReleaseCertsAndKey(ret);
|
|
return NULL;
|
|
}
|
|
|
|
CERTS_AND_KEY* NewCertsAndKeyFromMemory(LIST* cert_buf_list, BUF* key_buf)
|
|
{
|
|
UINT i;
|
|
CERTS_AND_KEY* ret = NULL;
|
|
if (cert_buf_list == NULL || LIST_NUM(cert_buf_list) == 0 || key_buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(CERTS_AND_KEY));
|
|
|
|
ret->Ref = NewRef();
|
|
|
|
ret->CertList = NewListFast(NULL);
|
|
|
|
ret->Key = BufToK(key_buf, true, true, NULL);
|
|
if (ret->Key == NULL) goto L_ERROR;
|
|
|
|
for (i = 0;i < LIST_NUM(cert_buf_list);i++)
|
|
{
|
|
BUF* b = LIST_DATA(cert_buf_list, i);
|
|
|
|
X* x = BufToX(b, true);
|
|
if (x == NULL) goto L_ERROR;
|
|
|
|
Add(ret->CertList, x);
|
|
}
|
|
|
|
UpdateCertsAndKeyHashCacheAndCheckedState(ret);
|
|
|
|
return ret;
|
|
|
|
L_ERROR:
|
|
ReleaseCertsAndKey(ret);
|
|
return NULL;
|
|
}
|
|
|
|
void ReleaseCertsAndKey(CERTS_AND_KEY* c)
|
|
{
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Release(c->Ref) == 0)
|
|
{
|
|
CleanupCertsAndKey(c);
|
|
}
|
|
}
|
|
|
|
void CleanupCertsAndKey(CERTS_AND_KEY* c)
|
|
{
|
|
UINT i;
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < LIST_NUM(c->CertList);i++)
|
|
{
|
|
X* x = LIST_DATA(c->CertList, i);
|
|
|
|
FreeX(x);
|
|
}
|
|
|
|
FreeK(c->Key);
|
|
|
|
ReleaseList(c->CertList);
|
|
|
|
Free(c);
|
|
}
|
|
|
|
// 証明書が特定のディレクトリの CRL によって無効化されているかどうか確認する
|
|
bool IsXRevoked(X *x)
|
|
{
|
|
char dirname[MAX_PATH];
|
|
UINT i;
|
|
bool ret = false;
|
|
DIRLIST *t;
|
|
// 引数チェック
|
|
if (x == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
GetExeDir(dirname, sizeof(dirname));
|
|
|
|
// CRL ファイルの検索
|
|
t = EnumDir(dirname);
|
|
|
|
for (i = 0;i < t->NumFiles;i++)
|
|
{
|
|
char *name = t->File[i]->FileName;
|
|
if (t->File[i]->Folder == false)
|
|
{
|
|
if (EndWith(name, ".crl"))
|
|
{
|
|
char filename[MAX_PATH];
|
|
X_CRL *r;
|
|
|
|
ConbinePath(filename, sizeof(filename), dirname, name);
|
|
|
|
r = FileToXCrl(filename);
|
|
|
|
if (r != NULL)
|
|
{
|
|
if (IsXRevokedByXCrl(x, r))
|
|
{
|
|
ret = true;
|
|
}
|
|
|
|
FreeXCrl(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeDir(t);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// 証明書が CRL によって無効化されているかどうか確認する
|
|
bool IsXRevokedByXCrl(X *x, X_CRL *r)
|
|
{
|
|
// 手抜きさん
|
|
return false;
|
|
}
|
|
|
|
// CRL の解放
|
|
void FreeXCrl(X_CRL *r)
|
|
{
|
|
// 引数チェック
|
|
if (r == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
X509_CRL_free(r->Crl);
|
|
|
|
Free(r);
|
|
}
|
|
|
|
// ファイルを CRL に変換
|
|
X_CRL *FileToXCrl(char *filename)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
X_CRL *ret = FileToXCrlW(filename_w);
|
|
|
|
Free(filename_w);
|
|
|
|
return ret;
|
|
}
|
|
X_CRL *FileToXCrlW(wchar_t *filename)
|
|
{
|
|
BUF *b;
|
|
X_CRL *r;
|
|
// 引数チェック
|
|
if (filename == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = ReadDumpW(filename);
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
r = BufToXCrl(b);
|
|
|
|
FreeBuf(b);
|
|
|
|
return r;
|
|
}
|
|
|
|
// バッファを CRL に変換
|
|
X_CRL *BufToXCrl(BUF *b)
|
|
{
|
|
X_CRL *r;
|
|
X509_CRL *x509crl;
|
|
BIO *bio;
|
|
// 引数チェック
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = BufToBio(b);
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x509crl = NULL;
|
|
|
|
if (d2i_X509_CRL_bio(bio, &x509crl) == NULL || x509crl == NULL)
|
|
{
|
|
FreeBio(bio);
|
|
return NULL;
|
|
}
|
|
|
|
r = ZeroMalloc(sizeof(X_CRL));
|
|
r->Crl = x509crl;
|
|
|
|
FreeBio(bio);
|
|
|
|
return r;
|
|
}
|
|
|
|
// Copied from t1_enc.c of OpenSSL
|
|
void Enc_tls1_P_hash(const EVP_MD *md, const unsigned char *sec, int sec_len,
|
|
const unsigned char *seed, int seed_len, unsigned char *out, int olen)
|
|
{
|
|
int chunk,n;
|
|
unsigned int j;
|
|
HMAC_CTX *ctx;
|
|
HMAC_CTX *ctx_tmp;
|
|
unsigned char A1[EVP_MAX_MD_SIZE];
|
|
unsigned int A1_len;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
ctx = HMAC_CTX_new();
|
|
ctx_tmp = HMAC_CTX_new();
|
|
#else
|
|
HMAC_CTX ctx_;
|
|
HMAC_CTX ctx_tmp_;
|
|
ctx = &ctx_;
|
|
ctx_tmp = &ctx_tmp_;
|
|
Zero(ctx, sizeof(HMAC_CTX));
|
|
Zero(ctx_tmp, sizeof(HMAC_CTX));
|
|
#endif
|
|
chunk=EVP_MD_size(md);
|
|
|
|
HMAC_Init_ex(ctx,sec,sec_len,md, NULL);
|
|
HMAC_Init_ex(ctx_tmp,sec,sec_len,md, NULL);
|
|
HMAC_Update(ctx,seed,seed_len);
|
|
HMAC_Final(ctx,A1,&A1_len);
|
|
|
|
n=0;
|
|
for (;;)
|
|
{
|
|
HMAC_Init_ex(ctx,NULL,0,NULL,NULL); /* re-init */
|
|
HMAC_Init_ex(ctx_tmp,NULL,0,NULL,NULL); /* re-init */
|
|
HMAC_Update(ctx,A1,A1_len);
|
|
HMAC_Update(ctx_tmp,A1,A1_len);
|
|
HMAC_Update(ctx,seed,seed_len);
|
|
|
|
if (olen > chunk)
|
|
{
|
|
HMAC_Final(ctx,out,&j);
|
|
out+=j;
|
|
olen-=j;
|
|
HMAC_Final(ctx_tmp,A1,&A1_len); /* calc the next A1 value */
|
|
}
|
|
else /* last one */
|
|
{
|
|
HMAC_Final(ctx,A1,&A1_len);
|
|
memcpy(out,A1,olen);
|
|
break;
|
|
}
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
HMAC_CTX_free(ctx);
|
|
HMAC_CTX_free(ctx_tmp);
|
|
#else
|
|
HMAC_CTX_cleanup(ctx);
|
|
HMAC_CTX_cleanup(ctx_tmp);
|
|
#endif
|
|
Zero (A1, sizeof(A1));
|
|
}
|
|
|
|
void Enc_tls1_PRF(unsigned char *label, int label_len, const unsigned char *sec,
|
|
int slen, unsigned char *out1, int olen)
|
|
{
|
|
const EVP_MD *md5 = EVP_md5();
|
|
const EVP_MD *sha1 = EVP_sha1();
|
|
int len,i;
|
|
const unsigned char *S1,*S2;
|
|
unsigned char *out2;
|
|
|
|
out2 = (unsigned char *) Malloc (olen);
|
|
|
|
len=slen/2;
|
|
S1=sec;
|
|
S2= &(sec[len]);
|
|
len+=(slen&1); /* add for odd, make longer */
|
|
|
|
|
|
Enc_tls1_P_hash(md5 ,S1,len,label,label_len,out1,olen);
|
|
Enc_tls1_P_hash(sha1,S2,len,label,label_len,out2,olen);
|
|
|
|
for (i=0; i<olen; i++)
|
|
out1[i]^=out2[i];
|
|
|
|
memset (out2, 0, olen);
|
|
Free(out2);
|
|
}
|
|
|
|
// Easy encryption
|
|
BUF *EasyEncrypt(BUF *src_buf)
|
|
{
|
|
UCHAR key[SHA1_SIZE];
|
|
BUF *tmp_data;
|
|
CRYPT *rc4;
|
|
BUF *ret;
|
|
// Validate arguments
|
|
if (src_buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Rand(key, SHA1_SIZE);
|
|
|
|
tmp_data = CloneBuf(src_buf);
|
|
|
|
rc4 = NewCrypt(key, SHA1_SIZE);
|
|
|
|
Encrypt(rc4, tmp_data->Buf, tmp_data->Buf, tmp_data->Size);
|
|
|
|
ret = NewBuf();
|
|
|
|
WriteBuf(ret, key, SHA1_SIZE);
|
|
WriteBufBuf(ret, tmp_data);
|
|
|
|
FreeCrypt(rc4);
|
|
FreeBuf(tmp_data);
|
|
|
|
SeekBufToBegin(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Easy decryption
|
|
BUF *EasyDecrypt(BUF *src_buf)
|
|
{
|
|
UCHAR key[SHA1_SIZE];
|
|
BUF *tmp_buf;
|
|
CRYPT *rc4;
|
|
// Validate arguments
|
|
if (src_buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
SeekBufToBegin(src_buf);
|
|
|
|
if (ReadBuf(src_buf, key, SHA1_SIZE) != SHA1_SIZE)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
tmp_buf = ReadRemainBuf(src_buf);
|
|
if (tmp_buf == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
rc4 = NewCrypt(key, SHA1_SIZE);
|
|
Encrypt(rc4, tmp_buf->Buf, tmp_buf->Buf, tmp_buf->Size);
|
|
FreeCrypt(rc4);
|
|
|
|
SeekBufToBegin(tmp_buf);
|
|
|
|
return tmp_buf;
|
|
}
|
|
|
|
// Calculation of HMAC (MD5)
|
|
void HMacMd5(void *dst, void *key, UINT key_size, void *data, UINT data_size)
|
|
{
|
|
UCHAR k[HMAC_BLOCK_SIZE];
|
|
UCHAR hash1[MD5_SIZE];
|
|
UCHAR data2[HMAC_BLOCK_SIZE];
|
|
MD5_CTX md5_ctx1;
|
|
UCHAR pad1[HMAC_BLOCK_SIZE];
|
|
UINT i;
|
|
// Validate arguments
|
|
if (dst == NULL || (key == NULL && key_size != 0) || (data == NULL && data_size != 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Creating a K
|
|
if (key_size <= HMAC_BLOCK_SIZE)
|
|
{
|
|
for (i = 0;i < key_size;i++)
|
|
{
|
|
pad1[i] = ((UCHAR *)key)[i] ^ 0x36;
|
|
}
|
|
for (i = key_size;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
pad1[i] = 0 ^ 0x36;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Zero(k, sizeof(k));
|
|
Hash(k, key, key_size, false);
|
|
|
|
for (i = 0;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
pad1[i] = k[i] ^ 0x36;
|
|
}
|
|
}
|
|
|
|
MD5_Init(&md5_ctx1);
|
|
MD5_Update(&md5_ctx1, pad1, sizeof(pad1));
|
|
MD5_Update(&md5_ctx1, data, data_size);
|
|
MD5_Final(hash1, &md5_ctx1);
|
|
|
|
// Generation of data 2
|
|
if (key_size <= HMAC_BLOCK_SIZE)
|
|
{
|
|
for (i = 0;i < key_size;i++)
|
|
{
|
|
data2[i] = ((UCHAR *)key)[i] ^ 0x5c;
|
|
}
|
|
for (i = key_size;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
data2[i] = 0 ^ 0x5c;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
data2[i] = k[i] ^ 0x5c;
|
|
}
|
|
}
|
|
|
|
MD5_Init(&md5_ctx1);
|
|
MD5_Update(&md5_ctx1, data2, HMAC_BLOCK_SIZE);
|
|
MD5_Update(&md5_ctx1, hash1, MD5_SIZE);
|
|
MD5_Final(dst, &md5_ctx1);
|
|
}
|
|
|
|
// Calculation of HMAC (SHA-1)
|
|
void HMacSha1(void *dst, void *key, UINT key_size, void *data, UINT data_size)
|
|
{
|
|
UCHAR k[HMAC_BLOCK_SIZE];
|
|
UCHAR hash1[SHA1_SIZE];
|
|
UCHAR data2[HMAC_BLOCK_SIZE];
|
|
SHA_CTX sha_ctx1;
|
|
UCHAR pad1[HMAC_BLOCK_SIZE];
|
|
UINT i;
|
|
// Validate arguments
|
|
if (dst == NULL || (key == NULL && key_size != 0) || (data == NULL && data_size != 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Creating a K
|
|
if (key_size <= HMAC_BLOCK_SIZE)
|
|
{
|
|
for (i = 0;i < key_size;i++)
|
|
{
|
|
pad1[i] = ((UCHAR *)key)[i] ^ 0x36;
|
|
}
|
|
for (i = key_size;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
pad1[i] = 0 ^ 0x36;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Zero(k, sizeof(k));
|
|
HashSha1(k, key, key_size);
|
|
|
|
for (i = 0;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
pad1[i] = k[i] ^ 0x36;
|
|
}
|
|
}
|
|
|
|
SHA1_Init(&sha_ctx1);
|
|
SHA1_Update(&sha_ctx1, pad1, sizeof(pad1));
|
|
SHA1_Update(&sha_ctx1, data, data_size);
|
|
SHA1_Final(hash1, &sha_ctx1);
|
|
|
|
// Generation of data 2
|
|
if (key_size <= HMAC_BLOCK_SIZE)
|
|
{
|
|
for (i = 0;i < key_size;i++)
|
|
{
|
|
data2[i] = ((UCHAR *)key)[i] ^ 0x5c;
|
|
}
|
|
for (i = key_size;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
data2[i] = 0 ^ 0x5c;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0;i < HMAC_BLOCK_SIZE;i++)
|
|
{
|
|
data2[i] = k[i] ^ 0x5c;
|
|
}
|
|
}
|
|
|
|
SHA1_Init(&sha_ctx1);
|
|
SHA1_Update(&sha_ctx1, data2, HMAC_BLOCK_SIZE);
|
|
SHA1_Update(&sha_ctx1, hash1, SHA1_SIZE);
|
|
SHA1_Final(dst, &sha_ctx1);
|
|
}
|
|
|
|
// Calculate the HMAC
|
|
void MdProcess(MD *md, void *dest, void *src, UINT size)
|
|
{
|
|
int r;
|
|
// Validate arguments
|
|
if (md == NULL || dest == NULL || (src != NULL && size == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
HMAC_Init_ex(md->Ctx, NULL, 0, NULL, NULL);
|
|
HMAC_Update(md->Ctx, src, size);
|
|
|
|
r = 0;
|
|
HMAC_Final(md->Ctx, dest, &r);
|
|
}
|
|
|
|
// Set the key to the message digest object
|
|
void SetMdKey(MD *md, void *key, UINT key_size)
|
|
{
|
|
// Validate arguments
|
|
if (md == NULL || (key != NULL && key_size == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
HMAC_Init_ex(md->Ctx, key, key_size, (const EVP_MD *)md->Md, NULL);
|
|
}
|
|
|
|
// Creating a message digest object
|
|
MD *NewMd(char *name)
|
|
{
|
|
MD *m;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
m = ZeroMalloc(sizeof(MD));
|
|
|
|
StrCpy(m->Name, sizeof(m->Name), name);
|
|
m->Md = (const struct evp_md_st *)EVP_get_digestbyname(name);
|
|
if (m->Md == NULL)
|
|
{
|
|
FreeMd(m);
|
|
return NULL;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
m->Ctx = HMAC_CTX_new();
|
|
#else
|
|
m->Ctx = ZeroMalloc(sizeof(struct hmac_ctx_st));
|
|
HMAC_CTX_init(m->Ctx);
|
|
#endif
|
|
|
|
m->Size = EVP_MD_size((const EVP_MD *)m->Md);
|
|
|
|
return m;
|
|
}
|
|
|
|
// Release of the message digest object
|
|
void FreeMd(MD *md)
|
|
{
|
|
// Validate arguments
|
|
if (md == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (md->Ctx != NULL)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
HMAC_CTX_free(md->Ctx);
|
|
#else
|
|
HMAC_CTX_cleanup(md->Ctx);
|
|
Free(md->Ctx);
|
|
#endif
|
|
}
|
|
|
|
Free(md);
|
|
}
|
|
|
|
// Creating a cipher object
|
|
CIPHER *NewCipher(char *name)
|
|
{
|
|
CIPHER *c;
|
|
// Validate arguments
|
|
if (name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
c = ZeroMalloc(sizeof(CIPHER));
|
|
|
|
StrCpy(c->Name, sizeof(c->Name), name);
|
|
|
|
if (StrCmpi(name, "[null-cipher]") == 0 ||
|
|
StrCmpi(name, "NULL") == 0 ||
|
|
IsEmptyStr(name))
|
|
{
|
|
c->IsNullCipher = true;
|
|
return c;
|
|
}
|
|
|
|
c->Cipher = EVP_get_cipherbyname(c->Name);
|
|
if (c->Cipher == NULL)
|
|
{
|
|
FreeCipher(c);
|
|
return NULL;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
c->Ctx = EVP_CIPHER_CTX_new();
|
|
#else
|
|
c->Ctx = ZeroMalloc(sizeof(struct evp_cipher_ctx_st));
|
|
EVP_CIPHER_CTX_init(c->Ctx);
|
|
#endif
|
|
|
|
c->IsAeadCipher = (EVP_CIPHER_flags(c->Cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) != 0;
|
|
c->BlockSize = EVP_CIPHER_block_size(c->Cipher);
|
|
c->KeySize = EVP_CIPHER_key_length(c->Cipher);
|
|
c->IvSize = EVP_CIPHER_iv_length(c->Cipher);
|
|
|
|
return c;
|
|
}
|
|
|
|
// Set the key to the cipher object
|
|
void SetCipherKey(CIPHER *c, void *key, bool enc)
|
|
{
|
|
// Validate arguments
|
|
if (c == NULL || key == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->IsNullCipher == false)
|
|
{
|
|
if (c->Ctx != NULL)
|
|
{
|
|
EVP_CipherInit(c->Ctx, c->Cipher, key, NULL, enc);
|
|
}
|
|
}
|
|
|
|
c->Encrypt = enc;
|
|
}
|
|
|
|
// Process encryption / decryption
|
|
UINT CipherProcess(CIPHER *c, void *iv, void *dest, void *src, UINT size)
|
|
{
|
|
int r = size;
|
|
int r2 = 0;
|
|
if (c != NULL && c->IsNullCipher)
|
|
{
|
|
if (dest != src)
|
|
{
|
|
Copy(dest, src, size);
|
|
}
|
|
return size;
|
|
}
|
|
// Validate arguments
|
|
if (c == NULL || iv == NULL || dest == NULL || src == NULL || size == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (EVP_CipherInit(c->Ctx, NULL, NULL, iv, c->Encrypt) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (EVP_CipherUpdate(c->Ctx, dest, &r, src, size) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (EVP_CipherFinal(c->Ctx, ((UCHAR *)dest) + (UINT)r, &r2) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return r + r2;
|
|
}
|
|
|
|
// Process encryption / decryption (AEAD)
|
|
UINT CipherProcessAead(CIPHER *c, void *iv, void *tag, UINT tag_size, void *dest, void *src, UINT src_size, void *aad, UINT aad_size)
|
|
{
|
|
int r = src_size;
|
|
int r2 = 0;
|
|
// Validate arguments
|
|
if (c == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (c->IsNullCipher)
|
|
{
|
|
Copy(dest, src, src_size);
|
|
return src_size;
|
|
}
|
|
else if (c->IsAeadCipher == false || iv == NULL || tag == NULL || tag_size == 0 || dest == NULL || src == NULL || src_size == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (EVP_CipherInit_ex(c->Ctx, NULL, NULL, NULL, iv, c->Encrypt) == false)
|
|
{
|
|
Debug("CipherProcessAead(): EVP_CipherInit_ex() failed with error: %s\n", ERR_error_string(ERR_get_error(),NULL));
|
|
return 0;
|
|
}
|
|
|
|
if (c->Encrypt == false)
|
|
{
|
|
if (EVP_CIPHER_CTX_ctrl(c->Ctx, EVP_CTRL_AEAD_SET_TAG, tag_size, tag) == false)
|
|
{
|
|
Debug("CipherProcessAead(): EVP_CIPHER_CTX_ctrl() failed to set the tag!\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (aad != NULL && aad_size != 0)
|
|
{
|
|
if (EVP_CipherUpdate(c->Ctx, NULL, &r, aad, aad_size) == false)
|
|
{
|
|
Debug("CipherProcessAead(): EVP_CipherUpdate() failed with error: %s\n", ERR_error_string(ERR_get_error(),NULL));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (EVP_CipherUpdate(c->Ctx, dest, &r, src, src_size) == false)
|
|
{
|
|
Debug("CipherProcessAead(): EVP_CipherUpdate() failed with error: %s\n", ERR_error_string(ERR_get_error(),NULL));
|
|
return 0;
|
|
}
|
|
|
|
if (EVP_CipherFinal_ex(c->Ctx, ((UCHAR *)dest) + (UINT)r, &r2) == false)
|
|
{
|
|
Debug("CipherProcessAead(): EVP_CipherFinal_ex() failed with error: %s\n", ERR_error_string(ERR_get_error(),NULL));
|
|
return 0;
|
|
}
|
|
|
|
if (c->Encrypt)
|
|
{
|
|
if (EVP_CIPHER_CTX_ctrl(c->Ctx, EVP_CTRL_AEAD_GET_TAG, tag_size, tag) == false)
|
|
{
|
|
Debug("CipherProcessAead(): EVP_CIPHER_CTX_ctrl() failed to get the tag!\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return r + r2;
|
|
}
|
|
|
|
// Release of the cipher object
|
|
void FreeCipher(CIPHER *c)
|
|
{
|
|
// Validate arguments
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (c->Ctx != NULL)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
EVP_CIPHER_CTX_free(c->Ctx);
|
|
#else
|
|
EVP_CIPHER_CTX_cleanup(c->Ctx);
|
|
Free(c->Ctx);
|
|
#endif
|
|
}
|
|
|
|
Free(c);
|
|
}
|
|
|
|
// Convert the buffer to the public key
|
|
K *RsaBinToPublic(void *data, UINT size)
|
|
{
|
|
RSA *rsa;
|
|
K *k;
|
|
BIO *bio;
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BIGNUM *e, *n;
|
|
#endif
|
|
// Validate arguments
|
|
if (data == NULL || size < 4)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
rsa = RSA_new();
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
e = BN_new();
|
|
BN_set_word(e, RSA_F4);
|
|
|
|
n = BinToBigNum(data, size);
|
|
|
|
RSA_set0_key(rsa, n, e, NULL);
|
|
#else
|
|
if (rsa->e != NULL)
|
|
{
|
|
BN_free(rsa->e);
|
|
}
|
|
|
|
rsa->e = BN_new();
|
|
BN_set_word(rsa->e, RSA_F4);
|
|
|
|
if (rsa->n != NULL)
|
|
{
|
|
BN_free(rsa->n);
|
|
}
|
|
|
|
rsa->n = BinToBigNum(data, size);
|
|
#endif
|
|
|
|
bio = NewBio();
|
|
LockOpenSSL();
|
|
{
|
|
i2d_RSA_PUBKEY_bio(bio, rsa);
|
|
}
|
|
UnlockOpenSSL();
|
|
BIO_seek(bio, 0);
|
|
k = BioToK(bio, false, false, NULL);
|
|
FreeBio(bio);
|
|
|
|
RSA_free(rsa);
|
|
|
|
return k;
|
|
}
|
|
|
|
// Convert the public key to a buffer
|
|
BUF *RsaPublicToBuf(K *k)
|
|
{
|
|
BUF *b;
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
RSA *rsa;
|
|
const BIGNUM *n;
|
|
#endif
|
|
// Validate arguments
|
|
if (k == NULL || k->pkey == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
rsa = (RSA*)EVP_PKEY_get0_RSA(k->pkey);
|
|
if (rsa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
RSA_get0_key(rsa, &n, NULL, NULL);
|
|
if (n == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = BigNumToBuf(n);
|
|
#else
|
|
if (k->pkey->pkey.rsa == NULL || k->pkey->pkey.rsa->n == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = BigNumToBuf(k->pkey->pkey.rsa->n);
|
|
#endif
|
|
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
// Convert the public key to a binary
|
|
void RsaPublicToBin(K *k, void *data)
|
|
{
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (data == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
b = RsaPublicToBuf(k);
|
|
if (b == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(data, b->Buf, b->Size);
|
|
|
|
FreeBuf(b);
|
|
}
|
|
|
|
// Get public key size
|
|
UINT RsaPublicSize(K *k)
|
|
{
|
|
BUF *b;
|
|
UINT ret;
|
|
|
|
b = RsaPublicToBuf(k);
|
|
if (b == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ret = b->Size;
|
|
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Stupid test
|
|
void CertTest2()
|
|
{
|
|
}
|
|
|
|
// Yagi test
|
|
void CertTest()
|
|
{
|
|
}
|
|
|
|
// Test function related to certificate
|
|
void CertTest_()
|
|
{
|
|
}
|
|
|
|
// Hash a pointer to a 32-bit
|
|
UINT HashPtrToUINT(void *p)
|
|
{
|
|
UCHAR hash_data[SHA256_SIZE];
|
|
UCHAR hash_src[CANARY_RAND_SIZE + sizeof(void *)];
|
|
UINT ret;
|
|
// Validate arguments
|
|
if (p == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Zero(hash_src, sizeof(hash_src));
|
|
Copy(hash_src + 0, GetCanaryRand(CANARY_RAND_ID_PTR_KEY_HASH), CANARY_RAND_SIZE);
|
|
Copy(hash_src + CANARY_RAND_SIZE, p, sizeof(void *));
|
|
|
|
HashSha256(hash_data, hash_src, sizeof(hash_src));
|
|
|
|
Copy(&ret, hash_data, sizeof(ret));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Copy of the NAME
|
|
NAME *CopyName(NAME *n)
|
|
{
|
|
// Validate arguments
|
|
if (n == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return NewName(n->CommonName, n->Organization, n->Unit,
|
|
n->Country, n->State, n->Local);
|
|
}
|
|
|
|
// Convert a BIGNUM to a string
|
|
char *BigNumToStr(BIGNUM *bn)
|
|
{
|
|
BIO *bio;
|
|
BUF *b;
|
|
char *ret;
|
|
// Validate arguments
|
|
if (bn == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = NewBio();
|
|
|
|
BN_print(bio, bn);
|
|
|
|
b = BioToBuf(bio);
|
|
|
|
FreeBio(bio);
|
|
|
|
ret = ZeroMalloc(b->Size + 1);
|
|
Copy(ret, b->Buf, b->Size);
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Convert the binary to the BIGNUM
|
|
BIGNUM *BinToBigNum(void *data, UINT size)
|
|
{
|
|
BIGNUM *bn;
|
|
// Validate arguments
|
|
if (data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bn = BN_new();
|
|
BN_bin2bn(data, size, bn);
|
|
|
|
return bn;
|
|
}
|
|
|
|
// Convert the buffer to a BIGNUM
|
|
BIGNUM *BufToBigNum(BUF *b)
|
|
{
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return BinToBigNum(b->Buf, b->Size);
|
|
}
|
|
|
|
// Convert a BIGNUM to a buffer
|
|
BUF *BigNumToBuf(const BIGNUM *bn)
|
|
{
|
|
UINT size;
|
|
UCHAR *tmp;
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (bn == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
size = BN_num_bytes(bn);
|
|
tmp = ZeroMalloc(size);
|
|
BN_bn2bin(bn, tmp);
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, tmp, size);
|
|
Free(tmp);
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Initialization of the lock of OpenSSL
|
|
void OpenSSL_InitLock()
|
|
{
|
|
UINT i;
|
|
|
|
// Initialization of the lock object
|
|
ssl_lock_num = CRYPTO_num_locks();
|
|
ssl_lock_obj = Malloc(sizeof(LOCK *) * ssl_lock_num);
|
|
for (i = 0;i < ssl_lock_num;i++)
|
|
{
|
|
ssl_lock_obj[i] = NewLock();
|
|
}
|
|
|
|
// Setting the lock function
|
|
CRYPTO_set_locking_callback(OpenSSL_Lock);
|
|
CRYPTO_set_id_callback(OpenSSL_Id);
|
|
}
|
|
|
|
// Release of the lock of OpenSSL
|
|
void OpenSSL_FreeLock()
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0;i < ssl_lock_num;i++)
|
|
{
|
|
DeleteLock(ssl_lock_obj[i]);
|
|
}
|
|
Free(ssl_lock_obj);
|
|
ssl_lock_obj = NULL;
|
|
|
|
CRYPTO_set_locking_callback(NULL);
|
|
CRYPTO_set_id_callback(NULL);
|
|
}
|
|
|
|
// Lock function for OpenSSL
|
|
void OpenSSL_Lock(int mode, int n, const char *file, int line)
|
|
{
|
|
LOCK *lock = ssl_lock_obj[n];
|
|
|
|
if (mode & CRYPTO_LOCK)
|
|
{
|
|
// Lock
|
|
Lock(lock);
|
|
}
|
|
else
|
|
{
|
|
// Unlock
|
|
Unlock(lock);
|
|
}
|
|
}
|
|
|
|
// Return the thread ID
|
|
unsigned long OpenSSL_Id(void)
|
|
{
|
|
return (unsigned long)ThreadId();
|
|
}
|
|
|
|
// Get the display name of the certificate
|
|
void GetPrintNameFromX(wchar_t *str, UINT size, X *x)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetPrintNameFromName(str, size, x->subject_name);
|
|
}
|
|
void GetPrintNameFromXA(char *str, UINT size, X *x)
|
|
{
|
|
wchar_t tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL || x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetPrintNameFromX(tmp, sizeof(tmp), x);
|
|
|
|
UniToStr(str, size, tmp);
|
|
}
|
|
void GetAllNameFromXEx(wchar_t *str, UINT size, X *x)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL || str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetAllNameFromNameEx(str, size, x->subject_name);
|
|
}
|
|
void GetAllNameFromXExA(char *str, UINT size, X *x)
|
|
{
|
|
wchar_t tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL || x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetAllNameFromXEx(tmp, sizeof(tmp), x);
|
|
|
|
UniToStr(str, size, tmp);
|
|
}
|
|
|
|
// Get the display name from NAME
|
|
void GetPrintNameFromName(wchar_t *str, UINT size, NAME *name)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (name->CommonName != NULL)
|
|
{
|
|
UniStrCpy(str, size, name->CommonName);
|
|
}
|
|
else if (name->Organization != NULL)
|
|
{
|
|
UniStrCpy(str, size, name->Organization);
|
|
}
|
|
else if (name->Unit != NULL)
|
|
{
|
|
UniStrCpy(str, size, name->Unit);
|
|
}
|
|
else if (name->State != NULL)
|
|
{
|
|
UniStrCpy(str, size, name->State);
|
|
}
|
|
else if (name->Local != NULL)
|
|
{
|
|
UniStrCpy(str, size, name->Local);
|
|
}
|
|
else if (name->Country != NULL)
|
|
{
|
|
UniStrCpy(str, size, name->Country);
|
|
}
|
|
else
|
|
{
|
|
UniStrCpy(str, size, L"untitled");
|
|
}
|
|
}
|
|
|
|
// Get all the name strings from the certificate
|
|
void GetAllNameFromX(wchar_t *str, UINT size, X *x)
|
|
{
|
|
UCHAR md5[MD5_SIZE], sha1[SHA1_SIZE];
|
|
char tmp1[MD5_SIZE * 3 + 8], tmp2[SHA1_SIZE * 3 + 8];
|
|
wchar_t tmp3[sizeof(tmp1) + sizeof(tmp2) + 64];
|
|
// Validate arguments
|
|
if (str == NULL || x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetAllNameFromName(str, size, x->subject_name);
|
|
|
|
if (x->serial != NULL && x->serial->size >= 1)
|
|
{
|
|
char tmp[128];
|
|
wchar_t tmp2[128];
|
|
|
|
BinToStr(tmp, sizeof(tmp), x->serial->data, x->serial->size);
|
|
UniFormat(tmp2, sizeof(tmp2), L", SERIAL=\"%S\"", tmp);
|
|
|
|
UniStrCat(str, size, tmp2);
|
|
}
|
|
|
|
// Digest value
|
|
GetXDigest(x, md5, false);
|
|
GetXDigest(x, sha1, true);
|
|
|
|
BinToStr(tmp1, sizeof(tmp1), md5, MD5_SIZE);
|
|
BinToStr(tmp2, sizeof(tmp2), sha1, SHA1_SIZE);
|
|
|
|
UniFormat(tmp3, sizeof(tmp3), L" (Digest: MD5=\"%S\", SHA1=\"%S\")", tmp1, tmp2);
|
|
UniStrCat(str, size, tmp3);
|
|
}
|
|
void GetAllNameFromA(char *str, UINT size, X *x)
|
|
{
|
|
wchar_t tmp[MAX_SIZE];
|
|
// Validate arguments
|
|
if (str == NULL || x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetAllNameFromX(tmp, sizeof(tmp), x);
|
|
UniToStr(str, size, tmp);
|
|
}
|
|
|
|
// Get the all name strings from NAME
|
|
void GetAllNameFromName(wchar_t *str, UINT size, NAME *name)
|
|
{
|
|
UniStrCpy(str, size, L"");
|
|
// Validate arguments
|
|
if (str == NULL || name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (name->CommonName != NULL)
|
|
{
|
|
UniFormat(str, size, L"%sCN=%s, ", str, name->CommonName);
|
|
}
|
|
if (name->Organization != NULL)
|
|
{
|
|
UniFormat(str, size, L"%sO=%s, ", str, name->Organization);
|
|
}
|
|
if (name->Unit != NULL)
|
|
{
|
|
UniFormat(str, size, L"%sOU=%s, ", str, name->Unit);
|
|
}
|
|
if (name->State != NULL)
|
|
{
|
|
UniFormat(str, size, L"%sS=%s, ", str, name->State);
|
|
}
|
|
if (name->Local != NULL)
|
|
{
|
|
UniFormat(str, size, L"%sL=%s, ", str, name->Local);
|
|
}
|
|
if (name->Country != NULL)
|
|
{
|
|
UniFormat(str, size, L"%sC=%s, ", str, name->Country);
|
|
}
|
|
|
|
if (UniStrLen(str) >= 3)
|
|
{
|
|
UINT len = UniStrLen(str);
|
|
if (str[len - 2] == L',' &&
|
|
str[len - 1] == L' ')
|
|
{
|
|
str[len - 2] = 0;
|
|
}
|
|
}
|
|
}
|
|
void GetAllNameFromNameEx(wchar_t *str, UINT size, NAME *name)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || name == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UniStrCpy(str, size, L"");
|
|
if (name->CommonName != NULL)
|
|
{
|
|
UniFormat(str, size, L"%s%s, ", str, name->CommonName);
|
|
}
|
|
if (name->Organization != NULL)
|
|
{
|
|
UniFormat(str, size, L"%s%s, ", str, name->Organization);
|
|
}
|
|
if (name->Unit != NULL)
|
|
{
|
|
UniFormat(str, size, L"%s%s, ", str, name->Unit);
|
|
}
|
|
if (name->State != NULL)
|
|
{
|
|
UniFormat(str, size, L"%s%s, ", str, name->State);
|
|
}
|
|
if (name->Local != NULL)
|
|
{
|
|
UniFormat(str, size, L"%s%s, ", str, name->Local);
|
|
}
|
|
if (name->Country != NULL)
|
|
{
|
|
UniFormat(str, size, L"%s%s, ", str, name->Country);
|
|
}
|
|
|
|
if (UniStrLen(str) >= 3)
|
|
{
|
|
UINT len = UniStrLen(str);
|
|
if (str[len - 2] == L',' &&
|
|
str[len - 1] == L' ')
|
|
{
|
|
str[len - 2] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
K* CloneKFast(K* k)
|
|
{
|
|
K* ret;
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(K));
|
|
|
|
ret->private_key = k->private_key;
|
|
ret->pkey = k->pkey;
|
|
|
|
if (ret->pkey != NULL)
|
|
{
|
|
EVP_PKEY_up_ref(ret->pkey);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Clone of the key
|
|
K *CloneK(K *k)
|
|
{
|
|
BUF *b;
|
|
K *ret;
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = KToBuf(k, false, NULL);
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = BufToK(b, k->private_key, false, NULL);
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
X* CloneXFast(X* x)
|
|
{
|
|
X* ret;
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = ZeroMalloc(sizeof(X));
|
|
|
|
ret->issuer_name = CopyName(x->issuer_name);
|
|
ret->subject_name = CopyName(x->subject_name);
|
|
ret->root_cert = x->root_cert;
|
|
ret->notBefore = x->notBefore;
|
|
ret->notAfter = x->notAfter;
|
|
ret->serial = CloneXSerial(x->serial);
|
|
ret->do_not_free = false;
|
|
ret->is_compatible_bit = x->is_compatible_bit;
|
|
ret->bits = x->bits;
|
|
ret->has_basic_constraints = x->has_basic_constraints;
|
|
StrCpy(ret->issuer_url, sizeof(ret->issuer_url), x->issuer_url);
|
|
|
|
ret->x509 = x->x509;
|
|
|
|
if (ret->x509 != NULL)
|
|
{
|
|
X509_up_ref(ret->x509);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Clone of certificate
|
|
X *CloneX(X *x)
|
|
{
|
|
BUF *b;
|
|
X *ret;
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = XToBuf(x, false);
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ret = BufToX(b, false);
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Generate a P12
|
|
P12 *NewP12(X *x, K *k, char *password)
|
|
{
|
|
PKCS12 *pkcs12;
|
|
P12 *p12;
|
|
// Validate arguments
|
|
if (x == NULL || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (password && StrLen(password) == 0)
|
|
{
|
|
password = NULL;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
pkcs12 = PKCS12_create(password, NULL, k->pkey, x->x509, NULL, 0, 0, 0, 0, 0);
|
|
if (pkcs12 == NULL)
|
|
{
|
|
UnlockOpenSSL();
|
|
return NULL;
|
|
}
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
p12 = PKCS12ToP12(pkcs12);
|
|
|
|
return p12;
|
|
}
|
|
|
|
// Check whether the P12 is encrypted
|
|
bool IsEncryptedP12(P12 *p12)
|
|
{
|
|
X *x;
|
|
K *k;
|
|
// Validate arguments
|
|
if (p12 == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ParseP12(p12, &x, &k, NULL) == true)
|
|
{
|
|
FreeX(x);
|
|
FreeK(k);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Extract the X and the K from the P12
|
|
bool ParseP12(P12 *p12, X **x, K **k, char *password)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
X509 *x509;
|
|
// Validate arguments
|
|
if (p12 == NULL || x == NULL || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (password && StrLen(password) == 0)
|
|
{
|
|
password = NULL;
|
|
}
|
|
if (password == NULL)
|
|
{
|
|
password = "";
|
|
}
|
|
|
|
// Password confirmation
|
|
LockOpenSSL();
|
|
{
|
|
if (PKCS12_verify_mac(p12->pkcs12, password, -1) == false &&
|
|
PKCS12_verify_mac(p12->pkcs12, NULL, -1) == false)
|
|
{
|
|
UnlockOpenSSL();
|
|
return false;
|
|
}
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
// Extraction
|
|
LockOpenSSL();
|
|
{
|
|
if (PKCS12_parse(p12->pkcs12, password, &pkey, &x509, NULL) == false)
|
|
{
|
|
if (PKCS12_parse(p12->pkcs12, NULL, &pkey, &x509, NULL) == false)
|
|
{
|
|
UnlockOpenSSL();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
// Conversion
|
|
*x = X509ToX(x509);
|
|
|
|
if (*x == NULL)
|
|
{
|
|
FreePKey(pkey);
|
|
return false;
|
|
}
|
|
|
|
*k = ZeroMalloc(sizeof(K));
|
|
(*k)->private_key = true;
|
|
(*k)->pkey = pkey;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Write the P12 to a file
|
|
bool P12ToFile(P12 *p12, char *filename)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
bool ret = P12ToFileW(p12, filename_w);
|
|
|
|
return ret;
|
|
}
|
|
bool P12ToFileW(P12 *p12, wchar_t *filename)
|
|
{
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (p12 == NULL || filename == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
b = P12ToBuf(p12);
|
|
if (b == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (DumpBufW(b, filename) == false)
|
|
{
|
|
FreeBuf(b);
|
|
return false;
|
|
}
|
|
|
|
FreeBuf(b);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Read a P12 from the file
|
|
P12 *FileToP12(char *filename)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
P12 *ret = FileToP12W(filename_w);
|
|
|
|
Free(filename_w);
|
|
|
|
return ret;
|
|
}
|
|
P12 *FileToP12W(wchar_t *filename)
|
|
{
|
|
BUF *b;
|
|
P12 *p12;
|
|
// Validate arguments
|
|
if (filename == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = ReadDumpW(filename);
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
p12 = BufToP12(b);
|
|
FreeBuf(b);
|
|
|
|
return p12;
|
|
}
|
|
|
|
// Release of P12
|
|
void FreeP12(P12 *p12)
|
|
{
|
|
// Validate arguments
|
|
if (p12 == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreePKCS12(p12->pkcs12);
|
|
Free(p12);
|
|
}
|
|
|
|
// Release of PKCS12
|
|
void FreePKCS12(PKCS12 *pkcs12)
|
|
{
|
|
// Validate arguments
|
|
if (pkcs12 == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PKCS12_free(pkcs12);
|
|
}
|
|
|
|
// Converted the P12 to a BUF
|
|
BUF *P12ToBuf(P12 *p12)
|
|
{
|
|
BIO *bio;
|
|
BUF *buf;
|
|
// Validate arguments
|
|
if (p12 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = P12ToBio(p12);
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
buf = BioToBuf(bio);
|
|
FreeBio(bio);
|
|
|
|
SeekBuf(buf, 0, 0);
|
|
|
|
return buf;
|
|
}
|
|
|
|
// Converted the P12 to a BIO
|
|
BIO *P12ToBio(P12 *p12)
|
|
{
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (p12 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = NewBio();
|
|
LockOpenSSL();
|
|
{
|
|
i2d_PKCS12_bio(bio, p12->pkcs12);
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
return bio;
|
|
}
|
|
|
|
// Read the P12 from the BUF
|
|
P12 *BufToP12(BUF *b)
|
|
{
|
|
P12 *p12;
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = BufToBio(b);
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
p12 = BioToP12(bio);
|
|
FreeBio(bio);
|
|
|
|
return p12;
|
|
}
|
|
|
|
// Read the P12 from the BIO
|
|
P12 *BioToP12(BIO *bio)
|
|
{
|
|
PKCS12 *pkcs12;
|
|
// Validate arguments
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Conversion
|
|
LockOpenSSL();
|
|
{
|
|
pkcs12 = d2i_PKCS12_bio(bio, NULL);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (pkcs12 == NULL)
|
|
{
|
|
// Failure
|
|
return NULL;
|
|
}
|
|
|
|
return PKCS12ToP12(pkcs12);
|
|
}
|
|
|
|
// Generate a P12 from a PKCS12
|
|
P12 *PKCS12ToP12(PKCS12 *pkcs12)
|
|
{
|
|
P12 *p12;
|
|
// Validate arguments
|
|
if (pkcs12 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
p12 = ZeroMalloc(sizeof(P12));
|
|
p12->pkcs12 = pkcs12;
|
|
|
|
return p12;
|
|
}
|
|
|
|
// Convert a binary to a string
|
|
char *ByteToStr(BYTE *src, UINT src_size)
|
|
{
|
|
UINT size;
|
|
char *dst;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (src == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
size = MAX(src_size * 3, 1);
|
|
dst = Malloc(size);
|
|
dst[size - 1] = 0;
|
|
for (i = 0;i < src_size;i++)
|
|
{
|
|
char tmp[3];
|
|
Format(tmp, sizeof(tmp), "%02x", src[i]);
|
|
dst[i * 3 + 0] = tmp[0];
|
|
dst[i * 3 + 1] = tmp[1];
|
|
dst[i * 3 + 2] = ((i == (src_size - 1) ? 0 : ' '));
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
// Release of X_SERIAL
|
|
void FreeXSerial(X_SERIAL *serial)
|
|
{
|
|
// Validate arguments
|
|
if (serial == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Free(serial->data);
|
|
Free(serial);
|
|
}
|
|
|
|
// Comparison of X_SERIAL
|
|
bool CompareXSerial(X_SERIAL *s1, X_SERIAL *s2)
|
|
{
|
|
// Validate arguments
|
|
if (s1 == NULL || s2 == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (s1->size != s2->size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Cmp(s1->data, s2->data, s1->size) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Copy of X_SERIAL
|
|
X_SERIAL *CloneXSerial(X_SERIAL *src)
|
|
{
|
|
X_SERIAL *s;
|
|
// Validate arguments
|
|
if (src == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
s = ZeroMalloc(sizeof(X_SERIAL));
|
|
s->data = ZeroMalloc(src->size);
|
|
Copy(s->data, src->data, src->size);
|
|
s->size = src->size;
|
|
|
|
return s;
|
|
}
|
|
|
|
// Initialization of X_SERIAL
|
|
X_SERIAL *NewXSerial(void *data, UINT size)
|
|
{
|
|
X_SERIAL *serial;
|
|
UCHAR *buf = (UCHAR *)data;
|
|
UINT i;
|
|
// Validate arguments
|
|
if (data == NULL || size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0;i < size;i++)
|
|
{
|
|
if (buf[i] != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == size)
|
|
{
|
|
i = size - 1;
|
|
}
|
|
buf += i;
|
|
|
|
serial = Malloc(sizeof(X_SERIAL));
|
|
serial->size = size - i;
|
|
serial->data = ZeroMalloc(size + 16);
|
|
Copy(serial->data, buf, size - i);
|
|
|
|
return serial;
|
|
}
|
|
|
|
// Get the number of days till January 1, 2038
|
|
UINT GetDaysUntil2038()
|
|
{
|
|
UINT64 now = SystemTime64();
|
|
UINT64 target;
|
|
SYSTEMTIME st;
|
|
|
|
Zero(&st, sizeof(st));
|
|
st.wYear = 2038;
|
|
st.wMonth = 1;
|
|
st.wDay = 1;
|
|
|
|
target = SystemToUINT64(&st);
|
|
|
|
if (now >= target)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return (UINT)((target - now) / (UINT64)(1000 * 60 * 60 * 24));
|
|
}
|
|
}
|
|
UINT GetDaysUntil2038Ex()
|
|
{
|
|
SYSTEMTIME now;
|
|
|
|
Zero(&now, sizeof(now));
|
|
SystemTime(&now);
|
|
|
|
if (now.wYear >= 2030)
|
|
{
|
|
UINT64 now = SystemTime64();
|
|
UINT64 target;
|
|
SYSTEMTIME st;
|
|
|
|
Zero(&st, sizeof(st));
|
|
st.wYear = 2049;
|
|
st.wMonth = 12;
|
|
st.wDay = 30;
|
|
|
|
target = SystemToUINT64(&st);
|
|
|
|
if (now >= target)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return (UINT)((target - now) / (UINT64)(1000 * 60 * 60 * 24));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return GetDaysUntil2038();
|
|
}
|
|
}
|
|
|
|
// Issue an X509 certificate
|
|
X *NewX(K *pub, K *priv, X *ca, NAME *name, UINT days, X_SERIAL *serial)
|
|
{
|
|
return NewXEx(pub, priv, ca, name, days, serial, NULL);
|
|
}
|
|
X *NewXEx(K *pub, K *priv, X *ca, NAME *name, UINT days, X_SERIAL *serial, NAME *name_issuer)
|
|
{
|
|
X509 *x509;
|
|
X *x;
|
|
// Validate arguments
|
|
if (pub == NULL || priv == NULL || name == NULL || ca == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x509 = NewX509Ex(pub, priv, ca, name, days, serial, name_issuer);
|
|
if (x509 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x = X509ToX(x509);
|
|
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
// Create a root certificate
|
|
X *NewRootX(K *pub, K *priv, NAME *name, UINT days, X_SERIAL *serial)
|
|
{
|
|
X509 *x509;
|
|
X *x, *x2;
|
|
// Validate arguments
|
|
if (pub == NULL || priv == NULL || name == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x509 = NewRootX509(pub, priv, name, days, serial);
|
|
if (x509 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x = X509ToX(x509);
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x2 = CloneX(x);
|
|
FreeX(x);
|
|
|
|
return x2;
|
|
}
|
|
|
|
// Create new X509 basic & extended key usage
|
|
void AddKeyUsageX509(EXTENDED_KEY_USAGE *ex, int nid)
|
|
{
|
|
ASN1_OBJECT *obj;
|
|
// Validate arguments
|
|
if (ex == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
obj = OBJ_nid2obj(nid);
|
|
if (obj != NULL)
|
|
{
|
|
sk_ASN1_OBJECT_push(ex, obj);
|
|
}
|
|
}
|
|
X509_EXTENSION *NewExtendedKeyUsageForX509(bool root_cert)
|
|
{
|
|
EXTENDED_KEY_USAGE *ex = sk_ASN1_OBJECT_new_null();
|
|
X509_EXTENSION *ret;
|
|
|
|
if (root_cert)
|
|
{
|
|
AddKeyUsageX509(ex, NID_server_auth);
|
|
AddKeyUsageX509(ex, NID_client_auth);
|
|
AddKeyUsageX509(ex, NID_code_sign);
|
|
AddKeyUsageX509(ex, NID_email_protect);
|
|
AddKeyUsageX509(ex, NID_ipsecEndSystem);
|
|
AddKeyUsageX509(ex, NID_ipsecTunnel);
|
|
AddKeyUsageX509(ex, NID_ipsecUser);
|
|
AddKeyUsageX509(ex, NID_time_stamp);
|
|
AddKeyUsageX509(ex, NID_OCSP_sign);
|
|
}
|
|
else
|
|
{
|
|
AddKeyUsageX509(ex, NID_server_auth);
|
|
AddKeyUsageX509(ex, NID_client_auth);
|
|
AddKeyUsageX509(ex, NID_ipsecEndSystem);
|
|
AddKeyUsageX509(ex, NID_ipsecTunnel);
|
|
AddKeyUsageX509(ex, NID_ipsecUser);
|
|
}
|
|
|
|
ret = X509V3_EXT_i2d(NID_ext_key_usage, 0, ex);
|
|
|
|
sk_ASN1_OBJECT_pop_free(ex, ASN1_OBJECT_free);
|
|
|
|
return ret;
|
|
}void BitStringSetBit(ASN1_BIT_STRING *str, int bit)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ASN1_BIT_STRING_set_bit(str, bit, 1);
|
|
}
|
|
X509_EXTENSION *NewBasicKeyUsageForX509(bool root_cert)
|
|
{
|
|
X509_EXTENSION *ret = NULL;
|
|
ASN1_BIT_STRING *str;
|
|
|
|
str = ASN1_BIT_STRING_new();
|
|
if (str != NULL)
|
|
{
|
|
if (root_cert)
|
|
{
|
|
BitStringSetBit(str, 0); // KU_DIGITAL_SIGNATURE
|
|
BitStringSetBit(str, 1); // KU_NON_REPUDIATION
|
|
BitStringSetBit(str, 2); // KU_KEY_ENCIPHERMENT
|
|
BitStringSetBit(str, 3); // KU_DATA_ENCIPHERMENT
|
|
//BitStringSetBit(str, 4); // KU_KEY_AGREEMENT
|
|
BitStringSetBit(str, 5); // KU_KEY_CERT_SIGN
|
|
BitStringSetBit(str, 6); // KU_CRL_SIGN
|
|
}
|
|
else
|
|
{
|
|
BitStringSetBit(str, 0); // KU_DIGITAL_SIGNATURE
|
|
BitStringSetBit(str, 2); // KU_KEY_ENCIPHERMENT
|
|
}
|
|
|
|
ret = X509V3_EXT_i2d(NID_key_usage, 0, str);
|
|
|
|
ASN1_BIT_STRING_free(str);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Issue an X509 certificate
|
|
X509 *NewX509(K *pub, K *priv, X *ca, NAME *name, UINT days, X_SERIAL *serial)
|
|
{
|
|
return NewX509Ex(pub, priv, ca, name, days, serial, NULL);
|
|
}
|
|
X509 *NewX509Ex(K *pub, K *priv, X *ca, NAME *name, UINT days, X_SERIAL *serial, NAME *name_issuer)
|
|
{
|
|
X509 *x509;
|
|
UINT64 notBefore, notAfter;
|
|
ASN1_TIME *t1, *t2;
|
|
X509_NAME *subject_name, *issuer_name;
|
|
X509_EXTENSION *ex = NULL;
|
|
X509_EXTENSION *eku = NULL;
|
|
X509_EXTENSION *busage = NULL;
|
|
ASN1_INTEGER *s;
|
|
// Validate arguments
|
|
if (pub == NULL || name == NULL || ca == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (pub->private_key != false)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (priv->private_key == false)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
notBefore = SystemTime64();
|
|
notAfter = notBefore + (UINT64)days * (UINT64)3600 * (UINT64)24 * (UINT64)1000;
|
|
|
|
// Creating a X509
|
|
x509 = X509_new();
|
|
if (x509 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Make it a v3 certificate
|
|
X509_set_version(x509, 2L);
|
|
|
|
// Set the Expiration
|
|
t1 = X509_get_notBefore(x509);
|
|
t2 = X509_get_notAfter(x509);
|
|
if (!UINT64ToAsn1Time(t1, notBefore))
|
|
{
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
if (!UINT64ToAsn1Time(t2, notAfter))
|
|
{
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
|
|
// Set the name
|
|
subject_name = NameToX509Name(name);
|
|
if (subject_name == NULL)
|
|
{
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
if (name_issuer == NULL)
|
|
{
|
|
issuer_name = X509_get_subject_name(ca->x509);
|
|
}
|
|
else
|
|
{
|
|
issuer_name = NameToX509Name(name_issuer);
|
|
}
|
|
|
|
if (issuer_name == NULL)
|
|
{
|
|
FreeX509Name(subject_name);
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
|
|
X509_set_issuer_name(x509, issuer_name);
|
|
X509_set_subject_name(x509, subject_name);
|
|
|
|
FreeX509Name(subject_name);
|
|
|
|
if (name_issuer != NULL)
|
|
{
|
|
FreeX509Name(issuer_name);
|
|
}
|
|
|
|
// Set the Serial Number
|
|
s = X509_get_serialNumber(x509);
|
|
OPENSSL_free(s->data);
|
|
if (serial == NULL)
|
|
{
|
|
char zero = 0;
|
|
s->data = OPENSSL_malloc(sizeof(char));
|
|
Copy(s->data, &zero, sizeof(char));
|
|
s->length = sizeof(char);
|
|
}
|
|
else
|
|
{
|
|
s->data = OPENSSL_malloc(serial->size);
|
|
Copy(s->data, serial->data, serial->size);
|
|
s->length = serial->size;
|
|
}
|
|
|
|
/*
|
|
// Extensions
|
|
ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, "critical,CA:TRUE");
|
|
X509_add_ext(x509, ex, -1);
|
|
X509_EXTENSION_free(ex);
|
|
*/
|
|
|
|
// Basic usage
|
|
busage = NewBasicKeyUsageForX509(false);
|
|
if (busage != NULL)
|
|
{
|
|
X509_add_ext(x509, busage, -1);
|
|
X509_EXTENSION_free(busage);
|
|
}
|
|
|
|
// EKU
|
|
eku = NewExtendedKeyUsageForX509(false);
|
|
if (eku != NULL)
|
|
{
|
|
X509_add_ext(x509, eku, -1);
|
|
X509_EXTENSION_free(eku);
|
|
}
|
|
|
|
// Alternative subject name
|
|
if (UniIsEmptyStr(name->CommonName) == false)
|
|
{
|
|
char alt_dns[MAX_PATH];
|
|
|
|
Format(alt_dns, sizeof(alt_dns), "DNS.1:%S", name->CommonName);
|
|
|
|
ex = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, alt_dns);
|
|
X509_add_ext(x509, ex, -1);
|
|
X509_EXTENSION_free(ex);
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
// Set the public key
|
|
X509_set_pubkey(x509, pub->pkey);
|
|
|
|
// Signature
|
|
// 2014.3.19 set the initial digest algorithm to SHA-256
|
|
X509_sign(x509, priv->pkey, EVP_sha256());
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
return x509;
|
|
}
|
|
|
|
// Create an X509 root certificate
|
|
X509 *NewRootX509(K *pub, K *priv, NAME *name, UINT days, X_SERIAL *serial)
|
|
{
|
|
X509 *x509;
|
|
UINT64 notBefore, notAfter;
|
|
ASN1_TIME *t1, *t2;
|
|
X509_NAME *subject_name, *issuer_name;
|
|
X509_EXTENSION *ex = NULL;
|
|
X509_EXTENSION *eku = NULL;
|
|
X509_EXTENSION *busage = NULL;
|
|
ASN1_INTEGER *s;
|
|
// Validate arguments
|
|
if (pub == NULL || name == NULL || priv == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (days == 0)
|
|
{
|
|
days = 365;
|
|
}
|
|
if (priv->private_key == false)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (pub->private_key != false)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
notBefore = SystemTime64();
|
|
notAfter = notBefore + (UINT64)days * (UINT64)3600 * (UINT64)24 * (UINT64)1000;
|
|
|
|
// Creating a X509
|
|
x509 = X509_new();
|
|
if (x509 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Make it a v3 certificate
|
|
X509_set_version(x509, 2L);
|
|
|
|
// Set the Expiration
|
|
t1 = X509_get_notBefore(x509);
|
|
t2 = X509_get_notAfter(x509);
|
|
if (!UINT64ToAsn1Time(t1, notBefore))
|
|
{
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
if (!UINT64ToAsn1Time(t2, notAfter))
|
|
{
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
|
|
// Set the name
|
|
subject_name = NameToX509Name(name);
|
|
if (subject_name == NULL)
|
|
{
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
issuer_name = NameToX509Name(name);
|
|
if (issuer_name == NULL)
|
|
{
|
|
FreeX509Name(subject_name);
|
|
FreeX509(x509);
|
|
return NULL;
|
|
}
|
|
|
|
X509_set_issuer_name(x509, issuer_name);
|
|
X509_set_subject_name(x509, subject_name);
|
|
|
|
FreeX509Name(subject_name);
|
|
FreeX509Name(issuer_name);
|
|
|
|
// Set a Serial Number
|
|
s = X509_get_serialNumber(x509);
|
|
OPENSSL_free(s->data);
|
|
if (serial == NULL)
|
|
{
|
|
char zero = 0;
|
|
s->data = OPENSSL_malloc(sizeof(char));
|
|
Copy(s->data, &zero, sizeof(char));
|
|
s->length = sizeof(char);
|
|
}
|
|
else
|
|
{
|
|
s->data = OPENSSL_malloc(serial->size);
|
|
Copy(s->data, serial->data, serial->size);
|
|
s->length = serial->size;
|
|
}
|
|
|
|
// Extensions
|
|
ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, "critical,CA:TRUE");
|
|
X509_add_ext(x509, ex, -1);
|
|
X509_EXTENSION_free(ex);
|
|
|
|
// Basic usage
|
|
busage = NewBasicKeyUsageForX509(true);
|
|
if (busage != NULL)
|
|
{
|
|
X509_add_ext(x509, busage, -1);
|
|
X509_EXTENSION_free(busage);
|
|
}
|
|
|
|
// EKU
|
|
eku = NewExtendedKeyUsageForX509(true);
|
|
if (eku != NULL)
|
|
{
|
|
X509_add_ext(x509, eku, -1);
|
|
X509_EXTENSION_free(eku);
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
// Set the public key
|
|
X509_set_pubkey(x509, pub->pkey);
|
|
|
|
// Signature
|
|
// 2014.3.19 set the initial digest algorithm to SHA-256
|
|
X509_sign(x509, priv->pkey, EVP_sha256());
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
return x509;
|
|
}
|
|
|
|
// Convert the NAMEto a X509_NAME
|
|
void *NameToX509Name(NAME *nm)
|
|
{
|
|
X509_NAME *xn;
|
|
// Validate arguments
|
|
if (nm == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
xn = X509_NAME_new();
|
|
if (xn == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Add the entries
|
|
AddX509Name(xn, NID_commonName, nm->CommonName);
|
|
AddX509Name(xn, NID_organizationName, nm->Organization);
|
|
AddX509Name(xn, NID_organizationalUnitName, nm->Unit);
|
|
AddX509Name(xn, NID_countryName, nm->Country);
|
|
AddX509Name(xn, NID_stateOrProvinceName, nm->State);
|
|
AddX509Name(xn, NID_localityName, nm->Local);
|
|
|
|
return xn;
|
|
}
|
|
|
|
// Add an entry to the X509_NAME
|
|
bool AddX509Name(void *xn, int nid, wchar_t *str)
|
|
{
|
|
X509_NAME *x509_name;
|
|
UINT utf8_size;
|
|
BYTE *utf8;
|
|
int encoding_type = MBSTRING_ASC;
|
|
// Validate arguments
|
|
if (xn == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Convert to UTF-8
|
|
utf8_size = CalcUniToUtf8(str);
|
|
if (utf8_size == 0)
|
|
{
|
|
return false;
|
|
}
|
|
utf8 = ZeroMalloc(utf8_size + 1);
|
|
UniToUtf8(utf8, utf8_size, str);
|
|
utf8[utf8_size] = 0;
|
|
|
|
if (StrLen(utf8) != UniStrLen(str))
|
|
{
|
|
encoding_type = MBSTRING_UTF8;
|
|
}
|
|
|
|
// Adding
|
|
x509_name = (X509_NAME *)xn;
|
|
LockOpenSSL();
|
|
{
|
|
X509_NAME_add_entry_by_NID(x509_name, nid, encoding_type, utf8, utf8_size, -1, 0);
|
|
}
|
|
UnlockOpenSSL();
|
|
Free(utf8);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Release the X509_NAME
|
|
void FreeX509Name(void *xn)
|
|
{
|
|
X509_NAME *x509_name;
|
|
// Validate arguments
|
|
if (xn == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
x509_name = (X509_NAME *)xn;
|
|
X509_NAME_free(x509_name);
|
|
}
|
|
|
|
// Creating the NAME
|
|
NAME *NewName(wchar_t *common_name, wchar_t *organization, wchar_t *unit,
|
|
wchar_t *country, wchar_t *state, wchar_t *local)
|
|
{
|
|
NAME *nm = ZeroMalloc(sizeof(NAME));
|
|
|
|
if (UniIsEmptyStr(common_name) == false)
|
|
{
|
|
nm->CommonName = CopyUniStr(common_name);
|
|
}
|
|
|
|
if (UniIsEmptyStr(organization) == false)
|
|
{
|
|
nm->Organization = CopyUniStr(organization);
|
|
}
|
|
|
|
if (UniIsEmptyStr(unit) == false)
|
|
{
|
|
nm->Unit = CopyUniStr(unit);
|
|
}
|
|
|
|
if (UniIsEmptyStr(country) == false)
|
|
{
|
|
nm->Country = CopyUniStr(country);
|
|
}
|
|
|
|
if (UniIsEmptyStr(state) == false)
|
|
{
|
|
nm->State = CopyUniStr(state);
|
|
}
|
|
|
|
if (UniIsEmptyStr(local) == false)
|
|
{
|
|
nm->Local = CopyUniStr(local);
|
|
}
|
|
|
|
return nm;
|
|
}
|
|
|
|
// Check the expiration date of the certificate by the current time
|
|
bool CheckXDateNow(X *x)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return CheckXDate(x, SystemTime64());
|
|
}
|
|
|
|
// Check the expiration date of the certificate
|
|
bool CheckXDate(X *x, UINT64 current_system_time)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (x->notBefore >= current_system_time || x->notAfter <= current_system_time)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Read the expiration date of the certificate
|
|
void LoadXDates(X *x)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
x->notBefore = Asn1TimeToUINT64((ASN1_TIME *)X509_get0_notBefore(x->x509));
|
|
x->notAfter = Asn1TimeToUINT64((ASN1_TIME *)X509_get0_notAfter(x->x509));
|
|
}
|
|
|
|
// Convert the 64bit system time to ASN1 time
|
|
bool UINT64ToAsn1Time(void *asn1_time, UINT64 t)
|
|
{
|
|
SYSTEMTIME st;
|
|
// Validate arguments
|
|
if (asn1_time == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UINT64ToSystem(&st, t);
|
|
return SystemToAsn1Time(asn1_time, &st);
|
|
}
|
|
|
|
// Convert the system time to the ASN1 time
|
|
bool SystemToAsn1Time(void *asn1_time, SYSTEMTIME *s)
|
|
{
|
|
char tmp[20];
|
|
ASN1_TIME *t;
|
|
// Validate arguments
|
|
if (asn1_time == NULL || s == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (SystemToStr(tmp, sizeof(tmp), s) == false)
|
|
{
|
|
return false;
|
|
}
|
|
t = (ASN1_TIME *)asn1_time;
|
|
if (t->data == NULL || t->length < sizeof(tmp))
|
|
{
|
|
t->data = OPENSSL_malloc(sizeof(tmp));
|
|
}
|
|
StrCpy((char *)t->data, t->length, tmp);
|
|
t->length = StrLen(tmp);
|
|
|
|
if (t->length == 15)
|
|
{
|
|
// YYYYMMDDHHMMSSZ
|
|
t->type = V_ASN1_GENERALIZEDTIME;
|
|
}
|
|
else
|
|
{
|
|
// YYMMDDHHMMSSZ
|
|
t->type = V_ASN1_UTCTIME;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Convert the system time to a string
|
|
bool SystemToStr(char *str, UINT size, SYSTEMTIME *s)
|
|
{
|
|
// Validate arguments
|
|
if (str == NULL || s == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (s->wYear <= 2049)
|
|
{
|
|
// 2000 to 2049: Use YYMMDDHHMMSSZ
|
|
Format(str, size, "%02u%02u%02u%02u%02u%02uZ",
|
|
s->wYear % 100, s->wMonth, s->wDay,
|
|
s->wHour, s->wMinute, s->wSecond);
|
|
}
|
|
else
|
|
{
|
|
// 2050 to 9999: Use YYYYMMDDHHMMSSZ
|
|
Format(str, size, "%04u%02u%02u%02u%02u%02uZ",
|
|
s->wYear, s->wMonth, s->wDay,
|
|
s->wHour, s->wMinute, s->wSecond);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Convert an ASN1 time to an UINT64 time
|
|
UINT64 Asn1TimeToUINT64(void *asn1_time)
|
|
{
|
|
SYSTEMTIME st;
|
|
// Validate arguments
|
|
if (asn1_time == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (Asn1TimeToSystem(&st, asn1_time) == false)
|
|
{
|
|
return 0;
|
|
}
|
|
return SystemToUINT64(&st);
|
|
}
|
|
|
|
// Converted an ASN1 time to a system time
|
|
bool Asn1TimeToSystem(SYSTEMTIME *s, void *asn1_time)
|
|
{
|
|
ASN1_TIME *t;
|
|
// Validate arguments
|
|
if (s == NULL || asn1_time == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
t = (ASN1_TIME *)asn1_time;
|
|
if (StrToSystem(s, (char *)t->data) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Convert the string to the system time
|
|
bool StrToSystem(SYSTEMTIME *s, char *str)
|
|
{
|
|
char century[3] = {0, 0, 0};
|
|
bool fourdigityear = false;
|
|
|
|
// Validate arguments
|
|
if (s == NULL || str == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (StrLen(str) != 13)
|
|
{
|
|
if (StrLen(str) != 15) return false;
|
|
|
|
//Year has 4 digits - save first two and use the rest
|
|
//as if it had two digits
|
|
fourdigityear = true;
|
|
century[0] = str[0];
|
|
century[1] = str[1];
|
|
str += 2;
|
|
}
|
|
if (str[12] != 'Z')
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Conversion
|
|
{
|
|
char year[3] = {str[0], str[1], 0},
|
|
month[3] = {str[2], str[3], 0},
|
|
day[3] = {str[4], str[5], 0},
|
|
hour[3] = {str[6], str[7], 0},
|
|
minute[3] = {str[8], str[9], 0},
|
|
second[3] = {str[10], str[11], 0};
|
|
Zero(s, sizeof(SYSTEMTIME));
|
|
s->wYear = ToInt(year);
|
|
if( fourdigityear ) {
|
|
s->wYear += ToInt(century) * 100;
|
|
}
|
|
else if (s->wYear >= 60)
|
|
{
|
|
s->wYear += 1900;
|
|
}
|
|
else
|
|
{
|
|
s->wYear += 2000;
|
|
}
|
|
s->wMonth = ToInt(month);
|
|
s->wDay = ToInt(day);
|
|
s->wHour = ToInt(hour);
|
|
s->wMinute = ToInt(minute);
|
|
s->wSecond = ToInt(second);
|
|
NormalizeSystem(s);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Verify the RSA signature
|
|
bool RsaVerify(void *data, UINT data_size, void *sign, K *k)
|
|
{
|
|
return RsaVerifyEx(data, data_size, sign, k, 0);
|
|
}
|
|
|
|
bool RsaVerifyEx(void *data, UINT data_size, void *sign, K *k, UINT bits)
|
|
{
|
|
UCHAR hash_data[SIGN_HASH_SIZE];
|
|
UCHAR *decrypt_data;
|
|
RSA *rsa;
|
|
// Validate arguments
|
|
if (data == NULL || sign == NULL || k == NULL || k->private_key != false)
|
|
{
|
|
return false;
|
|
}
|
|
if (bits == 0)
|
|
{
|
|
bits = 1024;
|
|
}
|
|
|
|
rsa = (RSA*)EVP_PKEY_get0_RSA(k->pkey);
|
|
if (rsa == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
decrypt_data = ZeroMalloc(RSA_size(rsa));
|
|
|
|
// Hash the data
|
|
if (HashForSign(hash_data, sizeof(hash_data), data, data_size) == false)
|
|
{
|
|
Free(decrypt_data);
|
|
return false;
|
|
}
|
|
|
|
// Decode the signature
|
|
if (RSA_public_decrypt(bits / 8, sign, decrypt_data, rsa, RSA_PKCS1_PADDING) <= 0)
|
|
{
|
|
Free(decrypt_data);
|
|
return false;
|
|
}
|
|
|
|
// Comparison
|
|
if (Cmp(decrypt_data, hash_data, SIGN_HASH_SIZE) != 0)
|
|
{
|
|
Free(decrypt_data);
|
|
return false;
|
|
}
|
|
|
|
Free(decrypt_data);
|
|
|
|
return true;
|
|
}
|
|
|
|
// RSA signature
|
|
bool RsaSign(void *dst, void *src, UINT size, K *k)
|
|
{
|
|
return RsaSignEx(dst, src, size, k, 0);
|
|
}
|
|
bool RsaSignEx(void *dst, void *src, UINT size, K *k, UINT bits)
|
|
{
|
|
UCHAR hash[SIGN_HASH_SIZE];
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL || k == NULL || EVP_PKEY_base_id(k->pkey) != EVP_PKEY_RSA)
|
|
{
|
|
return false;
|
|
}
|
|
if (bits == 0)
|
|
{
|
|
bits = 1024;
|
|
}
|
|
|
|
Zero(dst, bits / 8);
|
|
|
|
// Hash
|
|
if (HashForSign(hash, sizeof(hash), src, size) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Signature
|
|
if (RSA_private_encrypt(sizeof(hash), hash, dst, (RSA*)EVP_PKEY_get0_RSA(k->pkey), RSA_PKCS1_PADDING) <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Generation of signature data by SHA-1
|
|
bool HashForSign(void *dst, UINT dst_size, void *src, UINT src_size)
|
|
{
|
|
UCHAR *buf = (UCHAR *)dst;
|
|
UCHAR sign_data[] =
|
|
{
|
|
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E,
|
|
0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14,
|
|
};
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL || src_size == 0 || MIN_SIGN_HASH_SIZE > dst_size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Header part
|
|
Copy(buf, sign_data, sizeof(sign_data));
|
|
|
|
// Hash
|
|
HashSha1(HASHED_DATA(buf), src, src_size);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Decrypt with the RSA public key
|
|
bool RsaPublicDecrypt(void *dst, void *src, UINT size, K *k)
|
|
{
|
|
void *tmp;
|
|
int ret;
|
|
// Validate arguments
|
|
if (src == NULL || size == 0 || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tmp = ZeroMalloc(size);
|
|
LockOpenSSL();
|
|
{
|
|
ret = RSA_public_decrypt(size, src, tmp, (RSA *)EVP_PKEY_get0_RSA(k->pkey), RSA_NO_PADDING);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (ret <= 0)
|
|
{
|
|
/* Debug("RSA Error: 0x%x\n",
|
|
ERR_get_error());
|
|
*/ Free(tmp);
|
|
return false;
|
|
}
|
|
|
|
Copy(dst, tmp, size);
|
|
Free(tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Encrypt with the RSA private key
|
|
bool RsaPrivateEncrypt(void *dst, void *src, UINT size, K *k)
|
|
{
|
|
void *tmp;
|
|
int ret;
|
|
// Validate arguments
|
|
if (src == NULL || size == 0 || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tmp = ZeroMalloc(size);
|
|
LockOpenSSL();
|
|
{
|
|
ret = RSA_private_encrypt(size, src, tmp, (RSA *)EVP_PKEY_get0_RSA(k->pkey), RSA_NO_PADDING);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (ret <= 0)
|
|
{
|
|
Debug("RSA Error: %u\n",
|
|
ERR_GET_REASON(ERR_get_error()));
|
|
Free(tmp);
|
|
return false;
|
|
}
|
|
|
|
Copy(dst, tmp, size);
|
|
Free(tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Decrypt with the RSA private key
|
|
bool RsaPrivateDecrypt(void *dst, void *src, UINT size, K *k)
|
|
{
|
|
void *tmp;
|
|
int ret;
|
|
// Validate arguments
|
|
if (src == NULL || size == 0 || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tmp = ZeroMalloc(size);
|
|
LockOpenSSL();
|
|
{
|
|
ret = RSA_private_decrypt(size, src, tmp, (RSA *)EVP_PKEY_get0_RSA(k->pkey), RSA_NO_PADDING);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (ret <= 0)
|
|
{
|
|
Free(tmp);
|
|
return false;
|
|
}
|
|
|
|
Copy(dst, tmp, size);
|
|
Free(tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Encrypt with the RSA public key
|
|
bool RsaPublicEncrypt(void *dst, void *src, UINT size, K *k)
|
|
{
|
|
void *tmp;
|
|
int ret;
|
|
// Validate arguments
|
|
if (src == NULL || size == 0 || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
tmp = ZeroMalloc(size);
|
|
LockOpenSSL();
|
|
{
|
|
ret = RSA_public_encrypt(size, src, tmp, (RSA*)EVP_PKEY_get0_RSA(k->pkey), RSA_NO_PADDING);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (ret <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Copy(dst, tmp, size);
|
|
Free(tmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
// RSA operating environment check
|
|
bool RsaCheckEx()
|
|
{
|
|
UINT num = 20;
|
|
UINT i;
|
|
|
|
for (i = 0;i < num;i++)
|
|
{
|
|
if (RsaCheck())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
SleepThread(100);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
bool RsaCheck()
|
|
{
|
|
RSA *rsa;
|
|
K *priv_key, *pub_key;
|
|
BIO *bio;
|
|
char errbuf[MAX_SIZE];
|
|
UINT size = 0;
|
|
UINT bit = 1024;
|
|
// Validate arguments
|
|
|
|
// Key generation
|
|
LockOpenSSL();
|
|
{
|
|
rsa = RSA_generate_key(bit, RSA_F4, NULL, NULL);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (rsa == NULL)
|
|
{
|
|
Debug("RSA_generate_key: err=%s\n", ERR_error_string(ERR_get_error(), errbuf));
|
|
return false;
|
|
}
|
|
|
|
// Secret key
|
|
bio = NewBio();
|
|
LockOpenSSL();
|
|
{
|
|
i2d_RSAPrivateKey_bio(bio, rsa);
|
|
}
|
|
UnlockOpenSSL();
|
|
BIO_seek(bio, 0);
|
|
priv_key = BioToK(bio, true, false, NULL);
|
|
FreeBio(bio);
|
|
|
|
// Public key
|
|
bio = NewBio();
|
|
LockOpenSSL();
|
|
{
|
|
i2d_RSA_PUBKEY_bio(bio, rsa);
|
|
}
|
|
UnlockOpenSSL();
|
|
BIO_seek(bio, 0);
|
|
pub_key = BioToK(bio, false, false, NULL);
|
|
FreeBio(bio);
|
|
|
|
RSA_free(rsa);
|
|
|
|
size = RsaPublicSize(pub_key);
|
|
|
|
if (size != ((bit + 7) / 8))
|
|
{
|
|
FreeK(priv_key);
|
|
FreeK(pub_key);
|
|
|
|
return false;
|
|
}
|
|
|
|
FreeK(priv_key);
|
|
FreeK(pub_key);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Generation of RSA key
|
|
bool RsaGen(K **priv, K **pub, UINT bit)
|
|
{
|
|
RSA *rsa;
|
|
K *priv_key, *pub_key;
|
|
BIO *bio;
|
|
char errbuf[MAX_SIZE];
|
|
UINT size = 0;
|
|
// Validate arguments
|
|
if (priv == NULL || pub == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (bit == 0)
|
|
{
|
|
bit = 1024;
|
|
}
|
|
|
|
// Key generation
|
|
LockOpenSSL();
|
|
{
|
|
rsa = RSA_generate_key(bit, RSA_F4, NULL, NULL);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (rsa == NULL)
|
|
{
|
|
Debug("RSA_generate_key: err=%s\n", ERR_error_string(ERR_get_error(), errbuf));
|
|
return false;
|
|
}
|
|
|
|
// Secret key
|
|
bio = NewBio();
|
|
LockOpenSSL();
|
|
{
|
|
i2d_RSAPrivateKey_bio(bio, rsa);
|
|
}
|
|
UnlockOpenSSL();
|
|
BIO_seek(bio, 0);
|
|
priv_key = BioToK(bio, true, false, NULL);
|
|
FreeBio(bio);
|
|
|
|
// Public key
|
|
bio = NewBio();
|
|
LockOpenSSL();
|
|
{
|
|
i2d_RSA_PUBKEY_bio(bio, rsa);
|
|
}
|
|
UnlockOpenSSL();
|
|
BIO_seek(bio, 0);
|
|
pub_key = BioToK(bio, false, false, NULL);
|
|
FreeBio(bio);
|
|
|
|
*priv = priv_key;
|
|
*pub = pub_key;
|
|
|
|
RSA_free(rsa);
|
|
|
|
size = RsaPublicSize(*pub);
|
|
|
|
if (size != ((bit + 7) / 8))
|
|
{
|
|
FreeK(*priv);
|
|
FreeK(*pub);
|
|
|
|
return RsaGen(priv, pub, bit);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Confirm whether the certificate X is signed by the issuer of the certificate x_issuer
|
|
bool CheckX(X *x, X *x_issuer)
|
|
{
|
|
return CheckXEx(x, x_issuer, false, false);
|
|
}
|
|
bool CheckXEx(X *x, X *x_issuer, bool check_name, bool check_date)
|
|
{
|
|
K *k;
|
|
bool ret;
|
|
// Validate arguments
|
|
if (x == NULL || x_issuer == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
k = GetKFromX(x_issuer);
|
|
if (k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ret = CheckSignature(x, k);
|
|
|
|
if (ret)
|
|
{
|
|
if (check_name)
|
|
{
|
|
if (CompareName(x->issuer_name, x_issuer->subject_name) == false)
|
|
{
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
if (check_date)
|
|
{
|
|
if (CheckXDateNow(x_issuer) == false)
|
|
{
|
|
ret = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeK(k);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Confirm the signature of the certificate X with the public key K
|
|
bool CheckSignature(X *x, K *k)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
if (X509_verify(x->x509, k->pkey) == 0)
|
|
{
|
|
UnlockOpenSSL();
|
|
return false;
|
|
}
|
|
}
|
|
UnlockOpenSSL();
|
|
return true;
|
|
}
|
|
|
|
// Get the public key from the certificate
|
|
K *GetKFromX(X *x)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
K *k;
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
pkey = X509_get_pubkey(x->x509);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (pkey == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
k = ZeroMalloc(sizeof(K));
|
|
k->pkey = pkey;
|
|
|
|
return k;
|
|
}
|
|
|
|
// The name comparison
|
|
bool CompareName(NAME *n1, NAME *n2)
|
|
{
|
|
// Validate arguments
|
|
if (n1 == NULL || n2 == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Name comparison
|
|
if (UniStrCmpi(n1->CommonName, n2->CommonName) == 0 &&
|
|
UniStrCmpi(n1->Organization, n2->Organization) == 0 &&
|
|
UniStrCmpi(n1->Unit, n2->Unit) == 0 &&
|
|
UniStrCmpi(n1->Country, n2->Country) == 0 &&
|
|
UniStrCmpi(n1->State, n2->State) == 0 &&
|
|
UniStrCmpi(n1->Local, n2->Local) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Release the name of the X
|
|
void FreeXNames(X *x)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreeName(x->issuer_name);
|
|
x->issuer_name = NULL;
|
|
|
|
FreeName(x->subject_name);
|
|
x->subject_name = NULL;
|
|
}
|
|
|
|
// Release the name
|
|
void FreeName(NAME *n)
|
|
{
|
|
// Validate arguments
|
|
if (n == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Release the string
|
|
Free(n->CommonName);
|
|
Free(n->Organization);
|
|
Free(n->Unit);
|
|
Free(n->Country);
|
|
Free(n->State);
|
|
Free(n->Local);
|
|
|
|
// Release the object
|
|
Free(n);
|
|
|
|
return;
|
|
}
|
|
|
|
// Get the name of the certificate
|
|
void LoadXNames(X *x)
|
|
{
|
|
X509 *x509;
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
x509 = x->x509;
|
|
x->issuer_name = X509NameToName(X509_get_issuer_name(x509));
|
|
x->subject_name = X509NameToName(X509_get_subject_name(x509));
|
|
}
|
|
|
|
// Convert the X509_NAME structure to the NAME structure
|
|
NAME *X509NameToName(void *xn)
|
|
{
|
|
NAME *n;
|
|
// Validate arguments
|
|
if (xn == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
n = ZeroMalloc(sizeof(NAME));
|
|
|
|
// Get the strings one by one
|
|
n->CommonName = GetUniStrFromX509Name(xn, NID_commonName);
|
|
n->Organization = GetUniStrFromX509Name(xn, NID_organizationName);
|
|
n->Unit = GetUniStrFromX509Name(xn, NID_organizationalUnitName);
|
|
n->Country = GetUniStrFromX509Name(xn, NID_countryName);
|
|
n->State = GetUniStrFromX509Name(xn, NID_stateOrProvinceName);
|
|
n->Local = GetUniStrFromX509Name(xn, NID_localityName);
|
|
|
|
return n;
|
|
}
|
|
|
|
// Read a Unicode string from the X509_NAME structure
|
|
wchar_t *GetUniStrFromX509Name(void *xn, int nid)
|
|
{
|
|
UCHAR txt[1024];
|
|
bool b = false;
|
|
UINT i, size;
|
|
int index;
|
|
bool unicode = false;
|
|
bool is_utf_8 = false;
|
|
ASN1_OBJECT *obj;
|
|
ASN1_STRING *data;
|
|
// Validate arguments
|
|
if (xn == NULL || nid == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Zero(txt, sizeof(txt));
|
|
if (X509_NAME_get_text_by_NID(xn, nid, (char *)txt, sizeof(txt) - 2) <= 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
obj = OBJ_nid2obj(nid);
|
|
if (obj == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
index = X509_NAME_get_index_by_OBJ(xn, obj, -1);
|
|
if (index < 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(xn, index));
|
|
if (data == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (data->type == V_ASN1_BMPSTRING)
|
|
{
|
|
unicode = true;
|
|
}
|
|
if (data->type == V_ASN1_UTF8STRING || data->type == V_ASN1_T61STRING)
|
|
{
|
|
is_utf_8 = true;
|
|
}
|
|
|
|
size = UniStrLen((wchar_t *)txt) * 4 + 8;
|
|
for (i = 0;i < size;i++)
|
|
{
|
|
if (txt[i] >= 0x80)
|
|
{
|
|
unicode = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (is_utf_8)
|
|
{
|
|
wchar_t *ret;
|
|
UINT ret_size;
|
|
|
|
ret_size = CalcUtf8ToUni(txt, StrLen(txt));
|
|
ret = ZeroMalloc(ret_size + 8);
|
|
Utf8ToUni(ret, ret_size, txt, StrLen(txt));
|
|
|
|
return ret;
|
|
}
|
|
else if (unicode == false)
|
|
{
|
|
wchar_t tmp[1024];
|
|
StrToUni(tmp, sizeof(tmp), (char *)txt);
|
|
return CopyUniStr(tmp);
|
|
}
|
|
else
|
|
{
|
|
EndianUnicode((wchar_t *)txt);
|
|
return CopyUniStr((wchar_t *)txt);
|
|
}
|
|
}
|
|
|
|
// Check whether the certificate x1 equal to x2
|
|
bool CompareX(X *x1, X *x2)
|
|
{
|
|
// Validate arguments
|
|
if (x1 == NULL || x2 == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
if (X509_cmp(x1->x509, x2->x509) == 0)
|
|
{
|
|
UnlockOpenSSL();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
UnlockOpenSSL();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check whether K is private key of X
|
|
bool CheckXandK(X *x, K *k)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL || k == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
if (X509_check_private_key(x->x509, k->pkey) != 0)
|
|
{
|
|
UnlockOpenSSL();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
UnlockOpenSSL();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Read a X from the file
|
|
X *FileToX(char *filename)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
X *ret = FileToXW(filename_w);
|
|
|
|
Free(filename_w);
|
|
|
|
return ret;
|
|
}
|
|
X *FileToXW(wchar_t *filename)
|
|
{
|
|
bool text;
|
|
BUF *b;
|
|
X *x;
|
|
// Validate arguments
|
|
if (filename == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = ReadDumpW(filename);
|
|
text = IsBase64(b);
|
|
|
|
x = BufToX(b, text);
|
|
FreeBuf(b);
|
|
|
|
return x;
|
|
}
|
|
|
|
// Write the X to a file
|
|
bool XToFile(X *x, char *filename, bool text)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
bool ret = XToFileW(x, filename_w, text);
|
|
|
|
Free(filename_w);
|
|
|
|
return ret;
|
|
}
|
|
bool XToFileW(X *x, wchar_t *filename, bool text)
|
|
{
|
|
BUF *b;
|
|
bool ret;
|
|
// Validate arguments
|
|
if (x == NULL || filename == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
b = XToBuf(x, text);
|
|
if (b == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ret = DumpBufW(b, filename);
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Read a K from the file
|
|
K *FileToK(char *filename, bool private_key, char *password)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
K *ret;
|
|
|
|
ret = FileToKW(filename_w, private_key, password);
|
|
|
|
Free(filename_w);
|
|
|
|
return ret;
|
|
}
|
|
K *FileToKW(wchar_t *filename, bool private_key, char *password)
|
|
{
|
|
bool text;
|
|
BUF *b;
|
|
K *k;
|
|
// Validate arguments
|
|
if (filename == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = ReadDumpW(filename);
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
text = IsBase64(b);
|
|
if (text == false)
|
|
{
|
|
k = BufToK(b, private_key, false, NULL);
|
|
}
|
|
else
|
|
{
|
|
k = BufToK(b, private_key, true, NULL);
|
|
if (k == NULL)
|
|
{
|
|
k = BufToK(b, private_key, true, password);
|
|
}
|
|
}
|
|
|
|
FreeBuf(b);
|
|
|
|
return k;
|
|
}
|
|
|
|
// Save the K to a file
|
|
bool KToFile(K *k, char *filename, bool text, char *password)
|
|
{
|
|
wchar_t *filename_w = CopyStrToUni(filename);
|
|
bool ret = KToFileW(k, filename_w, text, password);
|
|
|
|
Free(filename_w);
|
|
|
|
return ret;
|
|
}
|
|
bool KToFileW(K *k, wchar_t *filename, bool text, char *password)
|
|
{
|
|
BUF *b;
|
|
bool ret;
|
|
// Validate arguments
|
|
if (k == NULL || filename == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
b = KToBuf(k, text, password);
|
|
if (b == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ret = DumpBufW(b, filename);
|
|
FreeBuf(b);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Convert the K to the BUF
|
|
BUF *KToBuf(K *k, bool text, char *password)
|
|
{
|
|
BUF *buf;
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = KToBio(k, text, password);
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
buf = BioToBuf(bio);
|
|
FreeBio(bio);
|
|
|
|
SeekBuf(buf, 0, 0);
|
|
|
|
return buf;
|
|
}
|
|
|
|
// Convert the K to the BIO
|
|
BIO *KToBio(K *k, bool text, char *password)
|
|
{
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = NewBio();
|
|
|
|
if (k->private_key)
|
|
{
|
|
// Secret key
|
|
if (text == false)
|
|
{
|
|
// Binary format
|
|
LockOpenSSL();
|
|
{
|
|
i2d_PrivateKey_bio(bio, k->pkey);
|
|
}
|
|
UnlockOpenSSL();
|
|
}
|
|
else
|
|
{
|
|
// Text format
|
|
if (password == 0 || StrLen(password) == 0)
|
|
{
|
|
// No encryption
|
|
LockOpenSSL();
|
|
{
|
|
PEM_write_bio_PrivateKey(bio, k->pkey, NULL, NULL, 0, NULL, NULL);
|
|
}
|
|
UnlockOpenSSL();
|
|
}
|
|
else
|
|
{
|
|
// Encrypt
|
|
CB_PARAM cb;
|
|
cb.password = password;
|
|
LockOpenSSL();
|
|
{
|
|
PEM_write_bio_PrivateKey(bio, k->pkey, EVP_des_ede3_cbc(),
|
|
NULL, 0, (pem_password_cb *)PKeyPasswordCallbackFunction, &cb);
|
|
}
|
|
UnlockOpenSSL();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Public key
|
|
if (text == false)
|
|
{
|
|
// Binary format
|
|
LockOpenSSL();
|
|
{
|
|
i2d_PUBKEY_bio(bio, k->pkey);
|
|
}
|
|
UnlockOpenSSL();
|
|
}
|
|
else
|
|
{
|
|
// Text format
|
|
LockOpenSSL();
|
|
{
|
|
PEM_write_bio_PUBKEY(bio, k->pkey);
|
|
}
|
|
UnlockOpenSSL();
|
|
}
|
|
}
|
|
|
|
return bio;
|
|
}
|
|
|
|
// Check whether the BUF is encoded as the Base64
|
|
bool IsBase64(BUF *b)
|
|
{
|
|
UINT i;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (SearchAsciiInBinary(b->Buf, b->Size, "-----BEGIN", false) != INFINITE)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (i = 0;i < b->Size;i++)
|
|
{
|
|
char c = ((char *)b->Buf)[i];
|
|
bool b = false;
|
|
if ('a' <= c && c <= 'z')
|
|
{
|
|
b = true;
|
|
}
|
|
else if ('A' <= c && c <= 'Z')
|
|
{
|
|
b = true;
|
|
}
|
|
else if ('0' <= c && c <= '9')
|
|
{
|
|
b = true;
|
|
}
|
|
else if (c == ':' || c == '.' || c == ';' || c == ',')
|
|
{
|
|
b = true;
|
|
}
|
|
else if (c == '!' || c == '&' || c == '#' || c == '(' || c == ')')
|
|
{
|
|
b = true;
|
|
}
|
|
else if (c == '-' || c == ' ')
|
|
{
|
|
b = true;
|
|
}
|
|
else if (c == 13 || c == 10 || c == EOF)
|
|
{
|
|
b = true;
|
|
}
|
|
else if (c == '\t' || c == '=' || c == '+' || c == '/')
|
|
{
|
|
b = true;
|
|
}
|
|
if (b == false)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Check whether the K in the BUF is encrypted
|
|
bool IsEncryptedK(BUF *b, bool private_key)
|
|
{
|
|
K *k;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (IsBase64(b) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
k = BufToK(b, private_key, true, NULL);
|
|
if (k != NULL)
|
|
{
|
|
FreeK(k);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Convert the BUF to a K
|
|
K *BufToK(BUF *b, bool private_key, bool text, char *password)
|
|
{
|
|
BIO *bio;
|
|
K *k;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = BufToBio(b);
|
|
k = BioToK(bio, private_key, text, password);
|
|
FreeBio(bio);
|
|
|
|
return k;
|
|
}
|
|
|
|
// Release of K
|
|
void FreeK(K *k)
|
|
{
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FreePKey(k->pkey);
|
|
Free(k);
|
|
}
|
|
|
|
// Release the secret key
|
|
void FreePKey(EVP_PKEY *pkey)
|
|
{
|
|
// Validate arguments
|
|
if (pkey == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EVP_PKEY_free(pkey);
|
|
}
|
|
|
|
// Convert the BIO to the K
|
|
K *BioToK(BIO *bio, bool private_key, bool text, char *password)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
K *k;
|
|
// Validate arguments
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (password != NULL && StrLen(password) == 0)
|
|
{
|
|
password = NULL;
|
|
}
|
|
|
|
if (private_key == false)
|
|
{
|
|
// Public key
|
|
if (text == false)
|
|
{
|
|
// Binary format
|
|
pkey = d2i_PUBKEY_bio(bio, NULL);
|
|
if (pkey == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Text format
|
|
CB_PARAM cb;
|
|
cb.password = password;
|
|
LockOpenSSL();
|
|
{
|
|
pkey = PEM_read_bio_PUBKEY(bio, NULL, (pem_password_cb *)PKeyPasswordCallbackFunction, &cb);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (pkey == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (text == false)
|
|
{
|
|
// Binary format
|
|
LockOpenSSL();
|
|
{
|
|
pkey = d2i_PrivateKey_bio(bio, NULL);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (pkey == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Text format
|
|
CB_PARAM cb;
|
|
cb.password = password;
|
|
LockOpenSSL();
|
|
{
|
|
pkey = PEM_read_bio_PrivateKey(bio, NULL, (pem_password_cb *)PKeyPasswordCallbackFunction, &cb);
|
|
}
|
|
UnlockOpenSSL();
|
|
if (pkey == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
k = ZeroMalloc(sizeof(K));
|
|
k->pkey = pkey;
|
|
k->private_key = private_key;
|
|
|
|
return k;
|
|
}
|
|
|
|
// Password callback function
|
|
int PKeyPasswordCallbackFunction(char *buf, int bufsize, int verify, void *param)
|
|
{
|
|
CB_PARAM *cb;
|
|
// Validate arguments
|
|
if (buf == NULL || param == NULL || bufsize == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
cb = (CB_PARAM *)param;
|
|
if (cb->password == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return StrCpy(buf, bufsize, cb->password);
|
|
}
|
|
|
|
// Convert the X to a BUF
|
|
BUF *XToBuf(X *x, bool text)
|
|
{
|
|
BIO *bio;
|
|
BUF *b;
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = XToBio(x, text);
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
b = BioToBuf(bio);
|
|
FreeBio(bio);
|
|
|
|
SeekBuf(b, 0, 0);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Convert the X to a BIO
|
|
BIO *XToBio(X *x, bool text)
|
|
{
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = NewBio();
|
|
|
|
LockOpenSSL();
|
|
{
|
|
if (text == false)
|
|
{
|
|
// Binary format
|
|
i2d_X509_bio(bio, x->x509);
|
|
}
|
|
else
|
|
{
|
|
// Text format
|
|
PEM_write_bio_X509(bio, x->x509);
|
|
}
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
return bio;
|
|
}
|
|
|
|
// Release of the X
|
|
void FreeX(X *x)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Release the name
|
|
FreeXNames(x);
|
|
|
|
|
|
// Release the Serial
|
|
FreeXSerial(x->serial);
|
|
|
|
if (x->do_not_free == false)
|
|
{
|
|
FreeX509(x->x509);
|
|
}
|
|
Free(x);
|
|
}
|
|
|
|
// Release of the X509
|
|
void FreeX509(X509 *x509)
|
|
{
|
|
// Validate arguments
|
|
if (x509 == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
X509_free(x509);
|
|
}
|
|
UnlockOpenSSL();
|
|
}
|
|
|
|
// Convert the BUF to a X
|
|
X *BufToX(BUF *b, bool text)
|
|
{
|
|
X *x;
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = BufToBio(b);
|
|
if (bio == NULL)
|
|
{
|
|
FreeBuf(b);
|
|
return NULL;
|
|
}
|
|
|
|
x = BioToX(bio, text);
|
|
|
|
FreeBio(bio);
|
|
|
|
return x;
|
|
}
|
|
|
|
// Create a new buffer by skipping the contents of the buffer to the specified string
|
|
BUF *SkipBufBeforeString(BUF *b, char *str)
|
|
{
|
|
char *tmp;
|
|
UINT tmp_size;
|
|
BUF *ret;
|
|
UINT i;
|
|
UINT offset = 0;
|
|
// Validate arguments
|
|
if (b == NULL || str == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
tmp_size = b->Size + 1;
|
|
tmp = ZeroMalloc(tmp_size);
|
|
Copy(tmp, b->Buf, b->Size);
|
|
|
|
i = SearchStrEx(tmp, str, 0, false);
|
|
if (i != INFINITE)
|
|
{
|
|
offset = i;
|
|
}
|
|
|
|
ret = NewBuf();
|
|
WriteBuf(ret, ((UCHAR *)b->Buf) + offset, b->Size - offset);
|
|
SeekBuf(ret, 0, 0);
|
|
|
|
Free(tmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Get a digest of the X
|
|
void GetXDigest(X *x, UCHAR *buf, bool sha1)
|
|
{
|
|
// Validate arguments
|
|
if (x == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (sha1 == false)
|
|
{
|
|
UINT size = MD5_SIZE;
|
|
X509_digest(x->x509, EVP_md5(), buf, (unsigned int *)&size);
|
|
}
|
|
else
|
|
{
|
|
UINT size = SHA1_SIZE;
|
|
X509_digest(x->x509, EVP_sha1(), buf, (unsigned int *)&size);
|
|
}
|
|
}
|
|
|
|
// Convert BIO to X
|
|
X *BioToX(BIO *bio, bool text)
|
|
{
|
|
X *x;
|
|
X509 *x509;
|
|
// Validate arguments
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
// Reading x509
|
|
if (text == false)
|
|
{
|
|
// Binary mode
|
|
x509 = d2i_X509_bio(bio, NULL);
|
|
}
|
|
else
|
|
{
|
|
// Text mode
|
|
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
if (x509 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x = X509ToX(x509);
|
|
|
|
if (x == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
// Convert the X509 to X
|
|
X *X509ToX(X509 *x509)
|
|
{
|
|
X *x;
|
|
K *k;
|
|
BUF *b;
|
|
UINT size;
|
|
UINT type;
|
|
ASN1_INTEGER *s;
|
|
// Validate arguments
|
|
if (x509 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
x = ZeroMalloc(sizeof(X));
|
|
x->x509 = x509;
|
|
|
|
// Name
|
|
LoadXNames(x);
|
|
|
|
// Expiration date
|
|
LoadXDates(x);
|
|
|
|
// Check whether it is a root certificate
|
|
if (CompareName(x->issuer_name, x->subject_name))
|
|
{
|
|
K *pubkey = GetKFromX(x);
|
|
if (pubkey != NULL)
|
|
{
|
|
if (CheckXandK(x, pubkey))
|
|
{
|
|
x->root_cert = true;
|
|
}
|
|
FreeK(pubkey);
|
|
}
|
|
}
|
|
|
|
// Check whether there is basic constraints
|
|
if (X509_get_ext_by_NID(x509, NID_basic_constraints, -1) != -1)
|
|
{
|
|
x->has_basic_constraints = true;
|
|
}
|
|
|
|
// Get the "Certification Authority Issuer" (1.3.6.1.5.5.7.48.2) field value
|
|
if (x->root_cert == false)
|
|
{
|
|
AUTHORITY_INFO_ACCESS *ads = (AUTHORITY_INFO_ACCESS *)X509_get_ext_d2i(x509, NID_info_access, NULL, NULL);
|
|
|
|
if (ads != NULL)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(ads); i++)
|
|
{
|
|
ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(ads, i);
|
|
if (ad != NULL)
|
|
{
|
|
if (OBJ_obj2nid(ad->method) == NID_ad_ca_issuers && ad->location->type == GEN_URI)
|
|
{
|
|
char *uri = (char *)ASN1_STRING_data(ad->location->d.uniformResourceIdentifier);
|
|
|
|
if (IsEmptyStr(uri) == false)
|
|
{
|
|
StrCpy(x->issuer_url, sizeof(x->issuer_url), uri);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AUTHORITY_INFO_ACCESS_free(ads);
|
|
}
|
|
}
|
|
|
|
// Get the Serial Number
|
|
s = X509_get_serialNumber(x509);
|
|
x->serial = NewXSerial(s->data, s->length);
|
|
if (x->serial == NULL)
|
|
{
|
|
char zero = 0;
|
|
x->serial = NewXSerial(&zero, sizeof(char));
|
|
}
|
|
|
|
k = GetKFromX(x);
|
|
if (k == NULL)
|
|
{
|
|
FreeX(x);
|
|
return NULL;
|
|
}
|
|
|
|
b = KToBuf(k, false, NULL);
|
|
|
|
size = b->Size;
|
|
type = EVP_PKEY_base_id(k->pkey);
|
|
|
|
FreeBuf(b);
|
|
|
|
//Fixed to get actual RSA key bits
|
|
x->bits = EVP_PKEY_bits(k->pkey);
|
|
|
|
FreeK(k);
|
|
|
|
if (type == EVP_PKEY_RSA)
|
|
{
|
|
x->is_compatible_bit = true;
|
|
|
|
if(x->bits != 1024 && x->bits != 1536 && x->bits != 2048 && x->bits != 3072 && x->bits != 4096)
|
|
{
|
|
x->is_compatible_bit = false;
|
|
}
|
|
else
|
|
{
|
|
x->is_compatible_bit = true;
|
|
}
|
|
|
|
/*switch (size)
|
|
{
|
|
case 162:
|
|
x->bits = 1024;
|
|
break;
|
|
|
|
case 226:
|
|
x->bits = 1536;
|
|
break;
|
|
|
|
case 294:
|
|
x->bits = 2048;
|
|
break;
|
|
|
|
case 442:
|
|
x->bits = 3072;
|
|
break;
|
|
|
|
case 550:
|
|
x->bits = 4096;
|
|
break;
|
|
|
|
default:
|
|
x->is_compatible_bit = false;
|
|
break;
|
|
}*/
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
// Create a BIO
|
|
BIO *NewBio()
|
|
{
|
|
return BIO_new(BIO_s_mem());
|
|
}
|
|
|
|
// Release the BIO
|
|
void FreeBio(BIO *bio)
|
|
{
|
|
// Validate arguments
|
|
if (bio == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
BIO_free(bio);
|
|
}
|
|
|
|
// Convert the BIO to the BUF
|
|
BUF *BioToBuf(BIO *bio)
|
|
{
|
|
BUF *b;
|
|
UINT size;
|
|
void *tmp;
|
|
// Validate arguments
|
|
if (bio == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
BIO_seek(bio, 0);
|
|
size = (UINT)BIO_number_written(bio);
|
|
tmp = Malloc(size);
|
|
BIO_read(bio, tmp, size);
|
|
|
|
b = NewBuf();
|
|
WriteBuf(b, tmp, size);
|
|
Free(tmp);
|
|
|
|
return b;
|
|
}
|
|
|
|
// Convert the BUF to a BIO
|
|
BIO *BufToBio(BUF *b)
|
|
{
|
|
BIO *bio;
|
|
// Validate arguments
|
|
if (b == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
LockOpenSSL();
|
|
{
|
|
bio = BIO_new(BIO_s_mem());
|
|
if (bio == NULL)
|
|
{
|
|
UnlockOpenSSL();
|
|
return NULL;
|
|
}
|
|
BIO_write(bio, b->Buf, b->Size);
|
|
BIO_seek(bio, 0);
|
|
}
|
|
UnlockOpenSSL();
|
|
|
|
return bio;
|
|
}
|
|
|
|
// New seed rand
|
|
SEEDRAND *NewSeedRand(void *seed, UINT seed_size)
|
|
{
|
|
SEEDRAND *r = ZeroMalloc(sizeof(SEEDRAND));
|
|
|
|
if (seed == NULL || seed_size == 0)
|
|
{
|
|
HashSha1(r->InitialSeed, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
HashSha1(r->InitialSeed, seed, seed_size);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
// Free seed rand
|
|
void FreeSeedRand(SEEDRAND *r)
|
|
{
|
|
if (r == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Free(r);
|
|
}
|
|
|
|
// Get seed rand next byte
|
|
UCHAR SeedRand8(SEEDRAND *r)
|
|
{
|
|
UCHAR tmp[SHA1_SIZE + sizeof(UINT64)];
|
|
UCHAR hash[SHA1_SIZE];
|
|
if (r == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Copy(tmp, r->InitialSeed, SHA1_SIZE);
|
|
WRITE_UINT64(tmp + SHA1_SIZE, r->CurrentCounter);
|
|
|
|
HashSha1(hash, tmp, sizeof(tmp));
|
|
|
|
r->CurrentCounter++;
|
|
|
|
return hash[0];
|
|
}
|
|
void SeedRand(SEEDRAND *r, void *buf, UINT size)
|
|
{
|
|
UINT i;
|
|
if (buf == NULL || size == 0)
|
|
{
|
|
return;
|
|
}
|
|
for (i = 0;i < size;i++)
|
|
{
|
|
((UCHAR *)buf)[i] = SeedRand8(r);
|
|
}
|
|
}
|
|
USHORT SeedRand16(SEEDRAND *r)
|
|
{
|
|
USHORT i;
|
|
SeedRand(r, &i, sizeof(i));
|
|
return i;
|
|
}
|
|
UINT SeedRand32(SEEDRAND *r)
|
|
{
|
|
UINT i;
|
|
SeedRand(r, &i, sizeof(i));
|
|
return i;
|
|
}
|
|
UINT64 SeedRand64(SEEDRAND *r)
|
|
{
|
|
UINT64 i;
|
|
SeedRand(r, &i, sizeof(i));
|
|
return i;
|
|
}
|
|
|
|
// 128-bit random number generation
|
|
void Rand128(void *buf)
|
|
{
|
|
Rand(buf, 16);
|
|
}
|
|
|
|
// 64-bit random number generation
|
|
UINT64 Rand64()
|
|
{
|
|
UINT64 i;
|
|
Rand(&i, sizeof(i));
|
|
return i;
|
|
}
|
|
|
|
// 32-bit random number generation
|
|
UINT Rand32()
|
|
{
|
|
UINT i;
|
|
Rand(&i, sizeof(i));
|
|
return i;
|
|
}
|
|
|
|
// 16-bit random number generation
|
|
USHORT Rand16()
|
|
{
|
|
USHORT i;
|
|
Rand(&i, sizeof(i));
|
|
return i;
|
|
}
|
|
|
|
// 8-bit random number generation
|
|
UCHAR Rand8()
|
|
{
|
|
UCHAR i;
|
|
Rand(&i, sizeof(i));
|
|
return i;
|
|
}
|
|
|
|
// 1-bit random number generation
|
|
bool Rand1()
|
|
{
|
|
return (Rand32() % 2) == 0 ? false : true;
|
|
}
|
|
|
|
// Random number generation
|
|
void Rand(void *buf, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (buf == NULL || size == 0)
|
|
{
|
|
return;
|
|
}
|
|
RAND_bytes(buf, size);
|
|
}
|
|
|
|
// Delete a thread-specific information that OpenSSL has holded
|
|
void FreeOpenSSLThreadState()
|
|
{
|
|
ERR_remove_state(0);
|
|
}
|
|
|
|
// Release the Crypt library
|
|
void FreeCryptLibrary()
|
|
{
|
|
openssl_inited = false;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
if (ossl_provider_default != NULL)
|
|
{
|
|
OSSL_PROVIDER_unload(ossl_provider_default);
|
|
ossl_provider_default = NULL;
|
|
}
|
|
|
|
if (ossl_provider_legacy != NULL)
|
|
{
|
|
OSSL_PROVIDER_unload(ossl_provider_legacy);
|
|
ossl_provider_legacy = NULL;
|
|
}
|
|
#endif
|
|
|
|
DeleteLock(openssl_lock);
|
|
openssl_lock = NULL;
|
|
// RAND_Free_For_SoftEther();
|
|
OpenSSL_FreeLock();
|
|
}
|
|
|
|
// Initialize the Crypt library
|
|
void InitCryptLibrary()
|
|
{
|
|
char tmp[16];
|
|
|
|
CheckIfIntelAesNiSupportedInit();
|
|
// RAND_Init_For_SoftEther()
|
|
openssl_lock = NewLock();
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
OPENSSL_init_ssl(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_NO_LOAD_CONFIG, NULL);
|
|
#else
|
|
SSL_library_init();
|
|
#endif
|
|
//OpenSSL_add_all_algorithms();
|
|
OpenSSL_add_all_ciphers();
|
|
OpenSSL_add_all_digests();
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
|
ossl_provider_legacy = OSSL_PROVIDER_load(NULL, "legacy");
|
|
ossl_provider_default = OSSL_PROVIDER_load(NULL, "default");
|
|
#endif
|
|
|
|
GetSslLibVersion(NULL, 0);
|
|
|
|
ERR_load_crypto_strings();
|
|
SSL_load_error_strings();
|
|
|
|
ssl_clientcert_index = SSL_get_ex_new_index(0, "struct SslClientCertInfo *", NULL, NULL, NULL);
|
|
|
|
#ifdef OS_UNIX
|
|
{
|
|
char *name1 = "/dev/random";
|
|
char *name2 = "/dev/urandom";
|
|
IO *o;
|
|
o = FileOpen(name1, false);
|
|
if (o == NULL)
|
|
{
|
|
o = FileOpen(name2, false);
|
|
if (o == NULL)
|
|
{
|
|
UINT64 now = SystemTime64();
|
|
BUF *b;
|
|
UINT i;
|
|
b = NewBuf();
|
|
for (i = 0;i < 4096;i++)
|
|
{
|
|
UCHAR c = rand() % 256;
|
|
WriteBuf(b, &c, 1);
|
|
}
|
|
WriteBuf(b, &now, sizeof(now));
|
|
RAND_seed(b->Buf, b->Size);
|
|
FreeBuf(b);
|
|
}
|
|
else
|
|
{
|
|
FileClose(o);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FileClose(o);
|
|
}
|
|
}
|
|
#endif // OS_UNIX
|
|
|
|
RAND_poll();
|
|
|
|
#ifdef OS_WIN32
|
|
// RAND_screen();
|
|
#endif
|
|
Rand(tmp, sizeof(tmp));
|
|
OpenSSL_InitLock();
|
|
|
|
openssl_inited = true;
|
|
}
|
|
|
|
// Hash function
|
|
void Hash(void *dst, void *src, UINT size, bool sha)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || (src == NULL && size != 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (sha == false)
|
|
{
|
|
// MD5 hash
|
|
MD5(src, size, dst);
|
|
}
|
|
else
|
|
{
|
|
// SHA hash
|
|
Internal_SHA0(src, size, dst);
|
|
}
|
|
}
|
|
|
|
// MD4 specific hash function
|
|
void HashMd4(void *dst, void *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || (size != 0 && src == NULL))
|
|
{
|
|
return;
|
|
}
|
|
MD4(src, size, dst);
|
|
}
|
|
|
|
// Hash with the SHA-1 and convert it to UINT
|
|
UINT HashToUINT(void *data, UINT size)
|
|
{
|
|
UCHAR hash[SHA1_SIZE];
|
|
UINT u;
|
|
// Validate arguments
|
|
if (data == NULL && size != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
HashSha1(hash, data, size);
|
|
|
|
Copy(&u, hash, sizeof(UINT));
|
|
|
|
u = Endian32(u);
|
|
|
|
return u;
|
|
}
|
|
|
|
// SHA-1 specific hash function
|
|
void HashSha1(void *dst, void *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || (size != 0 && src == NULL))
|
|
{
|
|
return;
|
|
}
|
|
SHA1(src, size, dst);
|
|
}
|
|
|
|
// SHA-256 specific hash function
|
|
void HashSha256(void *dst, void *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || (size != 0 && src == NULL))
|
|
{
|
|
return;
|
|
}
|
|
SHA256(src, size, dst);
|
|
}
|
|
|
|
// Creating a new CRYPT object
|
|
CRYPT *NewCrypt(void *key, UINT size)
|
|
{
|
|
CRYPT *c = ZeroMalloc(sizeof(CRYPT));
|
|
|
|
c->Rc4Key = Malloc(sizeof(struct rc4_key_st));
|
|
|
|
RC4_set_key(c->Rc4Key, size, (UCHAR *)key);
|
|
|
|
return c;
|
|
}
|
|
|
|
// Release the CRYPT object
|
|
void FreeCrypt(CRYPT *c)
|
|
{
|
|
// Validate arguments
|
|
if (c == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Memory release
|
|
Free(c->Rc4Key);
|
|
Free(c);
|
|
}
|
|
|
|
// Encryption and decryption
|
|
void Encrypt(CRYPT *c, void *dst, void *src, UINT size)
|
|
{
|
|
RC4(c->Rc4Key, size, src, dst);
|
|
}
|
|
|
|
// SHA-1 hash
|
|
void Sha(UINT sha_type, void *dst, void *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(sha_type) {
|
|
case SHA1_160:
|
|
SHA1(src, size, dst);
|
|
break;
|
|
case SHA2_256:
|
|
SHA256(src, size, dst);
|
|
break;
|
|
case SHA2_384:
|
|
SHA384(src, size, dst);
|
|
break;
|
|
case SHA2_512:
|
|
SHA512(src, size, dst);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// SHA-1 hash
|
|
void Sha1(void *dst, void *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SHA1(src, size, dst);
|
|
}
|
|
|
|
void Sha1__(void *dst, void *src, UINT size) {
|
|
Sha(SHA1_160, dst, src, size);
|
|
}
|
|
|
|
void Sha2_256(void *dst, void *src, UINT size) {
|
|
Sha(SHA2_256, dst, src, size);
|
|
}
|
|
void Sha2_384(void *dst, void *src, UINT size) {
|
|
Sha(SHA2_384, dst, src, size);
|
|
}
|
|
void Sha2_512(void *dst, void *src, UINT size) {
|
|
Sha(SHA2_512, dst, src, size);
|
|
}
|
|
|
|
// MD5 hash
|
|
void Md5(void *dst, void *src, UINT size)
|
|
{
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MD5(src, size, dst);
|
|
}
|
|
|
|
// 3DES encryption
|
|
void Des3Encrypt(void *dest, void *src, UINT size, DES_KEY *key, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[DES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || key == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, DES_IV_SIZE);
|
|
|
|
DES_ede3_cbc_encrypt(src, dest, size,
|
|
key->k1->KeySchedule,
|
|
key->k2->KeySchedule,
|
|
key->k3->KeySchedule,
|
|
(DES_cblock *)ivec_copy,
|
|
1);
|
|
}
|
|
void Des3Encrypt2(void *dest, void *src, UINT size, DES_KEY_VALUE *k1, DES_KEY_VALUE *k2, DES_KEY_VALUE *k3, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[DES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k1 == NULL || k2 == NULL || k3 == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, DES_IV_SIZE);
|
|
|
|
DES_ede3_cbc_encrypt(src, dest, size,
|
|
k1->KeySchedule,
|
|
k2->KeySchedule,
|
|
k3->KeySchedule,
|
|
(DES_cblock *)ivec_copy,
|
|
1);
|
|
}
|
|
|
|
// DES encryption
|
|
void DesEncrypt(void *dest, void *src, UINT size, DES_KEY_VALUE *k, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[DES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, DES_IV_SIZE);
|
|
|
|
DES_cbc_encrypt(src, dest, size,
|
|
k->KeySchedule,
|
|
(DES_cblock *)ivec_copy,
|
|
1);
|
|
}
|
|
|
|
// 3DES decryption
|
|
void Des3Decrypt(void *dest, void *src, UINT size, DES_KEY *key, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[DES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || key == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, DES_IV_SIZE);
|
|
|
|
DES_ede3_cbc_encrypt(src, dest, size,
|
|
key->k1->KeySchedule,
|
|
key->k2->KeySchedule,
|
|
key->k3->KeySchedule,
|
|
(DES_cblock *)ivec_copy,
|
|
0);
|
|
}
|
|
void Des3Decrypt2(void *dest, void *src, UINT size, DES_KEY_VALUE *k1, DES_KEY_VALUE *k2, DES_KEY_VALUE *k3, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[DES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k1 == NULL || k2 == NULL || k3 == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, DES_IV_SIZE);
|
|
|
|
DES_ede3_cbc_encrypt(src, dest, size,
|
|
k1->KeySchedule,
|
|
k2->KeySchedule,
|
|
k3->KeySchedule,
|
|
(DES_cblock *)ivec_copy,
|
|
0);
|
|
}
|
|
|
|
// DES-ECB encryption
|
|
void DesEcbEncrypt(void *dst, void *src, void *key_7bytes)
|
|
{
|
|
UCHAR *key_56;
|
|
DES_cblock key;
|
|
DES_key_schedule ks;
|
|
// Validate arguments
|
|
if (dst == NULL || src == NULL || key == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
key_56 = (UCHAR *)key_7bytes;
|
|
|
|
Zero(&key, sizeof(key));
|
|
Zero(&ks, sizeof(ks));
|
|
|
|
key[0] = key_56[0];
|
|
key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
|
|
key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
|
|
key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
|
|
key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
|
|
key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
|
|
key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
|
|
key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
|
|
|
|
DES_set_odd_parity(&key);
|
|
DES_set_key_unchecked(&key, &ks);
|
|
|
|
DES_ecb_encrypt(src, dst, &ks, 1);
|
|
}
|
|
|
|
// DES decryption
|
|
void DesDecrypt(void *dest, void *src, UINT size, DES_KEY_VALUE *k, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[DES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, DES_IV_SIZE);
|
|
|
|
DES_cbc_encrypt(src, dest, size,
|
|
k->KeySchedule,
|
|
(DES_cblock *)ivec_copy,
|
|
0);
|
|
}
|
|
|
|
// Generate a random 3DES key
|
|
DES_KEY *Des3RandKey()
|
|
{
|
|
DES_KEY *k = ZeroMalloc(sizeof(DES_KEY));
|
|
|
|
k->k1 = DesRandKeyValue();
|
|
k->k2 = DesRandKeyValue();
|
|
k->k3 = DesRandKeyValue();
|
|
|
|
return k;
|
|
}
|
|
|
|
// Generate a random DES key
|
|
DES_KEY *DesRandKey()
|
|
{
|
|
DES_KEY *k = ZeroMalloc(sizeof(DES_KEY));
|
|
|
|
k->k1 = DesRandKeyValue();
|
|
k->k2 = DesNewKeyValue(k->k1->KeyValue);
|
|
k->k3 = DesNewKeyValue(k->k1->KeyValue);
|
|
|
|
return k;
|
|
}
|
|
|
|
// Release the 3DES key
|
|
void Des3FreeKey(DES_KEY *k)
|
|
{
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DesFreeKeyValue(k->k1);
|
|
DesFreeKeyValue(k->k2);
|
|
DesFreeKeyValue(k->k3);
|
|
|
|
Free(k);
|
|
}
|
|
|
|
// Release the DES key
|
|
void DesFreeKey(DES_KEY *k)
|
|
{
|
|
Des3FreeKey(k);
|
|
}
|
|
|
|
// Create a 3DES key
|
|
DES_KEY *Des3NewKey(void *k1, void *k2, void *k3)
|
|
{
|
|
DES_KEY *k;
|
|
// Validate arguments
|
|
if (k1 == NULL || k2 == NULL || k3 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
k = ZeroMalloc(sizeof(DES_KEY));
|
|
|
|
k->k1 = DesNewKeyValue(k1);
|
|
k->k2 = DesNewKeyValue(k2);
|
|
k->k3 = DesNewKeyValue(k3);
|
|
|
|
return k;
|
|
}
|
|
|
|
// Create a DES key
|
|
DES_KEY *DesNewKey(void *k1)
|
|
{
|
|
return Des3NewKey(k1, k1, k1);
|
|
}
|
|
|
|
// Create a new DES key element
|
|
DES_KEY_VALUE *DesNewKeyValue(void *value)
|
|
{
|
|
DES_KEY_VALUE *v;
|
|
// Validate arguments
|
|
if (value == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
v = ZeroMalloc(sizeof(DES_KEY_VALUE));
|
|
|
|
Copy(v->KeyValue, value, DES_KEY_SIZE);
|
|
|
|
v->KeySchedule = ZeroMalloc(sizeof(DES_key_schedule));
|
|
|
|
DES_set_key_unchecked(value, v->KeySchedule);
|
|
|
|
return v;
|
|
}
|
|
|
|
// Random generation of new DES key element
|
|
DES_KEY_VALUE *DesRandKeyValue()
|
|
{
|
|
UCHAR key_value[DES_KEY_SIZE];
|
|
|
|
DES_random_key((DES_cblock *)key_value);
|
|
|
|
return DesNewKeyValue(key_value);
|
|
}
|
|
|
|
// Release of DES key element
|
|
void DesFreeKeyValue(DES_KEY_VALUE *v)
|
|
{
|
|
// Validate arguments
|
|
if (v == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Free(v->KeySchedule);
|
|
Free(v);
|
|
}
|
|
|
|
// Create a new AES key
|
|
AES_KEY_VALUE *AesNewKey(void *data, UINT size)
|
|
{
|
|
AES_KEY_VALUE *k;
|
|
// Validate arguments
|
|
if (data == NULL || (!(size == 16 || size == 24 || size == 32)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
k = ZeroMalloc(sizeof(AES_KEY_VALUE));
|
|
|
|
k->EncryptKey = ZeroMalloc(sizeof(struct aes_key_st));
|
|
k->DecryptKey = ZeroMalloc(sizeof(struct aes_key_st));
|
|
|
|
k->KeySize = size;
|
|
Copy(k->KeyValue, data, size);
|
|
|
|
AES_set_encrypt_key(data, size * 8, k->EncryptKey);
|
|
AES_set_decrypt_key(data, size * 8, k->DecryptKey);
|
|
|
|
return k;
|
|
}
|
|
|
|
// Release the AES key
|
|
void AesFreeKey(AES_KEY_VALUE *k)
|
|
{
|
|
// Validate arguments
|
|
if (k == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Free(k->EncryptKey);
|
|
Free(k->DecryptKey);
|
|
|
|
Free(k);
|
|
}
|
|
|
|
// AES encryption
|
|
void AesEncrypt(void *dest, void *src, UINT size, AES_KEY_VALUE *k, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[AES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_INTEL_AESNI_LIBRARY
|
|
if (is_intel_aes_supported)
|
|
{
|
|
AesEncryptWithIntel(dest, src, size, k, ivec);
|
|
return;
|
|
}
|
|
#endif // USE_INTEL_AESNI_LIBRARY
|
|
|
|
Copy(ivec_copy, ivec, AES_IV_SIZE);
|
|
|
|
AES_cbc_encrypt(src, dest, size, k->EncryptKey, ivec, 1);
|
|
}
|
|
|
|
// AES decryption
|
|
void AesDecrypt(void *dest, void *src, UINT size, AES_KEY_VALUE *k, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[AES_IV_SIZE];
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_INTEL_AESNI_LIBRARY
|
|
if (is_intel_aes_supported)
|
|
{
|
|
AesDecryptWithIntel(dest, src, size, k, ivec);
|
|
return;
|
|
}
|
|
#endif // USE_INTEL_AESNI_LIBRARY
|
|
|
|
Copy(ivec_copy, ivec, AES_IV_SIZE);
|
|
|
|
AES_cbc_encrypt(src, dest, size, k->DecryptKey, ivec, 0);
|
|
}
|
|
|
|
// Determine whether the Intel AES-NI is supported
|
|
bool IsIntelAesNiSupported()
|
|
{
|
|
return is_intel_aes_supported;
|
|
}
|
|
void CheckIfIntelAesNiSupportedInit()
|
|
{
|
|
#ifdef USE_INTEL_AESNI_LIBRARY
|
|
if (check_for_aes_instructions())
|
|
{
|
|
is_intel_aes_supported = true;
|
|
}
|
|
else
|
|
{
|
|
is_intel_aes_supported = false;
|
|
}
|
|
#else // USE_INTEL_AESNI_LIBRARY
|
|
is_intel_aes_supported = false;
|
|
#endif // USE_INTEL_AESNI_LIBRARY
|
|
}
|
|
|
|
// Disable the Intel AES-NI
|
|
void DisableIntelAesAccel()
|
|
{
|
|
is_intel_aes_supported = false;
|
|
}
|
|
|
|
#ifdef USE_INTEL_AESNI_LIBRARY
|
|
// Encrypt AES using the Intel AES-NI
|
|
void AesEncryptWithIntel(void *dest, void *src, UINT size, AES_KEY_VALUE *k, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[AES_IV_SIZE];
|
|
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, AES_IV_SIZE);
|
|
|
|
switch (k->KeySize)
|
|
{
|
|
case 16:
|
|
intel_AES_enc128_CBC(src, dest, k->KeyValue, (size / AES_IV_SIZE), ivec_copy);
|
|
break;
|
|
|
|
case 24:
|
|
intel_AES_enc192_CBC(src, dest, k->KeyValue, (size / AES_IV_SIZE), ivec_copy);
|
|
break;
|
|
|
|
case 32:
|
|
intel_AES_enc256_CBC(src, dest, k->KeyValue, (size / AES_IV_SIZE), ivec_copy);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Decrypt AES using the Intel AES-NI
|
|
void AesDecryptWithIntel(void *dest, void *src, UINT size, AES_KEY_VALUE *k, void *ivec)
|
|
{
|
|
UCHAR ivec_copy[AES_IV_SIZE];
|
|
|
|
// Validate arguments
|
|
if (dest == NULL || src == NULL || size == 0 || k == NULL || ivec == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Copy(ivec_copy, ivec, AES_IV_SIZE);
|
|
|
|
switch (k->KeySize)
|
|
{
|
|
case 16:
|
|
intel_AES_dec128_CBC(src, dest, k->KeyValue, (size / AES_IV_SIZE), ivec_copy);
|
|
break;
|
|
|
|
case 24:
|
|
intel_AES_dec192_CBC(src, dest, k->KeyValue, (size / AES_IV_SIZE), ivec_copy);
|
|
break;
|
|
|
|
case 32:
|
|
intel_AES_dec256_CBC(src, dest, k->KeyValue, (size / AES_IV_SIZE), ivec_copy);
|
|
break;
|
|
}
|
|
}
|
|
#endif // USE_INTEL_AESNI_LIBRARY
|
|
|
|
// Calculation of HMAC-SHA-1-96
|
|
void MacSha196(void *dst, void *key, void *data, UINT data_size)
|
|
{
|
|
UCHAR tmp[HMAC_SHA1_SIZE];
|
|
// Validate arguments
|
|
if (dst == NULL || key == NULL || data == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MacSha1(tmp, key, HMAC_SHA1_96_KEY_SIZE, data, data_size);
|
|
|
|
Copy(dst, tmp, HMAC_SHA1_96_HASH_SIZE);
|
|
}
|
|
|
|
// Calculation of HMAC-SHA-1
|
|
void MacSha1(void *dst, void *key, UINT key_size, void *data, UINT data_size)
|
|
{
|
|
UCHAR key_plus[SHA1_BLOCK_SIZE];
|
|
UCHAR key_plus2[SHA1_BLOCK_SIZE];
|
|
UCHAR key_plus5[SHA1_BLOCK_SIZE];
|
|
UCHAR hash4[SHA1_HASH_SIZE];
|
|
UINT i;
|
|
BUF *buf3;
|
|
BUF *buf6;
|
|
// Validate arguments
|
|
if (dst == NULL || key == NULL || data == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Zero(key_plus, sizeof(key_plus));
|
|
if (key_size <= SHA1_BLOCK_SIZE)
|
|
{
|
|
Copy(key_plus, key, key_size);
|
|
}
|
|
else
|
|
{
|
|
Sha1(key_plus, key, key_size);
|
|
}
|
|
|
|
for (i = 0;i < sizeof(key_plus);i++)
|
|
{
|
|
key_plus2[i] = key_plus[i] ^ 0x36;
|
|
}
|
|
|
|
buf3 = NewBuf();
|
|
WriteBuf(buf3, key_plus2, sizeof(key_plus2));
|
|
WriteBuf(buf3, data, data_size);
|
|
|
|
Sha1(hash4, buf3->Buf, buf3->Size);
|
|
|
|
for (i = 0;i < sizeof(key_plus);i++)
|
|
{
|
|
key_plus5[i] = key_plus[i] ^ 0x5c;
|
|
}
|
|
|
|
buf6 = NewBuf();
|
|
WriteBuf(buf6, key_plus5, sizeof(key_plus5));
|
|
WriteBuf(buf6, hash4, sizeof(hash4));
|
|
|
|
Sha1(dst, buf6->Buf, buf6->Size);
|
|
|
|
FreeBuf(buf3);
|
|
FreeBuf(buf6);
|
|
}
|
|
|
|
// DH calculation
|
|
bool DhCompute(DH_CTX *dh, void *dst_priv_key, void *src_pub_key, UINT key_size)
|
|
{
|
|
int i;
|
|
BIGNUM *bn;
|
|
bool ret = false;
|
|
// Validate arguments
|
|
if (dh == NULL || dst_priv_key == NULL || src_pub_key == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (key_size > dh->Size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bn = BinToBigNum(src_pub_key, key_size);
|
|
|
|
i = DH_compute_key(dst_priv_key, bn, dh->dh);
|
|
|
|
if (i == dh->Size)
|
|
{
|
|
ret = true;
|
|
}
|
|
else if ((UINT)i < dh->Size)
|
|
{
|
|
UCHAR *dst2 = Clone(dst_priv_key, i);
|
|
|
|
Zero(dst_priv_key, dh->Size);
|
|
|
|
Copy(((UCHAR *)dst_priv_key) + (dh->Size - i), dst2, i);
|
|
|
|
ret = true;
|
|
}
|
|
|
|
BN_free(bn);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Creating a DH 2048bit
|
|
DH_CTX *DhNew2048()
|
|
{
|
|
return DhNew(DH_SET_2048, 2);
|
|
}
|
|
// Creating a DH 3072bit
|
|
DH_CTX *DhNew3072()
|
|
{
|
|
return DhNew(DH_SET_3072, 2);
|
|
}
|
|
// Creating a DH 4096bit
|
|
DH_CTX *DhNew4096()
|
|
{
|
|
return DhNew(DH_SET_4096, 2);
|
|
}
|
|
|
|
// Creating a DH GROUP1
|
|
DH_CTX *DhNewGroup1()
|
|
{
|
|
return DhNew(DH_GROUP1_PRIME_768, 2);
|
|
}
|
|
|
|
// Creating a DH GROUP2
|
|
DH_CTX *DhNewGroup2()
|
|
{
|
|
return DhNew(DH_GROUP2_PRIME_1024, 2);
|
|
}
|
|
|
|
// Creating a DH GROUP5
|
|
DH_CTX *DhNewGroup5()
|
|
{
|
|
return DhNew(DH_GROUP5_PRIME_1536, 2);
|
|
}
|
|
|
|
// Creating a DH SIMPLE 160bits
|
|
DH_CTX *DhNewSimple160()
|
|
{
|
|
return DhNew(DH_SIMPLE_160, 2);
|
|
}
|
|
|
|
// Convert the DH parameters to file
|
|
BUF *DhToBuf(DH_CTX *dh)
|
|
{
|
|
BIO *bio;
|
|
BUF *buf = NULL;
|
|
int r;
|
|
// Validate arguments
|
|
if (dh == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
bio = NewBio();
|
|
|
|
r = i2d_DHparams_bio(bio, dh->dh);
|
|
if (r > 1)
|
|
{
|
|
buf = BioToBuf(bio);
|
|
}
|
|
|
|
FreeBio(bio);
|
|
|
|
return buf;
|
|
}
|
|
|
|
// Creating a new DH
|
|
DH_CTX *DhNew(char *prime, UINT g)
|
|
{
|
|
DH_CTX *dh;
|
|
BUF *buf;
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
BIGNUM *dhp, *dhg;
|
|
const BIGNUM *pub, *priv;
|
|
#endif
|
|
// Validate arguments
|
|
if (prime == NULL || g == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
buf = StrToBin(prime);
|
|
|
|
dh = ZeroMalloc(sizeof(DH_CTX));
|
|
|
|
dh->dh = DH_new();
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
dhp = BinToBigNum(buf->Buf, buf->Size);
|
|
dhg = BN_new();
|
|
BN_set_word(dhg, g);
|
|
DH_set0_pqg(dh->dh, dhp, NULL, dhg);
|
|
#else
|
|
dh->dh->p = BinToBigNum(buf->Buf, buf->Size);
|
|
dh->dh->g = BN_new();
|
|
BN_set_word(dh->dh->g, g);
|
|
#endif
|
|
|
|
DH_generate_key(dh->dh);
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
DH_get0_key(dh->dh, &pub, &priv);
|
|
dh->MyPublicKey = BigNumToBuf(pub);
|
|
dh->MyPrivateKey = BigNumToBuf(priv);
|
|
#else
|
|
dh->MyPublicKey = BigNumToBuf(dh->dh->pub_key);
|
|
dh->MyPrivateKey = BigNumToBuf(dh->dh->priv_key);
|
|
#endif
|
|
|
|
dh->Size = buf->Size;
|
|
|
|
FreeBuf(buf);
|
|
|
|
return dh;
|
|
}
|
|
|
|
// Release of DH
|
|
void DhFree(DH_CTX *dh)
|
|
{
|
|
// Validate arguments
|
|
if (dh == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DH_free(dh->dh);
|
|
|
|
FreeBuf(dh->MyPrivateKey);
|
|
FreeBuf(dh->MyPublicKey);
|
|
|
|
Free(dh);
|
|
}
|
|
|
|
|
|
/////////////////////////
|
|
// SHA0 implementation //
|
|
/////////////////////////
|
|
|
|
// Source codes from:
|
|
// https://android.googlesource.com/platform/system/core/+/81df1cc77722000f8d0025c1ab00ced123aa573c/libmincrypt/sha.c
|
|
// https://android.googlesource.com/platform/system/core/+/81df1cc77722000f8d0025c1ab00ced123aa573c/include/mincrypt/hash-internal.h
|
|
// https://android.googlesource.com/platform/system/core/+/81df1cc77722000f8d0025c1ab00ced123aa573c/include/mincrypt/sha.h
|
|
|
|
/*
|
|
* Copyright 2013 The Android Open Source Project
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Google Inc. nor the names of its contributors may
|
|
* be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
|
|
|
typedef struct MY_SHA0_CTX {
|
|
// const HASH_VTAB * f;
|
|
UINT64 count;
|
|
UCHAR buf[64];
|
|
UINT state[8]; // upto SHA2
|
|
} MY_SHA0_CTX;
|
|
|
|
#define MY_SHA0_DIGEST_SIZE 20
|
|
|
|
static void MY_SHA0_Transform(MY_SHA0_CTX* ctx) {
|
|
UINT W[80];
|
|
UINT A, B, C, D, E;
|
|
UCHAR* p = ctx->buf;
|
|
int t;
|
|
for(t = 0; t < 16; ++t) {
|
|
UINT tmp = *p++ << 24;
|
|
tmp |= *p++ << 16;
|
|
tmp |= *p++ << 8;
|
|
tmp |= *p++;
|
|
W[t] = tmp;
|
|
}
|
|
for(; t < 80; t++) {
|
|
//W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
|
W[t] = (1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
|
}
|
|
A = ctx->state[0];
|
|
B = ctx->state[1];
|
|
C = ctx->state[2];
|
|
D = ctx->state[3];
|
|
E = ctx->state[4];
|
|
for(t = 0; t < 80; t++) {
|
|
UINT tmp = rol(5,A) + E + W[t];
|
|
if (t < 20)
|
|
tmp += (D^(B&(C^D))) + 0x5A827999;
|
|
else if ( t < 40)
|
|
tmp += (B^C^D) + 0x6ED9EBA1;
|
|
else if ( t < 60)
|
|
tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC;
|
|
else
|
|
tmp += (B^C^D) + 0xCA62C1D6;
|
|
E = D;
|
|
D = C;
|
|
C = rol(30,B);
|
|
B = A;
|
|
A = tmp;
|
|
}
|
|
ctx->state[0] += A;
|
|
ctx->state[1] += B;
|
|
ctx->state[2] += C;
|
|
ctx->state[3] += D;
|
|
ctx->state[4] += E;
|
|
}
|
|
void MY_SHA0_init(MY_SHA0_CTX* ctx) {
|
|
//ctx->f = &SHA_VTAB;
|
|
ctx->state[0] = 0x67452301;
|
|
ctx->state[1] = 0xEFCDAB89;
|
|
ctx->state[2] = 0x98BADCFE;
|
|
ctx->state[3] = 0x10325476;
|
|
ctx->state[4] = 0xC3D2E1F0;
|
|
ctx->count = 0;
|
|
}
|
|
void MY_SHA0_update(MY_SHA0_CTX* ctx, const void* data, int len) {
|
|
int i = (int) (ctx->count & 63);
|
|
const UCHAR* p = (const UCHAR*)data;
|
|
ctx->count += len;
|
|
while (len--) {
|
|
ctx->buf[i++] = *p++;
|
|
if (i == 64) {
|
|
MY_SHA0_Transform(ctx);
|
|
i = 0;
|
|
}
|
|
}
|
|
}
|
|
const UCHAR* MY_SHA0_final(MY_SHA0_CTX* ctx) {
|
|
UCHAR *p = ctx->buf;
|
|
UINT64 cnt = ctx->count * 8;
|
|
int i;
|
|
MY_SHA0_update(ctx, (UCHAR*)"\x80", 1);
|
|
while ((ctx->count & 63) != 56) {
|
|
MY_SHA0_update(ctx, (UCHAR*)"\0", 1);
|
|
}
|
|
for (i = 0; i < 8; ++i) {
|
|
UCHAR tmp = (UCHAR) (cnt >> ((7 - i) * 8));
|
|
MY_SHA0_update(ctx, &tmp, 1);
|
|
}
|
|
for (i = 0; i < 5; i++) {
|
|
UINT tmp = ctx->state[i];
|
|
*p++ = tmp >> 24;
|
|
*p++ = tmp >> 16;
|
|
*p++ = tmp >> 8;
|
|
*p++ = tmp >> 0;
|
|
}
|
|
return ctx->buf;
|
|
}
|
|
/* Convenience function */
|
|
const UCHAR* MY_SHA0_hash(const void* data, int len, UCHAR* digest) {
|
|
MY_SHA0_CTX ctx;
|
|
MY_SHA0_init(&ctx);
|
|
MY_SHA0_update(&ctx, data, len);
|
|
memcpy(digest, MY_SHA0_final(&ctx), MY_SHA0_DIGEST_SIZE);
|
|
return digest;
|
|
}
|
|
static unsigned char *Internal_SHA0(const unsigned char *d, size_t n, unsigned char *md)
|
|
{
|
|
return (unsigned char *)MY_SHA0_hash(d, (int)n, md);
|
|
}
|
|
|
|
|
|
int GetSslClientCertIndex()
|
|
{
|
|
return ssl_clientcert_index;
|
|
}
|
|
|
|
|
|
|
|
//// RFC 8439: ChaCha20 and Poly1305 for IETF Protocols
|
|
//// Implementation from libsodium: https://github.com/jedisct1/libsodium
|
|
////
|
|
//// SoftEther VPN must support OpenSSL versions between 1.0.2 to the latest version.
|
|
//// Since we are unable to use ChaCha20 and Poly1305 on OpenSSL 1.0.x,
|
|
//// we copied the C implementation from libsodium.
|
|
//// Please note that the C implementation for ChaCha20 and Poly1305 is slow than
|
|
//// the OpenSSL 1.0.0 or later's implementation for ChaCha20 and Poly1305.
|
|
////
|
|
//// If OpenSSL 1.1.0 or later is linked, we use OpenSSL's ChaCha20 and Poly1305 implementation.
|
|
|
|
/*
|
|
* ISC License
|
|
*
|
|
* Copyright (c) 2013-2018
|
|
* Frank Denis <j at pureftpd dot org>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#ifdef OS_WIN32
|
|
#define inline __inline
|
|
#endif
|
|
|
|
#define poly1305_block_size 16
|
|
|
|
#define U32C(v) (v##U)
|
|
#define U32V(v) ((UINT)(v) &U32C(0xFFFFFFFF))
|
|
#define ROTATE(v, c) (ROTL32(v, c))
|
|
#define XOR(v, w) ((v) ^ (w))
|
|
#define PLUS(v, w) (U32V((v) + (w)))
|
|
#define PLUSONE(v) (PLUS((v), 1))
|
|
|
|
#define QUARTERROUND(a, b, c, d) \
|
|
a = PLUS(a, b); \
|
|
d = ROTATE(XOR(d, a), 16); \
|
|
c = PLUS(c, d); \
|
|
b = ROTATE(XOR(b, c), 12); \
|
|
a = PLUS(a, b); \
|
|
d = ROTATE(XOR(d, a), 8); \
|
|
c = PLUS(c, d); \
|
|
b = ROTATE(XOR(b, c), 7);
|
|
|
|
#define ROTL32(X, B) rotl32((X), (B))
|
|
static inline UINT
|
|
rotl32(const UINT x, const int b)
|
|
{
|
|
return (x << b) | (x >> (32 - b));
|
|
}
|
|
|
|
|
|
#define LOAD32_LE(SRC) load32_le(SRC)
|
|
|
|
static inline UINT
|
|
load32_le(const UCHAR src[4])
|
|
{
|
|
if (IsBigEndian() == false)
|
|
{
|
|
UINT w;
|
|
memcpy(&w, src, sizeof w);
|
|
return w;
|
|
}
|
|
else
|
|
{
|
|
UINT w = (UINT) src[0];
|
|
w |= (UINT) src[1] << 8;
|
|
w |= (UINT) src[2] << 16;
|
|
w |= (UINT) src[3] << 24;
|
|
return w;
|
|
}
|
|
}
|
|
|
|
#define STORE32_LE(DST, W) store32_le((DST), (W))
|
|
static inline void
|
|
store32_le(UCHAR dst[4], UINT w)
|
|
{
|
|
if (IsBigEndian() == false)
|
|
{
|
|
memcpy(dst, &w, sizeof w);
|
|
}
|
|
else
|
|
{
|
|
dst[0] = (UCHAR) w; w >>= 8;
|
|
dst[1] = (UCHAR) w; w >>= 8;
|
|
dst[2] = (UCHAR) w; w >>= 8;
|
|
dst[3] = (UCHAR) w;
|
|
}
|
|
}
|
|
|
|
|
|
#define LOAD64_LE(SRC) load64_le(SRC)
|
|
static inline UINT64
|
|
load64_le(const UCHAR src[8])
|
|
{
|
|
if (IsBigEndian() == false)
|
|
{
|
|
UINT64 w;
|
|
memcpy(&w, src, sizeof w);
|
|
return w;
|
|
}
|
|
else
|
|
{
|
|
UINT64 w = (UINT64) src[0];
|
|
w |= (UINT64) src[1] << 8;
|
|
w |= (UINT64) src[2] << 16;
|
|
w |= (UINT64) src[3] << 24;
|
|
w |= (UINT64) src[4] << 32;
|
|
w |= (UINT64) src[5] << 40;
|
|
w |= (UINT64) src[6] << 48;
|
|
w |= (UINT64) src[7] << 56;
|
|
return w;
|
|
}
|
|
}
|
|
|
|
#define STORE64_LE(DST, W) store64_le((DST), (W))
|
|
static inline void
|
|
store64_le(UCHAR dst[8], UINT64 w)
|
|
{
|
|
if (IsBigEndian() == false)
|
|
{
|
|
memcpy(dst, &w, sizeof w);
|
|
}
|
|
else
|
|
{
|
|
dst[0] = (UCHAR) w; w >>= 8;
|
|
dst[1] = (UCHAR) w; w >>= 8;
|
|
dst[2] = (UCHAR) w; w >>= 8;
|
|
dst[3] = (UCHAR) w; w >>= 8;
|
|
dst[4] = (UCHAR) w; w >>= 8;
|
|
dst[5] = (UCHAR) w; w >>= 8;
|
|
dst[6] = (UCHAR) w; w >>= 8;
|
|
dst[7] = (UCHAR) w;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
typedef struct chacha_ctx {
|
|
UINT input[16];
|
|
} chacha_ctx;
|
|
|
|
|
|
|
|
|
|
#define crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX \
|
|
(64ULL * (1ULL << 32))
|
|
|
|
typedef struct crypto_onetimeauth_poly1305_state {
|
|
unsigned char opaque[256];
|
|
} crypto_onetimeauth_poly1305_state;
|
|
|
|
/* 17 + sizeof(unsigned long long) + 14*sizeof(unsigned long) */
|
|
typedef struct poly1305_state_internal_t {
|
|
unsigned long r[5];
|
|
unsigned long h[5];
|
|
unsigned long pad[4];
|
|
unsigned long long leftover;
|
|
unsigned char buffer[poly1305_block_size];
|
|
unsigned char final;
|
|
} poly1305_state_internal_t;
|
|
static void
|
|
chacha_keysetup(chacha_ctx *ctx, const UCHAR *k)
|
|
{
|
|
ctx->input[0] = U32C(0x61707865);
|
|
ctx->input[1] = U32C(0x3320646e);
|
|
ctx->input[2] = U32C(0x79622d32);
|
|
ctx->input[3] = U32C(0x6b206574);
|
|
ctx->input[4] = LOAD32_LE(k + 0);
|
|
ctx->input[5] = LOAD32_LE(k + 4);
|
|
ctx->input[6] = LOAD32_LE(k + 8);
|
|
ctx->input[7] = LOAD32_LE(k + 12);
|
|
ctx->input[8] = LOAD32_LE(k + 16);
|
|
ctx->input[9] = LOAD32_LE(k + 20);
|
|
ctx->input[10] = LOAD32_LE(k + 24);
|
|
ctx->input[11] = LOAD32_LE(k + 28);
|
|
}
|
|
|
|
static void
|
|
chacha_ietf_ivsetup(chacha_ctx *ctx, const UCHAR *iv, const UCHAR *counter)
|
|
{
|
|
ctx->input[12] = counter == NULL ? 0 : LOAD32_LE(counter);
|
|
ctx->input[13] = LOAD32_LE(iv + 0);
|
|
ctx->input[14] = LOAD32_LE(iv + 4);
|
|
ctx->input[15] = LOAD32_LE(iv + 8);
|
|
}
|
|
|
|
static void
|
|
chacha20_encrypt_bytes(chacha_ctx *ctx, const UCHAR *m, UCHAR *c,
|
|
unsigned long long bytes)
|
|
{
|
|
UINT x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14,
|
|
x15;
|
|
UINT j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14,
|
|
j15;
|
|
UCHAR *ctarget = NULL;
|
|
UCHAR tmp[64];
|
|
unsigned int i;
|
|
|
|
if (!bytes) {
|
|
return; /* LCOV_EXCL_LINE */
|
|
}
|
|
j0 = ctx->input[0];
|
|
j1 = ctx->input[1];
|
|
j2 = ctx->input[2];
|
|
j3 = ctx->input[3];
|
|
j4 = ctx->input[4];
|
|
j5 = ctx->input[5];
|
|
j6 = ctx->input[6];
|
|
j7 = ctx->input[7];
|
|
j8 = ctx->input[8];
|
|
j9 = ctx->input[9];
|
|
j10 = ctx->input[10];
|
|
j11 = ctx->input[11];
|
|
j12 = ctx->input[12];
|
|
j13 = ctx->input[13];
|
|
j14 = ctx->input[14];
|
|
j15 = ctx->input[15];
|
|
|
|
for (;;) {
|
|
if (bytes < 64) {
|
|
memset(tmp, 0, 64);
|
|
for (i = 0; i < bytes; ++i) {
|
|
tmp[i] = m[i];
|
|
}
|
|
m = tmp;
|
|
ctarget = c;
|
|
c = tmp;
|
|
}
|
|
x0 = j0;
|
|
x1 = j1;
|
|
x2 = j2;
|
|
x3 = j3;
|
|
x4 = j4;
|
|
x5 = j5;
|
|
x6 = j6;
|
|
x7 = j7;
|
|
x8 = j8;
|
|
x9 = j9;
|
|
x10 = j10;
|
|
x11 = j11;
|
|
x12 = j12;
|
|
x13 = j13;
|
|
x14 = j14;
|
|
x15 = j15;
|
|
for (i = 20; i > 0; i -= 2) {
|
|
QUARTERROUND(x0, x4, x8, x12)
|
|
QUARTERROUND(x1, x5, x9, x13)
|
|
QUARTERROUND(x2, x6, x10, x14)
|
|
QUARTERROUND(x3, x7, x11, x15)
|
|
QUARTERROUND(x0, x5, x10, x15)
|
|
QUARTERROUND(x1, x6, x11, x12)
|
|
QUARTERROUND(x2, x7, x8, x13)
|
|
QUARTERROUND(x3, x4, x9, x14)
|
|
}
|
|
x0 = PLUS(x0, j0);
|
|
x1 = PLUS(x1, j1);
|
|
x2 = PLUS(x2, j2);
|
|
x3 = PLUS(x3, j3);
|
|
x4 = PLUS(x4, j4);
|
|
x5 = PLUS(x5, j5);
|
|
x6 = PLUS(x6, j6);
|
|
x7 = PLUS(x7, j7);
|
|
x8 = PLUS(x8, j8);
|
|
x9 = PLUS(x9, j9);
|
|
x10 = PLUS(x10, j10);
|
|
x11 = PLUS(x11, j11);
|
|
x12 = PLUS(x12, j12);
|
|
x13 = PLUS(x13, j13);
|
|
x14 = PLUS(x14, j14);
|
|
x15 = PLUS(x15, j15);
|
|
|
|
x0 = XOR(x0, LOAD32_LE(m + 0));
|
|
x1 = XOR(x1, LOAD32_LE(m + 4));
|
|
x2 = XOR(x2, LOAD32_LE(m + 8));
|
|
x3 = XOR(x3, LOAD32_LE(m + 12));
|
|
x4 = XOR(x4, LOAD32_LE(m + 16));
|
|
x5 = XOR(x5, LOAD32_LE(m + 20));
|
|
x6 = XOR(x6, LOAD32_LE(m + 24));
|
|
x7 = XOR(x7, LOAD32_LE(m + 28));
|
|
x8 = XOR(x8, LOAD32_LE(m + 32));
|
|
x9 = XOR(x9, LOAD32_LE(m + 36));
|
|
x10 = XOR(x10, LOAD32_LE(m + 40));
|
|
x11 = XOR(x11, LOAD32_LE(m + 44));
|
|
x12 = XOR(x12, LOAD32_LE(m + 48));
|
|
x13 = XOR(x13, LOAD32_LE(m + 52));
|
|
x14 = XOR(x14, LOAD32_LE(m + 56));
|
|
x15 = XOR(x15, LOAD32_LE(m + 60));
|
|
|
|
j12 = PLUSONE(j12);
|
|
/* LCOV_EXCL_START */
|
|
if (!j12) {
|
|
j13 = PLUSONE(j13);
|
|
}
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
STORE32_LE(c + 0, x0);
|
|
STORE32_LE(c + 4, x1);
|
|
STORE32_LE(c + 8, x2);
|
|
STORE32_LE(c + 12, x3);
|
|
STORE32_LE(c + 16, x4);
|
|
STORE32_LE(c + 20, x5);
|
|
STORE32_LE(c + 24, x6);
|
|
STORE32_LE(c + 28, x7);
|
|
STORE32_LE(c + 32, x8);
|
|
STORE32_LE(c + 36, x9);
|
|
STORE32_LE(c + 40, x10);
|
|
STORE32_LE(c + 44, x11);
|
|
STORE32_LE(c + 48, x12);
|
|
STORE32_LE(c + 52, x13);
|
|
STORE32_LE(c + 56, x14);
|
|
STORE32_LE(c + 60, x15);
|
|
|
|
if (bytes <= 64) {
|
|
if (bytes < 64) {
|
|
for (i = 0; i < (unsigned int) bytes; ++i) {
|
|
ctarget[i] = c[i]; /* ctarget cannot be NULL */
|
|
}
|
|
}
|
|
ctx->input[12] = j12;
|
|
ctx->input[13] = j13;
|
|
|
|
return;
|
|
}
|
|
bytes -= 64;
|
|
c += 64;
|
|
m += 64;
|
|
}
|
|
}
|
|
|
|
static int
|
|
stream_ietf_ext_ref(unsigned char *c, unsigned long long clen,
|
|
const unsigned char *n, const unsigned char *k)
|
|
{
|
|
struct chacha_ctx ctx;
|
|
|
|
if (!clen) {
|
|
return 0;
|
|
}
|
|
chacha_keysetup(&ctx, k);
|
|
chacha_ietf_ivsetup(&ctx, n, NULL);
|
|
memset(c, 0, (UINT)clen);
|
|
chacha20_encrypt_bytes(&ctx, c, c, clen);
|
|
Zero(&ctx, sizeof ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
crypto_stream_chacha20_ietf(unsigned char *c, unsigned long long clen,
|
|
const unsigned char *n, const unsigned char *k)
|
|
{
|
|
return stream_ietf_ext_ref(c, clen, n, k);
|
|
}
|
|
|
|
static void
|
|
poly1305_init(poly1305_state_internal_t *st, const unsigned char key[32])
|
|
{
|
|
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff - wiped after finalization */
|
|
st->r[0] = (LOAD32_LE(&key[0])) & 0x3ffffff;
|
|
st->r[1] = (LOAD32_LE(&key[3]) >> 2) & 0x3ffff03;
|
|
st->r[2] = (LOAD32_LE(&key[6]) >> 4) & 0x3ffc0ff;
|
|
st->r[3] = (LOAD32_LE(&key[9]) >> 6) & 0x3f03fff;
|
|
st->r[4] = (LOAD32_LE(&key[12]) >> 8) & 0x00fffff;
|
|
|
|
/* h = 0 */
|
|
st->h[0] = 0;
|
|
st->h[1] = 0;
|
|
st->h[2] = 0;
|
|
st->h[3] = 0;
|
|
st->h[4] = 0;
|
|
|
|
/* save pad for later */
|
|
st->pad[0] = LOAD32_LE(&key[16]);
|
|
st->pad[1] = LOAD32_LE(&key[20]);
|
|
st->pad[2] = LOAD32_LE(&key[24]);
|
|
st->pad[3] = LOAD32_LE(&key[28]);
|
|
|
|
st->leftover = 0;
|
|
st->final = 0;
|
|
}
|
|
|
|
static void
|
|
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m,
|
|
unsigned long long bytes)
|
|
{
|
|
const unsigned long hibit = (st->final) ? 0UL : (1UL << 24); /* 1 << 128 */
|
|
unsigned long r0, r1, r2, r3, r4;
|
|
unsigned long s1, s2, s3, s4;
|
|
unsigned long h0, h1, h2, h3, h4;
|
|
unsigned long long d0, d1, d2, d3, d4;
|
|
unsigned long c;
|
|
|
|
r0 = st->r[0];
|
|
r1 = st->r[1];
|
|
r2 = st->r[2];
|
|
r3 = st->r[3];
|
|
r4 = st->r[4];
|
|
|
|
s1 = r1 * 5;
|
|
s2 = r2 * 5;
|
|
s3 = r3 * 5;
|
|
s4 = r4 * 5;
|
|
|
|
h0 = st->h[0];
|
|
h1 = st->h[1];
|
|
h2 = st->h[2];
|
|
h3 = st->h[3];
|
|
h4 = st->h[4];
|
|
|
|
while (bytes >= poly1305_block_size) {
|
|
/* h += m[i] */
|
|
h0 += (LOAD32_LE(m + 0)) & 0x3ffffff;
|
|
h1 += (LOAD32_LE(m + 3) >> 2) & 0x3ffffff;
|
|
h2 += (LOAD32_LE(m + 6) >> 4) & 0x3ffffff;
|
|
h3 += (LOAD32_LE(m + 9) >> 6) & 0x3ffffff;
|
|
h4 += (LOAD32_LE(m + 12) >> 8) | hibit;
|
|
|
|
/* h *= r */
|
|
d0 = ((unsigned long long) h0 * r0) + ((unsigned long long) h1 * s4) +
|
|
((unsigned long long) h2 * s3) + ((unsigned long long) h3 * s2) +
|
|
((unsigned long long) h4 * s1);
|
|
d1 = ((unsigned long long) h0 * r1) + ((unsigned long long) h1 * r0) +
|
|
((unsigned long long) h2 * s4) + ((unsigned long long) h3 * s3) +
|
|
((unsigned long long) h4 * s2);
|
|
d2 = ((unsigned long long) h0 * r2) + ((unsigned long long) h1 * r1) +
|
|
((unsigned long long) h2 * r0) + ((unsigned long long) h3 * s4) +
|
|
((unsigned long long) h4 * s3);
|
|
d3 = ((unsigned long long) h0 * r3) + ((unsigned long long) h1 * r2) +
|
|
((unsigned long long) h2 * r1) + ((unsigned long long) h3 * r0) +
|
|
((unsigned long long) h4 * s4);
|
|
d4 = ((unsigned long long) h0 * r4) + ((unsigned long long) h1 * r3) +
|
|
((unsigned long long) h2 * r2) + ((unsigned long long) h3 * r1) +
|
|
((unsigned long long) h4 * r0);
|
|
|
|
/* (partial) h %= p */
|
|
c = (unsigned long) (d0 >> 26);
|
|
h0 = (unsigned long) d0 & 0x3ffffff;
|
|
d1 += c;
|
|
c = (unsigned long) (d1 >> 26);
|
|
h1 = (unsigned long) d1 & 0x3ffffff;
|
|
d2 += c;
|
|
c = (unsigned long) (d2 >> 26);
|
|
h2 = (unsigned long) d2 & 0x3ffffff;
|
|
d3 += c;
|
|
c = (unsigned long) (d3 >> 26);
|
|
h3 = (unsigned long) d3 & 0x3ffffff;
|
|
d4 += c;
|
|
c = (unsigned long) (d4 >> 26);
|
|
h4 = (unsigned long) d4 & 0x3ffffff;
|
|
h0 += c * 5;
|
|
c = (h0 >> 26);
|
|
h0 = h0 & 0x3ffffff;
|
|
h1 += c;
|
|
|
|
m += poly1305_block_size;
|
|
bytes -= poly1305_block_size;
|
|
}
|
|
|
|
st->h[0] = h0;
|
|
st->h[1] = h1;
|
|
st->h[2] = h2;
|
|
st->h[3] = h3;
|
|
st->h[4] = h4;
|
|
}
|
|
|
|
static void
|
|
poly1305_update(poly1305_state_internal_t *st, const unsigned char *m,
|
|
unsigned long long bytes)
|
|
{
|
|
unsigned long long i;
|
|
|
|
/* handle leftover */
|
|
if (st->leftover) {
|
|
unsigned long long want = (poly1305_block_size - st->leftover);
|
|
|
|
if (want > bytes) {
|
|
want = bytes;
|
|
}
|
|
for (i = 0; i < want; i++) {
|
|
st->buffer[st->leftover + i] = m[i];
|
|
}
|
|
bytes -= want;
|
|
m += want;
|
|
st->leftover += want;
|
|
if (st->leftover < poly1305_block_size) {
|
|
return;
|
|
}
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
st->leftover = 0;
|
|
}
|
|
|
|
/* process full blocks */
|
|
if (bytes >= poly1305_block_size) {
|
|
unsigned long long want = (bytes & ~(poly1305_block_size - 1));
|
|
|
|
poly1305_blocks(st, m, want);
|
|
m += want;
|
|
bytes -= want;
|
|
}
|
|
|
|
/* store leftover */
|
|
if (bytes) {
|
|
for (i = 0; i < bytes; i++) {
|
|
st->buffer[st->leftover + i] = m[i];
|
|
}
|
|
st->leftover += bytes;
|
|
}
|
|
}
|
|
|
|
static int
|
|
crypto_onetimeauth_poly1305_init(crypto_onetimeauth_poly1305_state *state,
|
|
const unsigned char *key)
|
|
{
|
|
poly1305_init((poly1305_state_internal_t *) (void *) state, key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
crypto_onetimeauth_poly1305_update(
|
|
crypto_onetimeauth_poly1305_state *state, const unsigned char *in,
|
|
unsigned long long inlen)
|
|
{
|
|
poly1305_update((poly1305_state_internal_t *) (void *) state, in, inlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
stream_ietf_ext_ref_xor_ic(unsigned char *c, const unsigned char *m,
|
|
unsigned long long mlen, const unsigned char *n,
|
|
UINT ic, const unsigned char *k)
|
|
{
|
|
struct chacha_ctx ctx;
|
|
UCHAR ic_bytes[4];
|
|
|
|
if (!mlen) {
|
|
return 0;
|
|
}
|
|
STORE32_LE(ic_bytes, ic);
|
|
chacha_keysetup(&ctx, k);
|
|
chacha_ietf_ivsetup(&ctx, n, ic_bytes);
|
|
chacha20_encrypt_bytes(&ctx, m, c, mlen);
|
|
Zero(&ctx, sizeof ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
crypto_stream_chacha20_ietf_xor_ic(unsigned char *c, const unsigned char *m,
|
|
unsigned long long mlen,
|
|
const unsigned char *n, UINT ic,
|
|
const unsigned char *k)
|
|
{
|
|
return stream_ietf_ext_ref_xor_ic(c, m, mlen, n, ic, k);
|
|
}
|
|
|
|
|
|
static void
|
|
poly1305_finish(poly1305_state_internal_t *st, unsigned char mac[16])
|
|
{
|
|
unsigned long h0, h1, h2, h3, h4, c;
|
|
unsigned long g0, g1, g2, g3, g4;
|
|
unsigned long long f;
|
|
unsigned long mask;
|
|
|
|
/* process the remaining block */
|
|
if (st->leftover) {
|
|
unsigned long long i = st->leftover;
|
|
|
|
st->buffer[i++] = 1;
|
|
for (; i < poly1305_block_size; i++) {
|
|
st->buffer[i] = 0;
|
|
}
|
|
st->final = 1;
|
|
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
|
}
|
|
|
|
/* fully carry h */
|
|
h0 = st->h[0];
|
|
h1 = st->h[1];
|
|
h2 = st->h[2];
|
|
h3 = st->h[3];
|
|
h4 = st->h[4];
|
|
|
|
c = h1 >> 26;
|
|
h1 = h1 & 0x3ffffff;
|
|
h2 += c;
|
|
c = h2 >> 26;
|
|
h2 = h2 & 0x3ffffff;
|
|
h3 += c;
|
|
c = h3 >> 26;
|
|
h3 = h3 & 0x3ffffff;
|
|
h4 += c;
|
|
c = h4 >> 26;
|
|
h4 = h4 & 0x3ffffff;
|
|
h0 += c * 5;
|
|
c = h0 >> 26;
|
|
h0 = h0 & 0x3ffffff;
|
|
h1 += c;
|
|
|
|
/* compute h + -p */
|
|
g0 = h0 + 5;
|
|
c = g0 >> 26;
|
|
g0 &= 0x3ffffff;
|
|
g1 = h1 + c;
|
|
c = g1 >> 26;
|
|
g1 &= 0x3ffffff;
|
|
g2 = h2 + c;
|
|
c = g2 >> 26;
|
|
g2 &= 0x3ffffff;
|
|
g3 = h3 + c;
|
|
c = g3 >> 26;
|
|
g3 &= 0x3ffffff;
|
|
g4 = h4 + c - (1UL << 26);
|
|
|
|
/* select h if h < p, or h + -p if h >= p */
|
|
mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
|
|
g0 &= mask;
|
|
g1 &= mask;
|
|
g2 &= mask;
|
|
g3 &= mask;
|
|
g4 &= mask;
|
|
mask = ~mask;
|
|
|
|
h0 = (h0 & mask) | g0;
|
|
h1 = (h1 & mask) | g1;
|
|
h2 = (h2 & mask) | g2;
|
|
h3 = (h3 & mask) | g3;
|
|
h4 = (h4 & mask) | g4;
|
|
|
|
/* h = h % (2^128) */
|
|
h0 = ((h0) | (h1 << 26)) & 0xffffffff;
|
|
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
|
|
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
|
|
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
|
|
|
|
/* mac = (h + pad) % (2^128) */
|
|
f = (unsigned long long) h0 + st->pad[0];
|
|
h0 = (unsigned long) f;
|
|
f = (unsigned long long) h1 + st->pad[1] + (f >> 32);
|
|
h1 = (unsigned long) f;
|
|
f = (unsigned long long) h2 + st->pad[2] + (f >> 32);
|
|
h2 = (unsigned long) f;
|
|
f = (unsigned long long) h3 + st->pad[3] + (f >> 32);
|
|
h3 = (unsigned long) f;
|
|
|
|
STORE32_LE(mac + 0, (UINT) h0);
|
|
STORE32_LE(mac + 4, (UINT) h1);
|
|
STORE32_LE(mac + 8, (UINT) h2);
|
|
STORE32_LE(mac + 12, (UINT) h3);
|
|
|
|
/* zero out the state */
|
|
Zero((void *) st, sizeof *st);
|
|
}
|
|
|
|
static int
|
|
crypto_onetimeauth_poly1305_final(
|
|
crypto_onetimeauth_poly1305_state *state, unsigned char *out)
|
|
{
|
|
poly1305_finish((poly1305_state_internal_t *) (void *) state, out);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const unsigned char _pad0[16] = { 0 };
|
|
|
|
int
|
|
crypto_aead_chacha20poly1305_ietf_encrypt_detached(unsigned char *c,
|
|
unsigned char *mac,
|
|
unsigned long long *maclen_p,
|
|
const unsigned char *m,
|
|
unsigned long long mlen,
|
|
const unsigned char *ad,
|
|
unsigned long long adlen,
|
|
const unsigned char *nsec,
|
|
const unsigned char *npub,
|
|
const unsigned char *k)
|
|
{
|
|
crypto_onetimeauth_poly1305_state state;
|
|
unsigned char block0[64U];
|
|
unsigned char slen[8U];
|
|
|
|
(void) nsec;
|
|
Zero(block0, sizeof block0);
|
|
crypto_stream_chacha20_ietf(block0, sizeof block0, npub, k);
|
|
crypto_onetimeauth_poly1305_init(&state, block0);
|
|
|
|
crypto_onetimeauth_poly1305_update(&state, ad, adlen);
|
|
crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - adlen) & 0xf);
|
|
|
|
crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, npub, 1U, k);
|
|
|
|
crypto_onetimeauth_poly1305_update(&state, c, mlen);
|
|
crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - mlen) & 0xf);
|
|
|
|
STORE64_LE(slen, (UINT64) adlen);
|
|
crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
|
|
|
|
STORE64_LE(slen, (UINT64) mlen);
|
|
crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
|
|
|
|
crypto_onetimeauth_poly1305_final(&state, mac);
|
|
Zero(&state, sizeof state);
|
|
|
|
if (maclen_p != NULL) {
|
|
*maclen_p = 16;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
crypto_aead_chacha20poly1305_ietf_decrypt_detached(unsigned char *m,
|
|
unsigned char *nsec,
|
|
const unsigned char *c,
|
|
unsigned long long clen,
|
|
const unsigned char *mac,
|
|
const unsigned char *ad,
|
|
unsigned long long adlen,
|
|
const unsigned char *npub,
|
|
const unsigned char *k)
|
|
{
|
|
crypto_onetimeauth_poly1305_state state;
|
|
unsigned char block0[64U];
|
|
unsigned char slen[8U];
|
|
unsigned char computed_mac[16];
|
|
unsigned long long mlen;
|
|
int ret;
|
|
|
|
(void) nsec;
|
|
Zero(block0, sizeof block0);
|
|
crypto_stream_chacha20_ietf(block0, sizeof block0, npub, k);
|
|
crypto_onetimeauth_poly1305_init(&state, block0);
|
|
|
|
crypto_onetimeauth_poly1305_update(&state, ad, adlen);
|
|
crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - adlen) & 0xf);
|
|
|
|
mlen = clen;
|
|
crypto_onetimeauth_poly1305_update(&state, c, mlen);
|
|
crypto_onetimeauth_poly1305_update(&state, _pad0, (0x10 - mlen) & 0xf);
|
|
|
|
STORE64_LE(slen, (UINT64) adlen);
|
|
crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
|
|
|
|
STORE64_LE(slen, (UINT64) mlen);
|
|
crypto_onetimeauth_poly1305_update(&state, slen, sizeof slen);
|
|
|
|
crypto_onetimeauth_poly1305_final(&state, computed_mac);
|
|
Zero(&state, sizeof state);
|
|
|
|
ret = Cmp((void *)computed_mac, (void *)mac, 16);
|
|
Zero(computed_mac, sizeof computed_mac);
|
|
if (m == NULL) {
|
|
return ret;
|
|
}
|
|
if (ret != 0) {
|
|
memset(m, 0, (UINT)mlen);
|
|
return -1;
|
|
}
|
|
crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, npub, 1U, k);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
crypto_aead_chacha20poly1305_ietf_decrypt(unsigned char *m,
|
|
unsigned long long *mlen_p,
|
|
unsigned char *nsec,
|
|
const unsigned char *c,
|
|
unsigned long long clen,
|
|
const unsigned char *ad,
|
|
unsigned long long adlen,
|
|
const unsigned char *npub,
|
|
const unsigned char *k)
|
|
{
|
|
unsigned long long mlen = 0ULL;
|
|
int ret = -1;
|
|
|
|
if (clen >= 16) {
|
|
ret = crypto_aead_chacha20poly1305_ietf_decrypt_detached
|
|
(m, nsec,
|
|
c, clen - 16,
|
|
c + clen - AEAD_CHACHA20_POLY1305_MAC_SIZE,
|
|
ad, adlen, npub, k);
|
|
}
|
|
if (mlen_p != NULL) {
|
|
if (ret == 0) {
|
|
mlen = clen - AEAD_CHACHA20_POLY1305_MAC_SIZE;
|
|
}
|
|
*mlen_p = mlen;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int
|
|
crypto_aead_chacha20poly1305_ietf_encrypt(unsigned char *c,
|
|
unsigned long long *clen_p,
|
|
const unsigned char *m,
|
|
unsigned long long mlen,
|
|
const unsigned char *ad,
|
|
unsigned long long adlen,
|
|
const unsigned char *nsec,
|
|
const unsigned char *npub,
|
|
const unsigned char *k)
|
|
{
|
|
unsigned long long clen = 0ULL;
|
|
int ret;
|
|
|
|
ret = crypto_aead_chacha20poly1305_ietf_encrypt_detached(c,
|
|
c + mlen, NULL,
|
|
m, mlen,
|
|
ad, adlen,
|
|
nsec, npub, k);
|
|
if (clen_p != NULL) {
|
|
if (ret == 0) {
|
|
clen = mlen + AEAD_CHACHA20_POLY1305_MAC_SIZE;
|
|
}
|
|
*clen_p = clen;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// OpenSSL 3.0.0 to 3.0.2 has a bug with RC4-MD5.
|
|
// See: https://github.com/openssl/openssl/issues/13363 https://github.com/openssl/openssl/pull/13378
|
|
|
|
static bool ssl_is_rc4md5_buggy_version = false;
|
|
static bool ssl_has_cache_is_rc4md5_buggy_version = false;
|
|
|
|
bool IsSslLibVersionBuggyForRc4Md5()
|
|
{
|
|
bool ret = false;
|
|
if (ssl_has_cache_is_rc4md5_buggy_version)
|
|
{
|
|
return ssl_is_rc4md5_buggy_version;
|
|
}
|
|
|
|
ret = IsSslLibVersionBuggyForRc4Md5_Internal();
|
|
|
|
ssl_is_rc4md5_buggy_version = ret;
|
|
ssl_has_cache_is_rc4md5_buggy_version = true;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool IsSslLibVersionBuggyForRc4Md5_Internal()
|
|
{
|
|
UINT verint = 0;
|
|
UINT ver_major = 0;
|
|
UINT ver_minor = 0;
|
|
UINT ver_fix = 0;
|
|
UINT ver_patch = 0;
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
DoNothing();
|
|
#else // OPENSSL_VERSION_NUMBER
|
|
verint = OpenSSL_version_num();
|
|
|
|
ver_major = (verint >> 28) & 0x0F;
|
|
ver_minor = (verint >> 20) & 0xFF;
|
|
ver_fix = (verint >> 12) & 0xFF;
|
|
ver_patch = (verint >> 4) & 0xFF;
|
|
#endif // OPENSSL_VERSION_NUMBER
|
|
|
|
if (ver_major == 3 && ver_minor == 0)
|
|
{
|
|
if (ver_patch <= 2)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static char ssl_version_cache[MAX_PATH] = CLEAN;
|
|
|
|
void GetSslLibVersion(char *str, UINT size)
|
|
{
|
|
if (IsEmptyStr(ssl_version_cache))
|
|
{
|
|
GetSslLibVersion_Internal(ssl_version_cache, sizeof(ssl_version_cache));
|
|
}
|
|
|
|
StrCpy(str, size, ssl_version_cache);
|
|
}
|
|
|
|
void GetSslLibVersion_Internal(char *str, UINT size)
|
|
{
|
|
char tmp[MAX_PATH] = CLEAN;
|
|
UINT verint = 0;
|
|
UINT ver_major = 0;
|
|
UINT ver_minor = 0;
|
|
UINT ver_fix = 0;
|
|
UINT ver_patch = 0;
|
|
if (str == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
Format(tmp, sizeof(tmp), "OpenSSL <= 1.0.2");
|
|
#else // OPENSSL_VERSION_NUMBER
|
|
verint = OpenSSL_version_num();
|
|
|
|
ver_major = (verint >> 28) & 0x0F;
|
|
ver_minor = (verint >> 20) & 0xFF;
|
|
ver_fix = (verint >> 12) & 0xFF;
|
|
ver_patch = (verint >> 4) & 0xFF;
|
|
|
|
if (ver_major >= 3)
|
|
{
|
|
Format(tmp, sizeof(tmp), "OpenSSL %u.%u.%u", ver_major, ver_minor, ver_patch);
|
|
}
|
|
else
|
|
{
|
|
char c = 0;
|
|
if (ver_patch >= 1)
|
|
{
|
|
c = 'a' + (ver_patch - 1);
|
|
}
|
|
Format(tmp, sizeof(tmp), "OpenSSL %u.%u.%u%c", ver_major, ver_minor, ver_fix, c);
|
|
}
|
|
|
|
#endif // OPENSSL_VERSION_NUMBER
|
|
|
|
StrCpy(str, size, tmp);
|
|
}
|
|
|
|
// RFC 8439: ChaCha20-Poly1305-IETF Encryption with AEAD
|
|
void Aead_ChaCha20Poly1305_Ietf_Encrypt(void *dst, void *src, UINT src_size,
|
|
void *key, void *nonce, void *aad, UINT aad_size)
|
|
{
|
|
#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
Aead_ChaCha20Poly1305_Ietf_Encrypt_OpenSSL(dst, src, src_size, key, nonce, aad, aad_size);
|
|
#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
Aead_ChaCha20Poly1305_Ietf_Encrypt_Embedded(dst, src, src_size, key, nonce, aad, aad_size);
|
|
#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
}
|
|
void Aead_ChaCha20Poly1305_Ietf_Encrypt_OpenSSL(void *dst, void *src, UINT src_size,
|
|
void *key, void *nonce, void *aad, UINT aad_size)
|
|
{
|
|
#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
EVP_CIPHER_CTX *ctx;
|
|
int outlen = 0;
|
|
|
|
if ((src_size != 0 && (dst == NULL || src == NULL)) ||
|
|
key == NULL || nonce == NULL ||
|
|
(aad_size != 0 && aad == NULL))
|
|
{
|
|
Zero(dst, src_size);
|
|
return;
|
|
}
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
|
|
EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, AEAD_CHACHA20_POLY1305_NONCE_SIZE, 0);
|
|
EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce);
|
|
EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_size);
|
|
EVP_EncryptUpdate(ctx, dst, &outlen, src, src_size);
|
|
EVP_EncryptFinal_ex(ctx, dst, &outlen);
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, AEAD_CHACHA20_POLY1305_MAC_SIZE,
|
|
((UCHAR *)dst) + src_size);
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
}
|
|
void Aead_ChaCha20Poly1305_Ietf_Encrypt_Embedded(void *dst, void *src, UINT src_size,
|
|
void *key, void *nonce, void *aad, UINT aad_size)
|
|
{
|
|
if ((src_size != 0 && (dst == NULL || src == NULL)) ||
|
|
key == NULL || nonce == NULL ||
|
|
(aad_size != 0 && aad == NULL))
|
|
{
|
|
Zero(dst, src_size);
|
|
return;
|
|
}
|
|
crypto_aead_chacha20poly1305_ietf_encrypt(dst, NULL, src, src_size, aad, aad_size,
|
|
NULL, nonce, key);
|
|
}
|
|
|
|
// RFC 8439: ChaCha20-Poly1305-IETF Decryption with AEAD
|
|
bool Aead_ChaCha20Poly1305_Ietf_Decrypt(void *dst, void *src, UINT src_size, void *key, void *nonce, void *aad, UINT aad_size)
|
|
{
|
|
#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
return Aead_ChaCha20Poly1305_Ietf_Decrypt_OpenSSL(dst, src, src_size, key,
|
|
nonce, aad, aad_size);
|
|
#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
return Aead_ChaCha20Poly1305_Ietf_Decrypt_Embedded(dst, src, src_size, key,
|
|
nonce, aad, aad_size);
|
|
#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
}
|
|
bool Aead_ChaCha20Poly1305_Ietf_Decrypt_OpenSSL(void *dst, void *src, UINT src_size, void *key,
|
|
void *nonce, void *aad, UINT aad_size)
|
|
{
|
|
#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
EVP_CIPHER_CTX *ctx;
|
|
int outlen = 0;
|
|
bool ret = false;
|
|
|
|
if ((src_size != 0 && (dst == NULL || src == NULL)) ||
|
|
key == NULL || nonce == NULL ||
|
|
(aad_size != 0 && aad == NULL) ||
|
|
(src_size < AEAD_CHACHA20_POLY1305_MAC_SIZE))
|
|
{
|
|
Zero(dst, src_size);
|
|
return false;
|
|
}
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
|
|
EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), 0, 0, 0);
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, AEAD_CHACHA20_POLY1305_NONCE_SIZE, 0);
|
|
|
|
if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce) == 1)
|
|
{
|
|
if (EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_size) == 1)
|
|
{
|
|
if (EVP_DecryptUpdate(ctx, dst, &outlen, src, src_size - AEAD_CHACHA20_POLY1305_MAC_SIZE) == 1)
|
|
{
|
|
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, AEAD_CHACHA20_POLY1305_MAC_SIZE,
|
|
((UCHAR *)src) + (src_size - AEAD_CHACHA20_POLY1305_MAC_SIZE));
|
|
|
|
if (EVP_DecryptFinal_ex(ctx, dst, &outlen))
|
|
{
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return ret;
|
|
#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
return false;
|
|
#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
}
|
|
bool Aead_ChaCha20Poly1305_Ietf_Decrypt_Embedded(void *dst, void *src, UINT src_size, void *key,
|
|
void *nonce, void *aad, UINT aad_size)
|
|
{
|
|
int ret;
|
|
if ((src_size != 0 && (dst == NULL || src == NULL)) ||
|
|
key == NULL || nonce == NULL ||
|
|
(aad_size != 0 && aad == NULL) ||
|
|
(src_size < AEAD_CHACHA20_POLY1305_MAC_SIZE))
|
|
{
|
|
Zero(dst, src_size);
|
|
return false;
|
|
}
|
|
|
|
ret = crypto_aead_chacha20poly1305_ietf_decrypt(
|
|
dst, NULL, NULL, src, src_size, aad, aad_size, nonce, key);
|
|
|
|
if (ret == -1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Aead_ChaCha20Poly1305_Ietf_IsOpenSSL()
|
|
{
|
|
#ifdef USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
return true;
|
|
#else // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
return false;
|
|
#endif // USE_OPENSSL_AEAD_CHACHA20POLY1305
|
|
}
|
|
|
|
// RFC 8439: ChaCha20-Poly1305-IETF AEAD Test
|
|
void Aead_ChaCha20Poly1305_Ietf_Test()
|
|
{
|
|
char *nonce_hex = "07 00 00 00 40 41 42 43 44 45 46 47";
|
|
char *plaintext_hex =
|
|
"4c 61 64 69 65 73 20 61 6e 64 20 47 65 6e 74 6c "
|
|
"65 6d 65 6e 20 6f 66 20 74 68 65 20 63 6c 61 73 "
|
|
"73 20 6f 66 20 27 39 39 3a 20 49 66 20 49 20 63 "
|
|
"6f 75 6c 64 20 6f 66 66 65 72 20 79 6f 75 20 6f "
|
|
"6e 6c 79 20 6f 6e 65 20 74 69 70 20 66 6f 72 20 "
|
|
"74 68 65 20 66 75 74 75 72 65 2c 20 73 75 6e 73 "
|
|
"63 72 65 65 6e 20 77 6f 75 6c 64 20 62 65 20 69 "
|
|
"74 2e";
|
|
char *aad_hex = "50 51 52 53 c0 c1 c2 c3 c4 c5 c6 c7";
|
|
char *key_hex =
|
|
"80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f "
|
|
"90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f";
|
|
BUF *nonce = StrToBin(nonce_hex);
|
|
BUF *plaintext = StrToBin(plaintext_hex);
|
|
BUF *aad = StrToBin(aad_hex);
|
|
BUF *key = StrToBin(key_hex);
|
|
UINT plaintext_size = plaintext->Size;
|
|
UCHAR *encrypted = Malloc(plaintext_size + AEAD_CHACHA20_POLY1305_MAC_SIZE);
|
|
UCHAR *decrypted = Malloc(plaintext_size);
|
|
char encrypted_hex[MAX_SIZE];
|
|
char mac_hex[MAX_SIZE];
|
|
|
|
Print("Aead_ChaCha20Poly1305_Ietf_Test()\n\n");
|
|
|
|
Aead_ChaCha20Poly1305_Ietf_Encrypt(encrypted, plaintext->Buf, plaintext_size,
|
|
key->Buf, nonce->Buf, aad->Buf, aad->Size);
|
|
|
|
BinToStrEx(encrypted_hex, sizeof(encrypted_hex), encrypted, plaintext_size);
|
|
|
|
BinToStrEx(mac_hex, sizeof(mac_hex), encrypted + plaintext_size, AEAD_CHACHA20_POLY1305_MAC_SIZE);
|
|
|
|
Print("Encrypted:\n%s\n\n", encrypted_hex);
|
|
|
|
Print("MAC:\n%s\n\n", mac_hex);
|
|
|
|
Print("Please check the results with https://tools.ietf.org/html/rfc8439#section-2.8.2 by your great eyes.\n\n");
|
|
|
|
if (Aead_ChaCha20Poly1305_Ietf_Decrypt(decrypted, encrypted, plaintext_size + AEAD_CHACHA20_POLY1305_MAC_SIZE,
|
|
key->Buf, nonce->Buf, aad->Buf, aad->Size) == false)
|
|
{
|
|
Print("Decrypt failed.\n");
|
|
}
|
|
else
|
|
{
|
|
Print("Decrypt OK.\n");
|
|
if (Cmp(plaintext->Buf, decrypted, plaintext_size) == 0)
|
|
{
|
|
Print("Same OK.\n");
|
|
}
|
|
else
|
|
{
|
|
Print("Different !!!\n");
|
|
}
|
|
}
|
|
|
|
FreeBuf(nonce);
|
|
FreeBuf(plaintext);
|
|
FreeBuf(aad);
|
|
FreeBuf(key);
|
|
Free(encrypted);
|
|
Free(decrypted);
|
|
}
|