/** * Set a series of bytes with a random number. Individual bytes can be 0 */ EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data) { #if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM) /* use the Linux default */ read(rng_fd, rand_data, num_rand_bytes); /* read from /dev/urandom */ #elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB) /* use Microsoft Crypto Libraries */ CryptGenRandom(gCryptProv, num_rand_bytes, rand_data); #elif defined(ESP8266) for (size_t cb = 0; cb < num_rand_bytes; cb += 4) { uint32_t r = phy_get_rand(); size_t left = num_rand_bytes - cb; left = (left < 4) ? left : 4; memcpy(rand_data + cb, &r, left); } #else /* nothing else to use, so use a custom RNG */ /* The method we use when we've got nothing better. Use RC4, time and a couple of random seeds to generate a random sequence */ RC4_CTX rng_ctx; struct timeval tv; MD5_CTX rng_digest_ctx; uint8_t digest[MD5_SIZE]; uint64_t *ep; int i; /* A proper implementation would use counters etc for entropy */ gettimeofday(&tv, NULL); ep = (uint64_t *)entropy_pool; ep[0] ^= ENTROPY_COUNTER1; ep[1] ^= ENTROPY_COUNTER2; /* use a digested version of the entropy pool as a key */ MD5_Init(&rng_digest_ctx); MD5_Update(&rng_digest_ctx, entropy_pool, ENTROPY_POOL_SIZE); MD5_Final(digest, &rng_digest_ctx); /* come up with the random sequence */ RC4_setup(&rng_ctx, digest, MD5_SIZE); /* use as a key */ memcpy(rand_data, entropy_pool, num_rand_bytes < ENTROPY_POOL_SIZE ? num_rand_bytes : ENTROPY_POOL_SIZE); RC4_crypt(&rng_ctx, rand_data, rand_data, num_rand_bytes); /* move things along */ for (i = ENTROPY_POOL_SIZE-1; i >= MD5_SIZE ; i--) entropy_pool[i] = entropy_pool[i-MD5_SIZE]; /* insert the digest at the start of the entropy pool */ memcpy(entropy_pool, digest, MD5_SIZE); #endif }
// Encode the message in net_scratchpad and start the send process void net_msg_encode_puts(void) { int k; char code; if (net_state == NET_STATE_DIAGMODE) { net_puts_ram(net_scratchpad); } else { if ((ptokenmade==1)&& (net_scratchpad[5]!='E')&& (net_scratchpad[5]!='A')&& (net_scratchpad[5]!='a')&& (net_scratchpad[5]!='g')&& (net_scratchpad[5]!='P')) { // We must convert the message to a paranoid one... // The message in net_scratchpad is of the form MP-0 X... // Where X is the code and ... is the (optional) data // Let's rebuild it in the net_msg_scratchpad... code = net_scratchpad[5]; strcpy(net_msg_scratchpad,net_scratchpad+6); // Paranoid encrypt the message part of the transaction RC4_setup(&pm_crypto1, &pm_crypto2, pdigest, MD5_SIZE); for (k=0;k<1024;k++) { net_scratchpad[0] = 0; RC4_crypt(&pm_crypto1, &pm_crypto2, net_scratchpad, 1); } k=strlen(net_msg_scratchpad); RC4_crypt(&pm_crypto1, &pm_crypto2, net_msg_scratchpad, k); strcpypgm2ram(net_scratchpad,(char const rom far*)"MP-0 EM"); net_scratchpad[7] = code; base64encode(net_msg_scratchpad,k,net_scratchpad+8); // The messdage is now in paranoid mode... } k=strlen(net_scratchpad); RC4_crypt(&tx_crypto1, &tx_crypto2, net_scratchpad, k); base64encodesend(net_scratchpad,k); } net_puts_rom("\r\n"); }
/* * Decrypt a pkcs8 block. */ static int p8_decrypt(const char *uni_pass, int uni_pass_len, const uint8_t *salt, int iter, uint8_t *priv_key, int priv_key_len, int id) { uint8_t p[BLOCK_SIZE*2]; uint8_t d[BLOCK_SIZE]; uint8_t Ai[SHA1_SIZE]; SHA1_CTX sha_ctx; RC4_CTX rc4_ctx; int i; for (i = 0; i < BLOCK_SIZE; i++) { p[i] = salt[i % SALT_SIZE]; p[BLOCK_SIZE+i] = uni_pass[i % uni_pass_len]; d[i] = id; } /* get the key - no IV since we are using RC4 */ SHA1_Init(&sha_ctx); SHA1_Update(&sha_ctx, d, sizeof(d)); SHA1_Update(&sha_ctx, p, sizeof(p)); SHA1_Final(Ai, &sha_ctx); for (i = 1; i < iter; i++) { SHA1_Init(&sha_ctx); SHA1_Update(&sha_ctx, Ai, SHA1_SIZE); SHA1_Final(Ai, &sha_ctx); } /* do the decryption */ if (id == PKCS12_KEY_ID) { RC4_setup(&rc4_ctx, Ai, 16); RC4_crypt(&rc4_ctx, priv_key, priv_key, priv_key_len); } else /* MAC */ memcpy(priv_key, Ai, SHA1_SIZE); return 0; }
// Receive a NET msg from the OVMS server void net_msg_in(char* msg) { int k; char s; if (net_msg_serverok == 0) { if (memcmppgm2ram(msg, (char const rom far*)"MP-S 0 ", 7) == 0) { net_msg_server_welcome(msg+7); net_granular_tick = 3590; // Nasty hack to force a status transmission in 10 seconds } return; // otherwise ignore it } // Ok, we've got an encrypted message waiting for work. // The following is a nasty hack because base64decode doesn't like incoming // messages of length divisible by 4, and is really expecting a CRLF // terminated string, so we give it one... CHECKPOINT(0x40) if (((strlen(msg)*4)/3) >= (NET_BUF_MAX-3)) { // Quick exit to reset link if incoming message is too big net_state_enter(NET_STATE_DONETINIT); return; } strcatpgm2ram(msg,(char const rom far*)"\r\n"); k = base64decode(msg,net_scratchpad); CHECKPOINT(0x41) RC4_crypt(&rx_crypto1, &rx_crypto2, net_scratchpad, k); if (memcmppgm2ram(net_scratchpad, (char const rom far*)"MP-0 ", 5) != 0) { net_state_enter(NET_STATE_DONETINIT); return; } msg = net_scratchpad+5; if ((*msg == 'E')&&(msg[1]=='M')) { // A paranoid-mode message from the server (or, more specifically, app) // The following is a nasty hack because base64decode doesn't like incoming // messages of length divisible by 4, and is really expecting a CRLF // terminated string, so we give it one... msg += 2; // Now pointing to the code just before encrypted paranoid message strcatpgm2ram(msg,(char const rom far*)"\r\n"); k = base64decode(msg+1,net_msg_scratchpad+1); RC4_setup(&pm_crypto1, &pm_crypto2, pdigest, MD5_SIZE); for (k=0;k<1024;k++) { net_scratchpad[0] = 0; RC4_crypt(&pm_crypto1, &pm_crypto2, net_scratchpad, 1); } RC4_crypt(&pm_crypto1, &pm_crypto2, net_msg_scratchpad+1, k); net_msg_scratchpad[0] = *msg; // The code // The message is now out of paranoid mode... msg = net_msg_scratchpad; } CHECKPOINT(0x42) switch (*msg) { case 'A': // PING strcpypgm2ram(net_scratchpad,(char const rom far*)"MP-0 a"); if (net_msg_sendpending==0) { net_msg_start(); net_msg_encode_puts(); net_msg_send(); } break; case 'Z': // PEER connection if (msg[1] != '0') { net_apps_connected = 1; if (net_msg_sendpending==0) { net_msg_start(); net_msgp_stat(0); net_msgp_gps(0); net_msgp_tpms(0); net_msgp_firmware(0); net_msgp_environment(0); net_msg_send(); } } else { net_apps_connected = 0; } break; case 'h': // Historical data acknowledgement #ifdef OVMS_LOGGINGMODULE logging_ack(atoi(msg+1)); #endif // #ifdef OVMS_LOGGINGMODULE break; case 'C': // COMMAND net_msg_cmd_in(msg+1); if (net_msg_sendpending==0) net_msg_cmd_do(); break; } }
void net_msg_server_welcome(char *msg) { // The server has sent a welcome (token <space> base64digest) char *d,*p,*s; int k; unsigned char hwv = 1; #ifdef OVMS_HW_V2 hwv = 2; #endif if( !msg ) return; for (d=msg;(*d != 0)&&(*d != ' ');d++) ; if (*d != ' ') return; *d++ = 0; // At this point, <msg> is token, and <x> is base64digest // (both null-terminated) // Check for token-replay attack if (strcmp(token,msg)==0) return; // Server is using our token! // Validate server token p = par_get(PARAM_SERVERPASS); hmac_md5(msg, strlen(msg), p, strlen(p), digest); base64encode(digest, MD5_SIZE, net_scratchpad); if (strcmp(d,net_scratchpad)!=0) return; // Invalid server digest // Ok, at this point, our token is ok strcpy(net_scratchpad,msg); strcat(net_scratchpad,token); hmac_md5(net_scratchpad,strlen(net_scratchpad),p,strlen(p),digest); // Setup, and prime the rx and tx cryptos RC4_setup(&rx_crypto1, &rx_crypto2, digest, MD5_SIZE); for (k=0;k<1024;k++) { net_scratchpad[0] = 0; RC4_crypt(&rx_crypto1, &rx_crypto2, net_scratchpad, 1); } RC4_setup(&tx_crypto1, &tx_crypto2, digest, MD5_SIZE); for (k=0;k<1024;k++) { net_scratchpad[0] = 0; RC4_crypt(&tx_crypto1, &tx_crypto2, net_scratchpad, 1); } net_msg_serverok = 1; p = par_get(PARAM_PARANOID); if (*p == 'P') { // Paranoid mode initialisation if (ptokenmade==0) { // We need to make the ptoken for (k=0;k<TOKEN_SIZE;k++) { ptoken[k] = cb64[rand()%64]; } ptoken[TOKEN_SIZE] = 0; } // To be truly paranoid, we must send the paranoid token to the server ;-) ptokenmade=0; // Leave it off for the MP-0 ET message strcpypgm2ram(net_scratchpad,(char const rom far*)"MP-0 ET"); strcat(net_scratchpad,ptoken); net_msg_start(); net_msg_encode_puts(); net_msg_send(); ptokenmade=1; // And enable paranoid mode from now on... // And calculate the pdigest for future use p = par_get(PARAM_MODULEPASS); hmac_md5(ptoken, strlen(ptoken), p, strlen(p), pdigest); } else { ptokenmade = 0; // This disables paranoid mode } /* DEBUG / QA stats: Send crash counter and last reason: * * MP-0 H*-OVM-DebugCrash,0,2592000 * ,<firmware_version>/<vehicle_type><vehicle_version>/V<hardware_version> * ,<crashcnt>,<crashreason>,<checkpoint> */ if (debug_crashreason & 0x80) { debug_crashreason &= ~0x80; // clear checkpoint hold bit s = stp_i(net_scratchpad, "MP-0 H*-OVM-DebugCrash,0,2592000,", ovms_firmware[0]); s = stp_i(s, ".", ovms_firmware[1]); s = stp_i(s, ".", ovms_firmware[2]); s = stp_s(s, "/", par_get(PARAM_VEHICLETYPE)); if (vehicle_version) s = stp_rom(s, vehicle_version); s = stp_i(s, "/V", hwv); s = stp_i(s, ",", debug_crashcnt); s = stp_x(s, ",", debug_crashreason); s = stp_i(s, ",", debug_checkpoint); delay100(20); net_msg_start(); net_msg_encode_puts(); net_msg_send(); } #ifdef OVMS_LOGGINGMODULE logging_serverconnect(); #endif // #ifdef OVMS_LOGGINGMODULE }
void net_msg_server_welcome(char *msg) { // The server has sent a welcome (token <space> base64digest) char *d,*p; int k; for (d=msg;(*d != 0)&&(*d != ' ');d++) ; if (*d != ' ') return; *d++ = 0; // At this point, <msg> is token, and <x> is base64digest // (both null-terminated) // Check for token-replay attack if (strcmp(token,msg)==0) return; // Server is using our token! // Validate server token p = par_get(PARAM_NETPASS1); hmac_md5(msg, strlen(msg), p, strlen(p), digest); base64encode(digest, MD5_SIZE, net_scratchpad); if (strcmp(d,net_scratchpad)!=0) return; // Invalid server digest // Ok, at this point, our token is ok strcpy(net_scratchpad,msg); strcat(net_scratchpad,token); hmac_md5(net_scratchpad,strlen(net_scratchpad),p,strlen(p),digest); // Setup, and prime the rx and tx cryptos RC4_setup(&rx_crypto1, &rx_crypto2, digest, MD5_SIZE); for (k=0;k<1024;k++) { net_scratchpad[0] = 0; RC4_crypt(&rx_crypto1, &rx_crypto2, net_scratchpad, 1); } RC4_setup(&tx_crypto1, &tx_crypto2, digest, MD5_SIZE); for (k=0;k<1024;k++) { net_scratchpad[0] = 0; RC4_crypt(&tx_crypto1, &tx_crypto2, net_scratchpad, 1); } net_msg_serverok = 1; p = par_get(PARAM_PARANOID); if (*p == 'P') { // Paranoid mode initialisation if (ptokenmade==0) { // We need to make the ptoken for (k=0;k<TOKEN_SIZE;k++) { ptoken[k] = cb64[rand()%64]; } ptoken[TOKEN_SIZE] = 0; } // To be truly paranoid, we must send the paranoid token to the server ;-) ptokenmade=0; // Leave it off for the MP-0 ET message strcpypgm2ram(net_scratchpad,(char const rom far*)"MP-0 ET"); strcat(net_scratchpad,ptoken); net_msg_start(); net_msg_encode_puts(); net_msg_send(); ptokenmade=1; // And enable paranoid mode from now on... // And calculate the pdigest for future use p = par_get(PARAM_REGPASS); hmac_md5(ptoken, strlen(ptoken), p, strlen(p), pdigest); } else { ptokenmade = 0; // This disables paranoid mode } }