X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=crypto.c;h=21b70954fc9045e163faf138591ece7bc0828051;hp=a7bbafbc0f648d8343db57bc736b88c9a964d5dd;hb=c573a47a538a3a90cb34ccf2e3e21a0d1b129d20;hpb=31fb67a67a5b78635cd738ab9e620f303bc41549 diff --git a/crypto.c b/crypto.c index a7bbafbc..21b70954 100644 --- a/crypto.c +++ b/crypto.c @@ -1,4 +1,24 @@ -// TODO key loading, generating, saving +/* +Copyright (C) 2010-2015 Rudolf Polzer (divVerent) +Copyright (C) 2010-2020 Ashley Rose Hale (LadyHavoc) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + #include "quakedef.h" #include "crypto.h" #include "common.h" @@ -7,19 +27,22 @@ #include "hmac.h" #include "libcurl.h" -void *crypto_mutex = NULL; +cvar_t crypto_developer = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_developer", "0", "print extra info about crypto handshake"}; +cvar_t crypto_aeslevel = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; -cvar_t crypto_developer = {CVAR_SAVE, "crypto_developer", "0", "print extra info about crypto handshake"}; -cvar_t crypto_servercpupercent = {CVAR_SAVE, "crypto_servercpupercent", "10", "allowed crypto CPU load in percent for server operation (0 = no limit, faster)"}; -cvar_t crypto_servercpumaxtime = {CVAR_SAVE, "crypto_servercpumaxtime", "0.01", "maximum allowed crypto CPU time per frame (0 = no limit)"}; -cvar_t crypto_servercpudebug = {CVAR_SAVE, "crypto_servercpudebug", "0", "print statistics about time usage by crypto"}; +cvar_t crypto_servercpupercent = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_servercpupercent", "10", "allowed crypto CPU load in percent for server operation (0 = no limit, faster)"}; +cvar_t crypto_servercpumaxtime = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_servercpumaxtime", "0.01", "maximum allowed crypto CPU time per frame (0 = no limit)"}; +cvar_t crypto_servercpudebug = {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "crypto_servercpudebug", "0", "print statistics about time usage by crypto"}; static double crypto_servercpu_accumulator = 0; static double crypto_servercpu_lastrealtime = 0; -cvar_t crypto_aeslevel = {CVAR_SAVE, "crypto_aeslevel", "1", "whether to support AES encryption in authenticated connections (0 = no, 1 = supported, 2 = requested, 3 = required)"}; + +extern cvar_t net_sourceaddresscheck; + int crypto_keyfp_recommended_length; static const char *crypto_idstring = NULL; static char crypto_idstring_buf[512]; + #define PROTOCOL_D0_BLIND_ID FOURCC_D0PK #define PROTOCOL_VLEN (('v' << 0) | ('l' << 8) | ('e' << 16) | ('n' << 24)) @@ -93,7 +116,7 @@ static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, co { if(pos + 4 + lumpsize[i] > len) return 0; - Crypto_UnLittleLong(&buf[pos], lumpsize[i]); + Crypto_UnLittleLong(&buf[pos], (unsigned long)lumpsize[i]); pos += 4; memcpy(&buf[pos], lumps[i], lumpsize[i]); pos += lumpsize[i]; @@ -104,7 +127,7 @@ static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, co #define USE_AES -#ifdef CRYPTO_STATIC +#ifdef LINK_TO_CRYPTO #include @@ -149,12 +172,14 @@ static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, co #define qd0_blind_id_sign_with_private_id_sign_detached d0_blind_id_sign_with_private_id_sign_detached #define qd0_blind_id_setmallocfuncs d0_blind_id_setmallocfuncs #define qd0_blind_id_setmutexfuncs d0_blind_id_setmutexfuncs +#define qd0_blind_id_verify_public_id d0_blind_id_verify_public_id +#define qd0_blind_id_verify_private_id d0_blind_id_verify_private_id #else // d0_blind_id interface #define D0_EXPORT -#ifdef __GNUC__ +#if defined (__GNUC__) || (__clang__) || (__TINYC__) #define D0_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else #define D0_WARN_UNUSED_RESULT @@ -207,6 +232,8 @@ static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_sign_with_private_ static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_sign_with_private_id_sign_detached) (d0_blind_id_t *ctx, D0_BOOL is_first, D0_BOOL send_modulus, const char *message, size_t msglen, char *outbuf, size_t *outbuflen); static D0_EXPORT void (*qd0_blind_id_setmallocfuncs)(d0_malloc_t *m, d0_free_t *f); static D0_EXPORT void (*qd0_blind_id_setmutexfuncs)(d0_createmutex_t *c, d0_destroymutex_t *d, d0_lockmutex_t *l, d0_unlockmutex_t *u); +static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_verify_public_id)(const d0_blind_id_t *ctx, D0_BOOL *status); +static D0_EXPORT D0_WARN_UNUSED_RESULT D0_BOOL (*qd0_blind_id_verify_private_id)(const d0_blind_id_t *ctx); static dllfunction_t d0_blind_id_funcs[] = { {"d0_blind_id_new", (void **) &qd0_blind_id_new}, @@ -246,12 +273,14 @@ static dllfunction_t d0_blind_id_funcs[] = {"d0_blind_id_sign_with_private_id_sign_detached", (void **) &qd0_blind_id_sign_with_private_id_sign_detached}, {"d0_blind_id_setmallocfuncs", (void **) &qd0_blind_id_setmallocfuncs}, {"d0_blind_id_setmutexfuncs", (void **) &qd0_blind_id_setmutexfuncs}, + {"d0_blind_id_verify_public_id", (void **) &qd0_blind_id_verify_public_id}, + {"d0_blind_id_verify_private_id", (void **) &qd0_blind_id_verify_private_id}, {NULL, NULL} }; // end of d0_blind_id interface static dllhandle_t d0_blind_id_dll = NULL; -static qboolean Crypto_OpenLibrary (void) +static qbool Crypto_OpenLibrary (void) { const char* dllnames [] = { @@ -271,17 +300,17 @@ static qboolean Crypto_OpenLibrary (void) return true; // Load the DLL - return Sys_LoadLibrary (dllnames, &d0_blind_id_dll, d0_blind_id_funcs); + return Sys_LoadDependency (dllnames, &d0_blind_id_dll, d0_blind_id_funcs); } static void Crypto_CloseLibrary (void) { - Sys_UnloadLibrary (&d0_blind_id_dll); + Sys_FreeLibrary (&d0_blind_id_dll); } #endif -#ifdef CRYPTO_RIJNDAEL_STATIC +#ifdef LINK_TO_CRYPTO_RIJNDAEL #include @@ -320,7 +349,7 @@ static dllfunction_t d0_rijndael_funcs[] = // end of d0_blind_id interface static dllhandle_t d0_rijndael_dll = NULL; -static qboolean Crypto_Rijndael_OpenLibrary (void) +static qbool Crypto_Rijndael_OpenLibrary (void) { const char* dllnames [] = { @@ -340,12 +369,12 @@ static qboolean Crypto_Rijndael_OpenLibrary (void) return true; // Load the DLL - return Sys_LoadLibrary (dllnames, &d0_rijndael_dll, d0_rijndael_funcs); + return Sys_LoadDependency (dllnames, &d0_rijndael_dll, d0_rijndael_funcs); } static void Crypto_Rijndael_CloseLibrary (void) { - Sys_UnloadLibrary (&d0_rijndael_dll); + Sys_FreeLibrary (&d0_rijndael_dll); } #endif @@ -356,14 +385,14 @@ void sha256(unsigned char *out, const unsigned char *in, int n) qd0_blind_id_util_sha256((char *) out, (const char *) in, n); } -static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax) +static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax, qbool inuserdir) { char vabuf[1024]; qfile_t *f = NULL; fs_offset_t n; - if(*fs_userdir) - f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%s%s", fs_userdir, path), "rb", false); - if(!f) + if(inuserdir) + f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%s%s", *fs_userdir ? fs_userdir : fs_basedir, path), "rb", false); + else f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%s%s", fs_basedir, path), "rb", false); if(!f) return 0; @@ -374,7 +403,7 @@ static size_t Crypto_LoadFile(const char *path, char *buf, size_t nmax) return (size_t) n; } -static qboolean PutWithNul(char **data, size_t *len, const char *str) +static qbool PutWithNul(char **data, size_t *len, const char *str) { // invariant: data points to insertion point size_t l = strlen(str); @@ -409,17 +438,12 @@ static const char *GetUntilNul(const char **data, size_t *len) *len = 0; return NULL; } - else - { - n = (p - *data) + 1; - *len -= n; - *data += n; - if(*len == 0) - *data = NULL; - return (const char *) data_save; - } - *data = NULL; - return NULL; + n = (p - *data) + 1; + *len -= n; + *data += n; + if(*len == 0) + *data = NULL; + return (const char *) data_save; } // d0pk reading @@ -442,7 +466,7 @@ static d0_blind_id_t *Crypto_ReadPublicKey(char *buf, size_t len) } // d0si reading -static qboolean Crypto_AddPrivateKey(d0_blind_id_t *pk, char *buf, size_t len) +static qbool Crypto_AddPrivateKey(d0_blind_id_t *pk, char *buf, size_t len) { const char *p[1]; size_t l[1]; @@ -457,7 +481,8 @@ static qboolean Crypto_AddPrivateKey(d0_blind_id_t *pk, char *buf, size_t len) #define MAX_PUBKEYS 16 static d0_blind_id_t *pubkeys[MAX_PUBKEYS]; static char pubkeys_fp64[MAX_PUBKEYS][FP64_SIZE+1]; -static qboolean pubkeys_havepriv[MAX_PUBKEYS]; +static qbool pubkeys_havepriv[MAX_PUBKEYS]; +static qbool pubkeys_havesig[MAX_PUBKEYS]; static char pubkeys_priv_fp64[MAX_PUBKEYS][FP64_SIZE+1]; static char challenge_append[1400]; static size_t challenge_append_length; @@ -489,7 +514,8 @@ typedef struct int next_step; char challenge[2048]; char wantserver_idfp[FP64_SIZE+1]; - qboolean wantserver_aes; + qbool wantserver_aes; + qbool wantserver_issigned; int cdata_id; } crypto_data_t; @@ -499,7 +525,7 @@ crypto_data_t; #define MAKE_CDATA if(!crypto->data) crypto->data = Z_Malloc(sizeof(crypto_data_t)) #define CLEAR_CDATA if(crypto->data) { if(CDATA->id) qd0_blind_id_free(CDATA->id); Z_Free(crypto->data); } crypto->data = NULL -static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qboolean allow_create) +static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qbool allow_create) { crypto_t *crypto; int i, best; @@ -513,7 +539,7 @@ static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qboolean if(i < MAX_CRYPTOCONNECTS && (allow_create || cryptoconnects[i].crypto.data)) { crypto = &cryptoconnects[i].crypto; - cryptoconnects[i].lasttime = realtime; + cryptoconnects[i].lasttime = host.realtime; return crypto; } if(!allow_create) @@ -523,13 +549,13 @@ static crypto_t *Crypto_ServerFindInstance(lhnetaddress_t *peeraddress, qboolean if(cryptoconnects[i].lasttime < cryptoconnects[best].lasttime) best = i; crypto = &cryptoconnects[best].crypto; - cryptoconnects[best].lasttime = realtime; + cryptoconnects[best].lasttime = host.realtime; memcpy(&cryptoconnects[best].address, peeraddress, sizeof(cryptoconnects[best].address)); CLEAR_CDATA; return crypto; } -qboolean Crypto_ServerFinishInstance(crypto_t *out, crypto_t *crypto) +qbool Crypto_FinishInstance(crypto_t *out, crypto_t *crypto) { // no check needed here (returned pointers are only used in prefilled fields) if(!crypto || !crypto->authenticated) @@ -540,7 +566,7 @@ qboolean Crypto_ServerFinishInstance(crypto_t *out, crypto_t *crypto) } CLEAR_CDATA; memcpy(out, crypto, sizeof(*out)); - memset(crypto, 0, sizeof(crypto)); + memset(crypto, 0, sizeof(*crypto)); return true; } @@ -557,6 +583,7 @@ typedef struct crypto_storedhostkey_s int keyid; char idfp[FP64_SIZE+1]; int aeslevel; + qbool issigned; } crypto_storedhostkey_t; static crypto_storedhostkey_t *crypto_storedhostkey_hashtable[CRYPTO_HOSTKEY_HASHSIZE]; @@ -583,12 +610,12 @@ static void Crypto_ClearHostKeys(void) } } -static qboolean Crypto_ClearHostKey(lhnetaddress_t *peeraddress) +static qbool Crypto_ClearHostKey(lhnetaddress_t *peeraddress) { char buf[128]; int hashindex; crypto_storedhostkey_t **hkp; - qboolean found = false; + qbool found = false; LHNETADDRESS_ToString(peeraddress, buf, sizeof(buf), 1); hashindex = CRC_Block((const unsigned char *) buf, strlen(buf)) % CRYPTO_HOSTKEY_HASHSIZE; @@ -605,7 +632,7 @@ static qboolean Crypto_ClearHostKey(lhnetaddress_t *peeraddress) return found; } -static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystring, qboolean complain) +static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystring, qbool complain) { char buf[128]; int hashindex; @@ -613,6 +640,7 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri int keyid; char idfp[FP64_SIZE+1]; int aeslevel; + qbool issigned; if(!d0_blind_id_dll) return; @@ -627,10 +655,12 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri ++keystring; keyid = -1; + issigned = false; while(*keystring && keyid < 0) { // id@key const char *idstart, *idend, *keystart, *keyend; + qbool thisissigned = true; ++keystring; // skip the space idstart = keystring; while(*keystring && *keystring != ' ' && *keystring != '@') @@ -644,18 +674,26 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri ++keystring; keyend = keystring; + if (keystart[0] == '~') + { + thisissigned = false; + ++keystart; + } + if(idend - idstart == FP64_SIZE && keyend - keystart == FP64_SIZE) { - for(keyid = 0; keyid < MAX_PUBKEYS; ++keyid) - if(pubkeys[keyid]) - if(!memcmp(pubkeys_fp64[keyid], keystart, FP64_SIZE)) + int thiskeyid; + for(thiskeyid = MAX_PUBKEYS - 1; thiskeyid >= 0; --thiskeyid) + if(pubkeys[thiskeyid]) + if(!memcmp(pubkeys_fp64[thiskeyid], keystart, FP64_SIZE)) { memcpy(idfp, idstart, FP64_SIZE); idfp[FP64_SIZE] = 0; + keyid = thiskeyid; + issigned = thisissigned; break; } - if(keyid >= MAX_PUBKEYS) - keyid = -1; + // If this failed, keyid will be -1. } } @@ -674,8 +712,11 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri Con_Printf("Server %s tried to change the host key to a value not in the host cache. Connecting to it will fail. To accept the new host key, do crypto_hostkey_clear %s\n", buf, buf); if(hk->aeslevel > aeslevel) Con_Printf("Server %s tried to reduce encryption status, not accepted. Connecting to it will fail. To accept, do crypto_hostkey_clear %s\n", buf, buf); + if(hk->issigned > issigned) + Con_Printf("Server %s tried to reduce signature status, not accepted. Connecting to it will fail. To accept, do crypto_hostkey_clear %s\n", buf, buf); } hk->aeslevel = max(aeslevel, hk->aeslevel); + hk->issigned = issigned; return; } @@ -686,10 +727,11 @@ static void Crypto_StoreHostKey(lhnetaddress_t *peeraddress, const char *keystri memcpy(hk->idfp, idfp, FP64_SIZE+1); hk->next = crypto_storedhostkey_hashtable[hashindex]; hk->aeslevel = aeslevel; + hk->issigned = issigned; crypto_storedhostkey_hashtable[hashindex] = hk; } -qboolean Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, int *aeslevel) +qbool Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, int *aeslevel, qbool *issigned) { char buf[128]; int hashindex; @@ -713,10 +755,12 @@ qboolean Crypto_RetrieveHostKey(lhnetaddress_t *peeraddress, int *keyid, char *k strlcpy(idfp, hk->idfp, idfplen); if(aeslevel) *aeslevel = hk->aeslevel; + if(issigned) + *issigned = hk->issigned; return true; } -int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen) // return value: -1 if more to come, +1 if valid, 0 if end of list +int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, size_t idfplen, qbool *issigned) // return value: -1 if more to come, +1 if valid, 0 if end of list { if(keyid < 0 || keyid >= MAX_PUBKEYS) return 0; @@ -730,7 +774,9 @@ int Crypto_RetrieveLocalKey(int keyid, char *keyfp, size_t keyfplen, char *idfp, strlcpy(keyfp, pubkeys_fp64[keyid], keyfplen); if(idfp) if(pubkeys_havepriv[keyid]) - strlcpy(idfp, pubkeys_priv_fp64[keyid], keyfplen); + strlcpy(idfp, pubkeys_priv_fp64[keyid], idfplen); + if(issigned) + *issigned = pubkeys_havesig[keyid]; return 1; } // end @@ -765,34 +811,82 @@ static void Crypto_BuildChallengeAppend(void) challenge_append_length = p - challenge_append; } -static void Crypto_LoadKeys(void) +static qbool Crypto_SavePubKeyTextFile(int i) +{ + qfile_t *f; + char vabuf[1024]; + + if(!pubkeys_havepriv[i]) + return false; + f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d-public-fp%s.txt", *fs_userdir ? fs_userdir : fs_basedir, i, sessionid.string), "w", false); + if(!f) + return false; + + // we ignore errors for this file, as it's not necessary to have + FS_Printf(f, "ID-Fingerprint: %s\n", pubkeys_priv_fp64[i]); + FS_Printf(f, "ID-Is-Signed: %s\n", pubkeys_havesig[i] ? "yes" : "no"); + FS_Printf(f, "ID-Is-For-Key: %s\n", pubkeys_fp64[i]); + FS_Printf(f, "\n"); + FS_Printf(f, "This is a PUBLIC ID file for DarkPlaces.\n"); + FS_Printf(f, "You are free to share this file or its contents.\n"); + FS_Printf(f, "\n"); + FS_Printf(f, "This file will be automatically generated again if deleted.\n"); + FS_Printf(f, "\n"); + FS_Printf(f, "However, NEVER share the accompanying SECRET ID file called\n"); + FS_Printf(f, "key_%d.d0si%s, as doing so would compromise security!\n", i, sessionid.string); + FS_Close(f); + + return true; +} + +static void Crypto_BuildIdString(void) +{ + int i; + char vabuf[1024]; + + crypto_idstring = NULL; + dpsnprintf(crypto_idstring_buf, sizeof(crypto_idstring_buf), "%d", d0_rijndael_dll ? crypto_aeslevel.integer : 0); + for (i = 0; i < MAX_PUBKEYS; ++i) + if (pubkeys[i]) + strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s%s", pubkeys_priv_fp64[i], pubkeys_havesig[i] ? "" : "~", pubkeys_fp64[i]), sizeof(crypto_idstring_buf)); + crypto_idstring = crypto_idstring_buf; +} + +void Crypto_LoadKeys(void) { char buf[8192]; size_t len, len2; int i; char vabuf[1024]; + if(!d0_blind_id_dll) // don't if we can't + return; + + if(crypto_idstring) // already loaded? then not + return; + + Host_LockSession(); // we use the session ID here + // load keys // note: we are just a CLIENT // so we load: // PUBLIC KEYS to accept (including modulus) // PRIVATE KEY of user - crypto_idstring = NULL; - dpsnprintf(crypto_idstring_buf, sizeof(crypto_idstring_buf), "%d", d0_rijndael_dll ? crypto_aeslevel.integer : 0); for(i = 0; i < MAX_PUBKEYS; ++i) { memset(pubkeys_fp64[i], 0, sizeof(pubkeys_fp64[i])); memset(pubkeys_priv_fp64[i], 0, sizeof(pubkeys_fp64[i])); pubkeys_havepriv[i] = false; - len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0pk", i), buf, sizeof(buf)); + pubkeys_havesig[i] = false; + len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0pk", i), buf, sizeof(buf), false); if((pubkeys[i] = Crypto_ReadPublicKey(buf, len))) { len2 = FP64_SIZE; if(qd0_blind_id_fingerprint64_public_key(pubkeys[i], pubkeys_fp64[i], &len2)) // keeps final NUL { Con_Printf("Loaded public key key_%d.d0pk (fingerprint: %s)\n", i, pubkeys_fp64[i]); - len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0si", i), buf, sizeof(buf)); + len = Crypto_LoadFile(va(vabuf, sizeof(vabuf), "key_%d.d0si%s", i, sessionid.string), buf, sizeof(buf), true); if(len) { if(Crypto_AddPrivateKey(pubkeys[i], buf, len)) @@ -800,14 +894,34 @@ static void Crypto_LoadKeys(void) len2 = FP64_SIZE; if(qd0_blind_id_fingerprint64_public_id(pubkeys[i], pubkeys_priv_fp64[i], &len2)) // keeps final NUL { - Con_Printf("Loaded private ID key_%d.d0si for key_%d.d0pk (public key fingerprint: %s)\n", i, i, pubkeys_priv_fp64[i]); - pubkeys_havepriv[i] = true; - strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[i], pubkeys_fp64[i]), sizeof(crypto_idstring_buf)); + D0_BOOL status = 0; + + Con_Printf("Loaded private ID key_%d.d0si%s for key_%d.d0pk (public key fingerprint: %s)\n", i, sessionid.string, i, pubkeys_priv_fp64[i]); + + // verify the key we just loaded (just in case) + if(qd0_blind_id_verify_private_id(pubkeys[i]) && qd0_blind_id_verify_public_id(pubkeys[i], &status)) + { + pubkeys_havepriv[i] = true; + pubkeys_havesig[i] = status; + + // verify the key we just got (just in case) + if(!status) + Con_Printf("NOTE: this ID has not yet been signed!\n"); + + Crypto_SavePubKeyTextFile(i); + } + else + { + Con_Printf("d0_blind_id_verify_private_id failed, this is not a valid key!\n"); + qd0_blind_id_free(pubkeys[i]); + pubkeys[i] = NULL; + } } else { - // can't really happen - // but nothing leaked here + Con_Printf("d0_blind_id_fingerprint64_public_id failed\n"); + qd0_blind_id_free(pubkeys[i]); + pubkeys[i] = NULL; } } } @@ -820,9 +934,9 @@ static void Crypto_LoadKeys(void) } } } - crypto_idstring = crypto_idstring_buf; keygen_i = -1; + Crypto_BuildIdString(); Crypto_BuildChallengeAppend(); // find a good prefix length for all the keys we know (yes, algorithm is not perfect yet, may yield too long prefix length) @@ -871,6 +985,7 @@ static void Crypto_LoadKeys(void) static void Crypto_UnloadKeys(void) { int i; + keygen_i = -1; for(i = 0; i < MAX_PUBKEYS; ++i) { @@ -878,6 +993,7 @@ static void Crypto_UnloadKeys(void) qd0_blind_id_free(pubkeys[i]); pubkeys[i] = NULL; pubkeys_havepriv[i] = false; + pubkeys_havesig[i] = false; memset(pubkeys_fp64[i], 0, sizeof(pubkeys_fp64[i])); memset(pubkeys_priv_fp64[i], 0, sizeof(pubkeys_fp64[i])); challenge_append_length = 0; @@ -885,39 +1001,38 @@ static void Crypto_UnloadKeys(void) crypto_idstring = NULL; } - static mempool_t *cryptomempool; #ifdef __cplusplus extern "C" { #endif -static void *d0_malloc(size_t len) +static void *Crypto_d0_malloc(size_t len) { return Mem_Alloc(cryptomempool, len); } -static void d0_free(void *p) +static void Crypto_d0_free(void *p) { Mem_Free(p); } -static void *d0_createmutex(void) +static void *Crypto_d0_createmutex(void) { return Thread_CreateMutex(); } -static void d0_destroymutex(void *m) +static void Crypto_d0_destroymutex(void *m) { Thread_DestroyMutex(m); } -static int d0_lockmutex(void *m) +static int Crypto_d0_lockmutex(void *m) { return Thread_LockMutex(m); } -static int d0_unlockmutex(void *m) +static int Crypto_d0_unlockmutex(void *m) { return Thread_UnlockMutex(m); } @@ -930,10 +1045,6 @@ void Crypto_Shutdown(void) crypto_t *crypto; int i; - if (crypto_mutex) - Thread_DestroyMutex(crypto_mutex); - crypto_mutex = NULL; - Crypto_Rijndael_CloseLibrary(); if(d0_blind_id_dll) @@ -965,12 +1076,9 @@ void Crypto_Init(void) if(!Crypto_OpenLibrary()) return; - qd0_blind_id_setmallocfuncs(d0_malloc, d0_free); + qd0_blind_id_setmallocfuncs(Crypto_d0_malloc, Crypto_d0_free); if (Thread_HasThreads()) - { - crypto_mutex = Thread_CreateMutex(); - qd0_blind_id_setmutexfuncs(d0_createmutex, d0_destroymutex, d0_lockmutex, d0_unlockmutex); - } + qd0_blind_id_setmutexfuncs(Crypto_d0_createmutex, Crypto_d0_destroymutex, Crypto_d0_lockmutex, Crypto_d0_unlockmutex); if(!qd0_blind_id_INITIALIZE()) { @@ -980,14 +1088,13 @@ void Crypto_Init(void) return; } - Crypto_Rijndael_OpenLibrary(); // if this fails, it's uncritical + (void) Crypto_Rijndael_OpenLibrary(); // if this fails, it's uncritical Crypto_InitHostKeys(); - Crypto_LoadKeys(); } // end -qboolean Crypto_Available(void) +qbool Crypto_Available(void) { if(!d0_blind_id_dll) return false; @@ -1001,35 +1108,33 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch size_t l[1]; static char buf[8192]; static char buf2[8192]; - size_t bufsize, buf2size; + size_t buf2size; qfile_t *f = NULL; - d0_blind_id_t *ctx, *ctx2; D0_BOOL status; - size_t len2; char vabuf[1024]; - if (crypto_mutex) Thread_LockMutex(crypto_mutex); + SV_LockThreadMutex(); if(!d0_blind_id_dll) { Con_Print("libd0_blind_id DLL not found, this command is inactive.\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } - if(keygen_i >= MAX_PUBKEYS || !pubkeys[keygen_i]) + if(keygen_i < 0) { - Con_Printf("overflow of keygen_i\n"); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + Con_Printf("Unexpected response from keygen server:\n"); + Com_HexDumpToConsole(buffer, (int)length_received); + SV_UnlockThreadMutex(); return; } - if(keygen_i < 0) + if(keygen_i >= MAX_PUBKEYS || !pubkeys[keygen_i]) { - Con_Printf("Unexpected response from keygen server:\n"); - Com_HexDumpToConsole(buffer, length_received); - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + Con_Printf("overflow of keygen_i\n"); + keygen_i = -1; + SV_UnlockThreadMutex(); return; } if(!Crypto_ParsePack((const char *) buffer, length_received, FOURCC_D0IR, p, l, 1)) @@ -1041,110 +1146,34 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch else { Con_Printf("Invalid response from keygen server:\n"); - Com_HexDumpToConsole(buffer, length_received); + Com_HexDumpToConsole(buffer, (int)length_received); } keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } if(!qd0_blind_id_finish_private_id_request(pubkeys[keygen_i], p[0], l[0])) { Con_Printf("d0_blind_id_finish_private_id_request failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } // verify the key we just got (just in case) - ctx = qd0_blind_id_new(); - if(!ctx) + if(!qd0_blind_id_verify_public_id(pubkeys[keygen_i], &status) || !status) { - Con_Printf("d0_blind_id_new failed\n"); + Con_Printf("d0_blind_id_verify_public_id failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } - ctx2 = qd0_blind_id_new(); - if(!ctx2) - { - Con_Printf("d0_blind_id_new failed\n"); - qd0_blind_id_free(ctx); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - if(!qd0_blind_id_copy(ctx, pubkeys[keygen_i])) - { - Con_Printf("d0_blind_id_copy failed\n"); - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - if(!qd0_blind_id_copy(ctx2, pubkeys[keygen_i])) - { - Con_Printf("d0_blind_id_copy failed\n"); - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - bufsize = sizeof(buf); - if(!qd0_blind_id_authenticate_with_private_id_start(ctx, 1, 1, "hello world", 11, buf, &bufsize)) - { - Con_Printf("d0_blind_id_authenticate_with_private_id_start failed\n"); - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - buf2size = sizeof(buf2); - if(!qd0_blind_id_authenticate_with_private_id_challenge(ctx2, 1, 1, buf, bufsize, buf2, &buf2size, &status) || !status) - { - Con_Printf("d0_blind_id_authenticate_with_private_id_challenge failed (server does not have the requested private key)\n"); - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - bufsize = sizeof(buf); - if(!qd0_blind_id_authenticate_with_private_id_response(ctx, buf2, buf2size, buf, &bufsize)) - { - Con_Printf("d0_blind_id_authenticate_with_private_id_response failed\n"); - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - buf2size = sizeof(buf2); - if(!qd0_blind_id_authenticate_with_private_id_verify(ctx2, buf, bufsize, buf2, &buf2size, &status) || !status) - { - Con_Printf("d0_blind_id_authenticate_with_private_id_verify failed (server does not have the requested private key)\n"); - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - qd0_blind_id_free(ctx); - qd0_blind_id_free(ctx2); // we have a valid key now! // make the rest of crypto.c know that - len2 = FP64_SIZE; - if(qd0_blind_id_fingerprint64_public_id(pubkeys[keygen_i], pubkeys_priv_fp64[keygen_i], &len2)) // keeps final NUL - { - Con_Printf("Received private ID key_%d.d0pk (public key fingerprint: %s)\n", keygen_i, pubkeys_priv_fp64[keygen_i]); - pubkeys_havepriv[keygen_i] = true; - strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[keygen_i], pubkeys_fp64[keygen_i]), sizeof(crypto_idstring_buf)); - crypto_idstring = crypto_idstring_buf; - Crypto_BuildChallengeAppend(); - } + Con_Printf("Received signature for private ID key_%d.d0pk (public key fingerprint: %s)\n", keygen_i, pubkeys_priv_fp64[keygen_i]); + pubkeys_havesig[keygen_i] = true; + // write the key to disk p[0] = buf; l[0] = sizeof(buf); @@ -1152,87 +1181,154 @@ static void Crypto_KeyGen_Finished(int code, size_t length_received, unsigned ch { Con_Printf("d0_blind_id_write_private_id failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } if(!(buf2size = Crypto_UnParsePack(buf2, sizeof(buf2), FOURCC_D0SI, p, l, 1))) { Con_Printf("Crypto_UnParsePack failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } - if(*fs_userdir) - { - FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_userdir, keygen_i)); - f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_userdir, keygen_i), "wb", false); - } - if(!f) - { - FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_basedir, keygen_i)); - f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si", fs_basedir, keygen_i), "wb", false); - } + FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string)); + f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string), "wb", false); if(!f) { - Con_Printf("Cannot open key_%d.d0si\n", keygen_i); + Con_Printf("Cannot open key_%d.d0si%s\n", keygen_i, sessionid.string); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } FS_Write(f, buf2, buf2size); FS_Close(f); - Con_Printf("Saved to key_%d.d0si\n", keygen_i); + Crypto_SavePubKeyTextFile(keygen_i); + + Con_Printf("Saved to key_%d.d0si%s\n", keygen_i, sessionid.string); + + Crypto_BuildIdString(); + keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); } -static void Crypto_KeyGen_f(void) +static void Crypto_KeyGen_f(cmd_state_t *cmd) { int i; const char *p[1]; size_t l[1]; static char buf[8192]; static char buf2[8192]; + size_t buf2size; size_t buf2l, buf2pos; + char vabuf[1024]; + size_t len2; + qfile_t *f = NULL; + if(!d0_blind_id_dll) { Con_Print("libd0_blind_id DLL not found, this command is inactive.\n"); return; } - if(Cmd_Argc() != 3) + if(Cmd_Argc(cmd) != 3) { - Con_Printf("usage:\n%s id url\n", Cmd_Argv(0)); + Con_Printf("usage:\n%s id url\n", Cmd_Argv(cmd, 0)); return; } - if (crypto_mutex) Thread_LockMutex(crypto_mutex); - i = atoi(Cmd_Argv(1)); + SV_LockThreadMutex(); + Crypto_LoadKeys(); + i = atoi(Cmd_Argv(cmd, 1)); if(!pubkeys[i]) { Con_Printf("there is no public key %d\n", i); - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; - } - if(pubkeys_havepriv[i]) - { - Con_Printf("there is already a private key for %d\n", i); - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } if(keygen_i >= 0) { Con_Printf("there is already a keygen run on the way\n"); - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } keygen_i = i; - if(!qd0_blind_id_generate_private_id_start(pubkeys[keygen_i])) + + // how to START the keygenning... + if(pubkeys_havepriv[keygen_i]) { - Con_Printf("d0_blind_id_start failed\n"); - keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); - return; + if(pubkeys_havesig[keygen_i]) + { + Con_Printf("there is already a signed private key for %d\n", i); + keygen_i = -1; + SV_UnlockThreadMutex(); + return; + } + // if we get here, we only need a signature, no new keygen run needed + Con_Printf("Only need a signature for an existing key...\n"); + } + else + { + // we also need a new ID itself + if(!qd0_blind_id_generate_private_id_start(pubkeys[keygen_i])) + { + Con_Printf("d0_blind_id_start failed\n"); + keygen_i = -1; + SV_UnlockThreadMutex(); + return; + } + // verify the key we just got (just in case) + if(!qd0_blind_id_verify_private_id(pubkeys[keygen_i])) + { + Con_Printf("d0_blind_id_verify_private_id failed\n"); + keygen_i = -1; + SV_UnlockThreadMutex(); + return; + } + // we have a valid key now! + // make the rest of crypto.c know that + len2 = FP64_SIZE; + if(qd0_blind_id_fingerprint64_public_id(pubkeys[keygen_i], pubkeys_priv_fp64[keygen_i], &len2)) // keeps final NUL + { + Con_Printf("Generated private ID key_%d.d0pk (public key fingerprint: %s)\n", keygen_i, pubkeys_priv_fp64[keygen_i]); + pubkeys_havepriv[keygen_i] = true; + strlcat(crypto_idstring_buf, va(vabuf, sizeof(vabuf), " %s@%s", pubkeys_priv_fp64[keygen_i], pubkeys_fp64[keygen_i]), sizeof(crypto_idstring_buf)); + crypto_idstring = crypto_idstring_buf; + Crypto_BuildChallengeAppend(); + } + // write the key to disk + p[0] = buf; + l[0] = sizeof(buf); + if(!qd0_blind_id_write_private_id(pubkeys[keygen_i], buf, &l[0])) + { + Con_Printf("d0_blind_id_write_private_id failed\n"); + keygen_i = -1; + SV_UnlockThreadMutex(); + return; + } + if(!(buf2size = Crypto_UnParsePack(buf2, sizeof(buf2), FOURCC_D0SI, p, l, 1))) + { + Con_Printf("Crypto_UnParsePack failed\n"); + keygen_i = -1; + SV_UnlockThreadMutex(); + return; + } + + FS_CreatePath(va(vabuf, sizeof(vabuf), "%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string)); + f = FS_SysOpen(va(vabuf, sizeof(vabuf), "%skey_%d.d0si%s", *fs_userdir ? fs_userdir : fs_basedir, keygen_i, sessionid.string), "wb", false); + if(!f) + { + Con_Printf("Cannot open key_%d.d0si%s\n", keygen_i, sessionid.string); + keygen_i = -1; + SV_UnlockThreadMutex(); + return; + } + FS_Write(f, buf2, buf2size); + FS_Close(f); + + Crypto_SavePubKeyTextFile(keygen_i); + + Con_Printf("Saved unsigned key to key_%d.d0si%s\n", keygen_i, sessionid.string); } p[0] = buf; l[0] = sizeof(buf); @@ -1240,48 +1336,48 @@ static void Crypto_KeyGen_f(void) { Con_Printf("d0_blind_id_generate_private_id_request failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } - buf2pos = strlen(Cmd_Argv(2)); - memcpy(buf2, Cmd_Argv(2), buf2pos); + buf2pos = strlen(Cmd_Argv(cmd, 2)); + memcpy(buf2, Cmd_Argv(cmd, 2), buf2pos); if(!(buf2l = Crypto_UnParsePack(buf2 + buf2pos, sizeof(buf2) - buf2pos - 1, FOURCC_D0IQ, p, l, 1))) { Con_Printf("Crypto_UnParsePack failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } if(!(buf2l = base64_encode((unsigned char *) (buf2 + buf2pos), buf2l, sizeof(buf2) - buf2pos - 1))) { Con_Printf("base64_encode failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } buf2l += buf2pos; - buf[buf2l] = 0; + buf2[buf2l] = 0; if(!Curl_Begin_ToMemory(buf2, 0, (unsigned char *) keygen_buf, sizeof(keygen_buf), Crypto_KeyGen_Finished, NULL)) { Con_Printf("curl failed\n"); keygen_i = -1; - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + SV_UnlockThreadMutex(); return; } - Con_Printf("key generation in progress\n"); - if (crypto_mutex) Thread_UnlockMutex(crypto_mutex); + Con_Printf("Signature generation in progress...\n"); + SV_UnlockThreadMutex(); } // end // console commands -static void Crypto_Reload_f(void) +static void Crypto_Reload_f(cmd_state_t *cmd) { Crypto_ClearHostKeys(); Crypto_UnloadKeys(); Crypto_LoadKeys(); } -static void Crypto_Keys_f(void) +static void Crypto_Keys_f(cmd_state_t *cmd) { int i; if(!d0_blind_id_dll) @@ -1295,12 +1391,16 @@ static void Crypto_Keys_f(void) { Con_Printf("%2d: public key key_%d.d0pk (fingerprint: %s)\n", i, i, pubkeys_fp64[i]); if(pubkeys_havepriv[i]) - Con_Printf(" private ID key_%d.d0si (public key fingerprint: %s)\n", i, pubkeys_priv_fp64[i]); + { + Con_Printf(" private ID key_%d.d0si%s (public key fingerprint: %s)\n", i, sessionid.string, pubkeys_priv_fp64[i]); + if(!pubkeys_havesig[i]) + Con_Printf(" NOTE: this ID has not yet been signed!\n"); + } } } } -static void Crypto_HostKeys_f(void) +static void Crypto_HostKeys_f(cmd_state_t *cmd) { int i; crypto_storedhostkey_t *hk; @@ -1325,7 +1425,7 @@ static void Crypto_HostKeys_f(void) } } -static void Crypto_HostKey_Clear_f(void) +static void Crypto_HostKey_Clear_f(cmd_state_t *cmd) { lhnetaddress_t addr; int i; @@ -1336,12 +1436,12 @@ static void Crypto_HostKey_Clear_f(void) return; } - for(i = 1; i < Cmd_Argc(); ++i) + for(i = 1; i < Cmd_Argc(cmd); ++i) { - LHNETADDRESS_FromString(&addr, Cmd_Argv(i), 26000); + LHNETADDRESS_FromString(&addr, Cmd_Argv(cmd, i), 26000); if(Crypto_ClearHostKey(&addr)) { - Con_Printf("cleared host key for %s\n", Cmd_Argv(i)); + Con_Printf("cleared host key for %s\n", Cmd_Argv(cmd, i)); } } } @@ -1350,11 +1450,12 @@ void Crypto_Init_Commands(void) { if(d0_blind_id_dll) { - Cmd_AddCommand("crypto_reload", Crypto_Reload_f, "reloads cryptographic keys"); - Cmd_AddCommand("crypto_keygen", Crypto_KeyGen_f, "generates and saves a cryptographic key"); - Cmd_AddCommand("crypto_keys", Crypto_Keys_f, "lists the loaded keys"); - Cmd_AddCommand("crypto_hostkeys", Crypto_HostKeys_f, "lists the cached host keys"); - Cmd_AddCommand("crypto_hostkey_clear", Crypto_HostKey_Clear_f, "clears a cached host key"); + Cmd_AddCommand(CF_SHARED, "crypto_reload", Crypto_Reload_f, "reloads cryptographic keys"); + Cmd_AddCommand(CF_SHARED, "crypto_keygen", Crypto_KeyGen_f, "generates and saves a cryptographic key"); + Cmd_AddCommand(CF_SHARED, "crypto_keys", Crypto_Keys_f, "lists the loaded keys"); + Cmd_AddCommand(CF_SHARED, "crypto_hostkeys", Crypto_HostKeys_f, "lists the cached host keys"); + Cmd_AddCommand(CF_SHARED, "crypto_hostkey_clear", Crypto_HostKey_Clear_f, "clears a cached host key"); + Cvar_RegisterVariable(&crypto_developer); if(d0_rijndael_dll) Cvar_RegisterVariable(&crypto_aeslevel); @@ -1437,15 +1538,15 @@ const void *Crypto_EncryptPacket(crypto_t *crypto, const void *data_src, size_t if(developer_networking.integer) { Con_Print("To be encrypted:\n"); - Com_HexDumpToConsole((const unsigned char *) data_src, len_src); + Com_HexDumpToConsole((const unsigned char *) data_src, (int)len_src); } - if(len_src + 32 > len || !HMAC_SHA256_32BYTES(h, (const unsigned char *) data_src, len_src, crypto->dhkey, DHKEY_SIZE)) + if(len_src + 32 > len || !HMAC_SHA256_32BYTES(h, (const unsigned char *) data_src, (int)len_src, crypto->dhkey, DHKEY_SIZE)) { Con_Printf("Crypto_EncryptPacket failed (not enough space: %d bytes in, %d bytes out)\n", (int) len_src, (int) len); return NULL; } *len_dst = ((len_src + 15) / 16) * 16 + 16; // add 16 for HMAC, then round to 16-size for AES - ((unsigned char *) data_dst)[0] = *len_dst - len_src; + ((unsigned char *) data_dst)[0] = (unsigned char)(*len_dst - len_src); memcpy(((unsigned char *) data_dst)+1, h, 15); aescpy(crypto->dhkey, (const unsigned char *) data_dst, ((unsigned char *) data_dst) + 16, (const unsigned char *) data_src, len_src); // IV dst src len @@ -1453,7 +1554,7 @@ const void *Crypto_EncryptPacket(crypto_t *crypto, const void *data_src, size_t else { // HMAC packet = 16 bytes HMAC-SHA-256 (truncated to 128 bits), data - if(len_src + 16 > len || !HMAC_SHA256_32BYTES(h, (const unsigned char *) data_src, len_src, crypto->dhkey, DHKEY_SIZE)) + if(len_src + 16 > len || !HMAC_SHA256_32BYTES(h, (const unsigned char *) data_src, (int)len_src, crypto->dhkey, DHKEY_SIZE)) { Con_Printf("Crypto_EncryptPacket failed (not enough space: %d bytes in, %d bytes out)\n", (int) len_src, (int) len); return NULL; @@ -1511,7 +1612,7 @@ const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t } seacpy(crypto->dhkey, (unsigned char *) data_src, (unsigned char *) data_dst, ((const unsigned char *) data_src) + 16, *len_dst); // IV dst src len - if(!HMAC_SHA256_32BYTES(h, (const unsigned char *) data_dst, *len_dst, crypto->dhkey, DHKEY_SIZE)) + if(!HMAC_SHA256_32BYTES(h, (const unsigned char *) data_dst, (int)*len_dst, crypto->dhkey, DHKEY_SIZE)) { Con_Printf("HMAC fail\n"); return NULL; @@ -1524,7 +1625,7 @@ const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t if(developer_networking.integer) { Con_Print("Decrypted:\n"); - Com_HexDumpToConsole((const unsigned char *) data_dst, *len_dst); + Com_HexDumpToConsole((const unsigned char *) data_dst, (int)*len_dst); } return data_dst; // no need to copy } @@ -1542,10 +1643,10 @@ const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t return NULL; } //memcpy(data_dst, data_src + 16, *len_dst); - if(!HMAC_SHA256_32BYTES(h, ((const unsigned char *) data_src) + 16, *len_dst, crypto->dhkey, DHKEY_SIZE)) + if(!HMAC_SHA256_32BYTES(h, ((const unsigned char *) data_src) + 16, (int)*len_dst, crypto->dhkey, DHKEY_SIZE)) { Con_Printf("HMAC fail\n"); - Com_HexDumpToConsole((const unsigned char *) data_src, len_src); + Com_HexDumpToConsole((const unsigned char *) data_src, (int)len_src); return NULL; } @@ -1563,14 +1664,14 @@ const void *Crypto_DecryptPacket(crypto_t *crypto, const void *data_src, size_t if(memcmp((const unsigned char *) data_src, h, 16)) // ignore first byte, used for length { Con_Printf("HMAC mismatch\n"); - Com_HexDumpToConsole((const unsigned char *) data_src, len_src); + Com_HexDumpToConsole((const unsigned char *) data_src, (int)len_src); return NULL; } } else { Con_Printf("HMAC mismatch\n"); - Com_HexDumpToConsole((const unsigned char *) data_src, len_src); + Com_HexDumpToConsole((const unsigned char *) data_src, (int)len_src); return NULL; } } @@ -1592,7 +1693,7 @@ const char *Crypto_GetInfoResponseDataString(void) } // network protocol -qboolean Crypto_ServerAppendToChallenge(const char *data_in, size_t len_in, char *data_out, size_t *len_out, size_t maxlen_out) +qbool Crypto_ServerAppendToChallenge(const char *data_in, size_t len_in, char *data_out, size_t *len_out, size_t maxlen_out) { // cheap op, all is precomputed if(!d0_blind_id_dll) @@ -1648,12 +1749,12 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, return CRYPTO_NOMATCH; // will be later accepted if encryption was set up // validate the challenge for (i = 0;i < MAX_CHALLENGES;i++) - if(challenge[i].time > 0) - if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s)) + if(challenges[i].time > 0) + if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s)) break; // if the challenge is not recognized, drop the packet if (i == MAX_CHALLENGES) // challenge mismatch is silent - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing challenge in connect"); crypto = Crypto_ServerFindInstance(peeraddress, false); if(!crypto || !crypto->authenticated) @@ -1668,23 +1769,23 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, id = (cnt ? atoi(cnt) : -1); cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)); if(!cnt) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing cnt in d0pk"); GetUntilNul(&data_in, &len_in); if(!data_in) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing appended data in d0pk"); if(!strcmp(cnt, "0")) { int i; if (!(s = InfoString_GetValue(string + 4, "challenge", infostringvalue, sizeof(infostringvalue)))) - return CRYPTO_DISCARD; // pre-challenge: rather be silent + return Crypto_SoftServerError(data_out, len_out, "missing challenge in d0pk\\0"); // validate the challenge for (i = 0;i < MAX_CHALLENGES;i++) - if(challenge[i].time > 0) - if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s)) + if(challenges[i].time > 0) + if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s)) break; // if the challenge is not recognized, drop the packet - if (i == MAX_CHALLENGES) // challenge mismatch is silent - return CRYPTO_DISCARD; // pre-challenge: rather be silent + if (i == MAX_CHALLENGES) + return Crypto_SoftServerError(data_out, len_out, "invalid challenge in d0pk\\0"); if (!(s = InfoString_GetValue(string + 4, "aeslevel", infostringvalue, sizeof(infostringvalue)))) aeslevel = 0; // not supported @@ -1714,13 +1815,13 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, p = GetUntilNul(&data_in, &len_in); if(p && *p) { + // Find the highest numbered matching key for p. for(i = 0; i < MAX_PUBKEYS; ++i) { if(pubkeys[i]) if(!strcmp(p, pubkeys_fp64[i])) if(pubkeys_havepriv[i]) - if(serverid < 0) - serverid = i; + serverid = i; } if(serverid < 0) return Crypto_ServerError(data_out, len_out, "Invalid server key", NULL); @@ -1728,12 +1829,12 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, p = GetUntilNul(&data_in, &len_in); if(p && *p) { + // Find the highest numbered matching key for p. for(i = 0; i < MAX_PUBKEYS; ++i) { if(pubkeys[i]) if(!strcmp(p, pubkeys_fp64[i])) - if(clientid < 0) - clientid = i; + clientid = i; } if(clientid < 0) return Crypto_ServerError(data_out, len_out, "Invalid client key", NULL); @@ -1759,6 +1860,7 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, // I am the server, and my key is ok... so let's set server_keyfp and server_idfp strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp)); strlcpy(crypto->server_idfp, pubkeys_priv_fp64[CDATA->s], sizeof(crypto->server_idfp)); + crypto->server_issigned = pubkeys_havesig[CDATA->s]; if(!CDATA->id) CDATA->id = qd0_blind_id_new(); @@ -1899,10 +2001,9 @@ static int Crypto_ServerParsePacket_Internal(const char *data_in, size_t len_in, CLEAR_CDATA; return Crypto_ServerError(data_out, len_out, "d0_blind_id_authenticate_with_private_id_verify failed (authentication error)", "Authentication error"); } - if(status) - strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp)); - else - crypto->client_keyfp[0] = 0; + strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp)); + crypto->client_issigned = status; + memset(crypto->client_idfp, 0, sizeof(crypto->client_idfp)); fpbuflen = FP64_SIZE; if(!qd0_blind_id_fingerprint64_public_id(CDATA->id, crypto->client_idfp, &fpbuflen)) @@ -1940,8 +2041,8 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, double t = 0; static double complain_time = 0; const char *cnt; - qboolean do_time = false; - qboolean do_reject = false; + qbool do_time = false; + qbool do_reject = false; char infostringvalue[MAX_INPUTLINE]; if(crypto_servercpupercent.value > 0 || crypto_servercpumaxtime.value > 0) if(len_in > 5 && !memcmp(data_in, "d0pk\\", 5)) @@ -1957,7 +2058,7 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, // check if we may perform crypto... if(crypto_servercpupercent.value > 0) { - crypto_servercpu_accumulator += (realtime - crypto_servercpu_lastrealtime) * crypto_servercpupercent.value * 0.01; + crypto_servercpu_accumulator += (host.realtime - crypto_servercpu_lastrealtime) * crypto_servercpupercent.value * 0.01; if(crypto_servercpumaxtime.value) if(crypto_servercpu_accumulator > crypto_servercpumaxtime.value) crypto_servercpu_accumulator = crypto_servercpumaxtime.value; @@ -1965,13 +2066,13 @@ int Crypto_ServerParsePacket(const char *data_in, size_t len_in, char *data_out, else { if(crypto_servercpumaxtime.value > 0) - if(realtime != crypto_servercpu_lastrealtime) + if(host.realtime != crypto_servercpu_lastrealtime) crypto_servercpu_accumulator = crypto_servercpumaxtime.value; } - crypto_servercpu_lastrealtime = realtime; + crypto_servercpu_lastrealtime = host.realtime; if(do_reject && crypto_servercpu_accumulator < 0) { - if(realtime > complain_time + 5) + if(host.realtime > complain_time + 5) Con_Printf("crypto: cannot perform requested crypto operations; denial service attack or crypto_servercpupercent/crypto_servercpumaxtime are too low\n"); *len_out = 0; return CRYPTO_DISCARD; @@ -2001,7 +2102,7 @@ static int Crypto_ClientError(char *data_out, size_t *len_out, const char *msg) static int Crypto_SoftClientError(char *data_out, size_t *len_out, const char *msg) { *len_out = 0; - Con_Printf("%s\n", msg); + Con_DPrintf("%s\n", msg); return CRYPTO_DISCARD; } @@ -2025,8 +2126,8 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if (len_in == 6 && !memcmp(string, "accept", 6) && cls.connect_trying && d0_rijndael_dll) { int wantserverid = -1; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL); - if(!crypto || !crypto->authenticated) + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL, NULL); + if(!crypto || !crypto->authenticated) // we ALSO get here if we are using an encrypted connection, so let's rule this out { if(wantserverid >= 0) return Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though a host key is present"); @@ -2035,11 +2136,33 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, } return CRYPTO_NOMATCH; } - else if (len_in >= 1 && string[0] == 'j' && cls.connect_trying && d0_rijndael_dll && crypto_aeslevel.integer >= 3) + else if (len_in >= 1 && string[0] == 'j' && cls.connect_trying && d0_rijndael_dll) { int wantserverid = -1; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL); - if(!crypto || !crypto->authenticated) + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL, NULL); + //if(!crypto || !crypto->authenticated) + { + if(wantserverid >= 0) + return Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though a host key is present"); + if(crypto_aeslevel.integer >= 3) + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)"); + } + return CRYPTO_NOMATCH; + } + else if (len_in >= 5 && BuffLittleLong((unsigned char *) string) == ((int)NETFLAG_CTL | (int)len_in)) + { + int wantserverid = -1; + + // these three are harmless + if((unsigned char) string[4] == CCREP_SERVER_INFO) + return CRYPTO_NOMATCH; + if((unsigned char) string[4] == CCREP_PLAYER_INFO) + return CRYPTO_NOMATCH; + if((unsigned char) string[4] == CCREP_RULE_INFO) + return CRYPTO_NOMATCH; + + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, NULL, 0, NULL, NULL); + //if(!crypto || !crypto->authenticated) { if(wantserverid >= 0) return Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though a host key is present"); @@ -2084,14 +2207,19 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, const char *p; int i; int clientid = -1, serverid = -1, wantserverid = -1; - qboolean server_can_auth = true; + qbool server_can_auth = true; char wantserver_idfp[FP64_SIZE+1]; - int wantserver_aeslevel; + int wantserver_aeslevel = 0; + qbool wantserver_issigned = false; + + // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server. + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + return Crypto_SoftClientError(data_out, len_out, "challenge message from wrong server"); // if we have a stored host key for the server, assume serverid to already be selected! // (the loop will refuse to overwrite this one then) wantserver_idfp[0] = 0; - Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, wantserver_idfp, sizeof(wantserver_idfp), &wantserver_aeslevel); + Crypto_RetrieveHostKey(&cls.connect_address, &wantserverid, NULL, 0, wantserver_idfp, sizeof(wantserver_idfp), &wantserver_aeslevel, &wantserver_issigned); // requirement: wantserver_idfp is a full ID if wantserverid set // if we leave, we have to consider the connection @@ -2104,7 +2232,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, GetUntilNul(&data_in, &len_in); if(!data_in) return (wantserverid >= 0) ? Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though a host key is present") : - (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; // FTEQW extension protocol @@ -2140,7 +2268,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(!vlen_blind_id_ptr) return (wantserverid >= 0) ? Crypto_ClientError(data_out, len_out, "Server tried an unauthenticated connection even though authentication is required") : - (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; data_in = vlen_blind_id_ptr; @@ -2164,22 +2292,20 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, break; continue; } + // Find the highest numbered matching key for p. for(i = 0; i < MAX_PUBKEYS; ++i) { if(pubkeys[i]) if(!strcmp(p, pubkeys_fp64[i])) { if(pubkeys_havepriv[i]) - if(clientid < 0) - clientid = i; + clientid = i; if(server_can_auth) - if(serverid < 0) - if(wantserverid < 0 || i == wantserverid) - serverid = i; + if(wantserverid < 0 || i == wantserverid) + serverid = i; } } - if(clientid >= 0 && serverid >= 0) - break; + // Not breaking, as higher keys in the list always have priority. } // if stored host key is not found: @@ -2188,7 +2314,6 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(serverid >= 0 || clientid >= 0) { - // TODO at this point, fill clientside crypto struct! MAKE_CDATA; CDATA->cdata_id = ++cdata_id; CDATA->s = serverid; @@ -2200,6 +2325,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, crypto->server_keyfp[0] = 0; crypto->server_idfp[0] = 0; memcpy(CDATA->wantserver_idfp, wantserver_idfp, sizeof(crypto->server_idfp)); + CDATA->wantserver_issigned = wantserver_issigned; if(CDATA->wantserver_idfp[0]) // if we know a host key, honor its encryption setting switch(bound(0, d0_rijndael_dll ? crypto_aeslevel.integer : 0, 3)) @@ -2207,7 +2333,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, default: // dummy, never happens, but to make gcc happy... case 0: if(wantserver_aeslevel >= 3) - return Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL); + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)"); CDATA->wantserver_aes = false; break; case 1: @@ -2218,7 +2344,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, break; case 3: if(wantserver_aeslevel <= 0) - return Crypto_ServerError(data_out, len_out, "This server requires encryption to be supported (crypto_aeslevel >= 1, and d0_rijndael library must be present)", NULL); + return Crypto_ClientError(data_out, len_out, "This server requires encryption to be supported (crypto_aeslevel >= 1, and d0_rijndael library must be present)"); CDATA->wantserver_aes = true; break; } @@ -2234,6 +2360,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, // I am the client, and my key is ok... so let's set client_keyfp and client_idfp strlcpy(crypto->client_keyfp, pubkeys_fp64[CDATA->c], sizeof(crypto->client_keyfp)); strlcpy(crypto->client_idfp, pubkeys_priv_fp64[CDATA->c], sizeof(crypto->client_idfp)); + crypto->client_issigned = pubkeys_havesig[CDATA->c]; } if(serverid >= 0) @@ -2253,7 +2380,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, CDATA->next_step = 1; *len_out = data_out_p - data_out; } - else if(clientid >= 0) + else // if(clientid >= 0) // guaranteed by condition one level outside { // skip over server auth, perform client auth only if(!CDATA->id) @@ -2277,9 +2404,6 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, data_out_p += *len_out; *len_out = data_out_p - data_out; } - else - *len_out = data_out_p - data_out; - return CRYPTO_DISCARD; } else @@ -2287,7 +2411,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(wantserver_idfp[0]) // if we know a host key, honor its encryption setting if(wantserver_aeslevel >= 3) return Crypto_ClientError(data_out, len_out, "Server insists on encryption, but neither can authenticate to the other"); - return (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ServerError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)", NULL) : + return (d0_rijndael_dll && crypto_aeslevel.integer >= 3) ? Crypto_ClientError(data_out, len_out, "This server requires encryption to be not required (crypto_aeslevel <= 2)") : CRYPTO_NOMATCH; } } @@ -2295,6 +2419,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, { const char *cnt; int id; + + // Must check the source IP here, if we want to prevent other servers' replies from falsely advancing the crypto state, preventing successful connect to the real server. + if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) + return Crypto_SoftClientError(data_out, len_out, "d0pk\\ message from wrong server"); + cnt = InfoString_GetValue(string + 4, "id", infostringvalue, sizeof(infostringvalue)); id = (cnt ? atoi(cnt) : -1); cnt = InfoString_GetValue(string + 4, "cnt", infostringvalue, sizeof(infostringvalue)); @@ -2308,11 +2437,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, { if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 1) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); - cls.connect_nextsendtime = max(cls.connect_nextsendtime, realtime + 1); // prevent "hammering" + cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering" if((s = InfoString_GetValue(string + 4, "aes", infostringvalue, sizeof(infostringvalue)))) aes = atoi(s); @@ -2357,21 +2486,26 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 3) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); - cls.connect_nextsendtime = max(cls.connect_nextsendtime, realtime + 1); // prevent "hammering" + cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering" if(!qd0_blind_id_authenticate_with_private_id_verify(CDATA->id, data_in, len_in, msgbuf, &msgbuflen, &status)) { CLEAR_CDATA; return Crypto_ClientError(data_out, len_out, "d0_blind_id_authenticate_with_private_id_verify failed (server authentication error)"); } - if(status) - strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp)); - else - crypto->server_keyfp[0] = 0; + + strlcpy(crypto->server_keyfp, pubkeys_fp64[CDATA->s], sizeof(crypto->server_keyfp)); + if (!status && CDATA->wantserver_issigned) + { + CLEAR_CDATA; + return Crypto_ClientError(data_out, len_out, "Stored host key requires a valid signature, but server did not provide any"); + } + crypto->server_issigned = status; + memset(crypto->server_idfp, 0, sizeof(crypto->server_idfp)); fpbuflen = FP64_SIZE; if(!qd0_blind_id_fingerprint64_public_id(CDATA->id, crypto->server_idfp, &fpbuflen)) @@ -2393,7 +2527,7 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, } // cache the server key - Crypto_StoreHostKey(&cls.connect_address, va(vabuf, sizeof(vabuf), "%d %s@%s", crypto->use_aes ? 1 : 0, crypto->server_idfp, pubkeys_fp64[CDATA->s]), false); + Crypto_StoreHostKey(&cls.connect_address, va(vabuf, sizeof(vabuf), "%d %s@%s%s", crypto->use_aes ? 1 : 0, crypto->server_idfp, crypto->server_issigned ? "" : "~", pubkeys_fp64[CDATA->s]), false); if(CDATA->c >= 0) { @@ -2434,11 +2568,11 @@ int Crypto_ClientParsePacket(const char *data_in, size_t len_in, char *data_out, if(id >= 0) if(CDATA->cdata_id != id) - return Crypto_SoftServerError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); + return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\id\\%d when expecting %d", id, CDATA->cdata_id)); if(CDATA->next_step != 5) return Crypto_SoftClientError(data_out, len_out, va(vabuf, sizeof(vabuf), "Got d0pk\\cnt\\%s when expecting %d", cnt, CDATA->next_step)); - cls.connect_nextsendtime = max(cls.connect_nextsendtime, realtime + 1); // prevent "hammering" + cls.connect_nextsendtime = max(cls.connect_nextsendtime, host.realtime + 1); // prevent "hammering" if(CDATA->s < 0) // only if server didn't auth {