#include #include #include #include #include #include #include #include #include #include #include #include "I2PEndian.h" #include "Reseed.h" #include "Log.h" #include "Identity.h" #include "Crypto.h" #include "NetDb.h" #include "util.h" namespace i2p { namespace data { static std::vector httpsReseedHostList = { "https://reseed.i2p-projekt.de/", // Only HTTPS "https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v3) support "https://i2p.mooo.com/netDb/", "https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required "https://us.reseed.i2p2.no:444/", "https://uk.reseed.i2p2.no:444/", "https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support "https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support "https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v3) support }; Reseeder::Reseeder() { } Reseeder::~Reseeder() { } int Reseeder::ReseedNowSU3 () { auto ind = rand () % httpsReseedHostList.size (); std::string& reseedHost = httpsReseedHostList[ind]; return ReseedFromSU3 (reseedHost, true); } int Reseeder::ReseedFromSU3 (const std::string& host, bool https) { std::string url = host + "i2pseeds.su3"; LogPrint (eLogInfo, "Dowloading SU3 from ", host); std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url); if (su3.length () > 0) { std::stringstream s(su3); return ProcessSU3Stream (s); } else { LogPrint (eLogWarning, "SU3 download failed"); return 0; } } int Reseeder::ProcessSU3File (const char * filename) { std::ifstream s(filename, std::ifstream::binary); if (s.is_open ()) return ProcessSU3Stream (s); else { LogPrint (eLogError, "Can't open file ", filename); return 0; } } const char SU3_MAGIC_NUMBER[]="I2Psu3"; const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; int Reseeder::ProcessSU3Stream (std::istream& s) { char magicNumber[7]; s.read (magicNumber, 7); // magic number and zero byte 6 if (strcmp (magicNumber, SU3_MAGIC_NUMBER)) { LogPrint (eLogError, "Unexpected SU3 magic number"); return 0; } s.seekg (1, std::ios::cur); // su3 file format version SigningKeyType signatureType; s.read ((char *)&signatureType, 2); // signature type signatureType = be16toh (signatureType); uint16_t signatureLength; s.read ((char *)&signatureLength, 2); // signature length signatureLength = be16toh (signatureLength); s.seekg (1, std::ios::cur); // unused uint8_t versionLength; s.read ((char *)&versionLength, 1); // version length s.seekg (1, std::ios::cur); // unused uint8_t signerIDLength; s.read ((char *)&signerIDLength, 1); // signer ID length uint64_t contentLength; s.read ((char *)&contentLength, 8); // content length contentLength = be64toh (contentLength); s.seekg (1, std::ios::cur); // unused uint8_t fileType; s.read ((char *)&fileType, 1); // file type if (fileType != 0x00) // zip file { LogPrint (eLogError, "Can't handle file type ", (int)fileType); return 0; } s.seekg (1, std::ios::cur); // unused uint8_t contentType; s.read ((char *)&contentType, 1); // content type if (contentType != 0x03) // reseed data { LogPrint (eLogError, "Unexpected content type ", (int)contentType); return 0; } s.seekg (12, std::ios::cur); // unused s.seekg (versionLength, std::ios::cur); // skip version char signerID[256]; s.read (signerID, signerIDLength); // signerID signerID[signerIDLength] = 0; //try to verify signature auto it = m_SigningKeys.find (signerID); if (it != m_SigningKeys.end ()) { // TODO: implement all signature types if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096) { size_t pos = s.tellg (); size_t tbsLen = pos + contentLength; uint8_t * tbs = new uint8_t[tbsLen]; s.seekg (0, std::ios::beg); s.read ((char *)tbs, tbsLen); uint8_t * signature = new uint8_t[signatureLength]; s.read ((char *)signature, signatureLength); // RSA-raw { // calculate digest uint8_t digest[64]; SHA512 (tbs, tbsLen, digest); // encrypt signature BN_CTX * bnctx = BN_CTX_new (); BIGNUM * s = BN_new (), * n = BN_new (); BN_bin2bn (signature, signatureLength, s); BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n); BN_mod_exp (s, s, i2p::crypto::rsae, n, bnctx); // s = s^e mod n uint8_t * enSigBuf = new uint8_t[signatureLength]; i2p::crypto::bn2buf (s, enSigBuf, signatureLength); // digest is right aligned // we can't use RSA_verify due wrong padding in SU3 if (memcmp (enSigBuf + (signatureLength - 64), digest, 64)) LogPrint (eLogWarning, "SU3 signature verification failed"); delete[] enSigBuf; BN_free (s); BN_free (n); BN_CTX_free (bnctx); } delete[] signature; delete[] tbs; s.seekg (pos, std::ios::beg); } else LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported"); } else LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded"); // handle content int numFiles = 0; size_t contentPos = s.tellg (); while (!s.eof ()) { uint32_t signature; s.read ((char *)&signature, 4); signature = le32toh (signature); if (signature == ZIP_HEADER_SIGNATURE) { // next local file s.seekg (2, std::ios::cur); // version uint16_t bitFlag; s.read ((char *)&bitFlag, 2); bitFlag = le16toh (bitFlag); uint16_t compressionMethod; s.read ((char *)&compressionMethod, 2); compressionMethod = le16toh (compressionMethod); s.seekg (4, std::ios::cur); // skip fields we don't care about uint32_t compressedSize, uncompressedSize; uint32_t crc_32; s.read ((char *)&crc_32, 4); crc_32 = le32toh (crc_32); s.read ((char *)&compressedSize, 4); compressedSize = le32toh (compressedSize); s.read ((char *)&uncompressedSize, 4); uncompressedSize = le32toh (uncompressedSize); uint16_t fileNameLength, extraFieldLength; s.read ((char *)&fileNameLength, 2); fileNameLength = le16toh (fileNameLength); s.read ((char *)&extraFieldLength, 2); extraFieldLength = le16toh (extraFieldLength); char localFileName[255]; s.read (localFileName, fileNameLength); localFileName[fileNameLength] = 0; s.seekg (extraFieldLength, std::ios::cur); // take care about data desriptor if presented if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) { size_t pos = s.tellg (); if (!FindZipDataDescriptor (s)) { LogPrint (eLogError, "SU3 archive data descriptor not found"); return numFiles; } s.read ((char *)&crc_32, 4); crc_32 = le32toh (crc_32); s.read ((char *)&compressedSize, 4); compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data s.read ((char *)&uncompressedSize, 4); uncompressedSize = le32toh (uncompressedSize); // now we know compressed and uncompressed size s.seekg (pos, std::ios::beg); // back to compressed data } LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes"); if (!compressedSize) { LogPrint (eLogWarning, "Unexpected size 0. Skipped"); continue; } uint8_t * compressed = new uint8_t[compressedSize]; s.read ((char *)compressed, compressedSize); if (compressionMethod) // we assume Deflate { z_stream inflator; memset (&inflator, 0, sizeof (inflator)); inflateInit2 (&inflator, -MAX_WBITS); // no zlib header uint8_t * uncompressed = new uint8_t[uncompressedSize]; inflator.next_in = compressed; inflator.avail_in = compressedSize; inflator.next_out = uncompressed; inflator.avail_out = uncompressedSize; int err; if ((err = inflate (&inflator, Z_SYNC_FLUSH)) >= 0) { uncompressedSize -= inflator.avail_out; if (crc32 (0, uncompressed, uncompressedSize) == crc_32) { i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize); numFiles++; } else LogPrint (eLogError, "CRC32 verification failed"); } else LogPrint (eLogError, "decompression error ", err); delete[] uncompressed; inflateEnd (&inflator); } else // no compression { i2p::data::netdb.AddRouterInfo (compressed, compressedSize); numFiles++; } delete[] compressed; if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR) s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4) } else { if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE) LogPrint (eLogWarning, "Missing zip central directory header"); break; // no more files } size_t end = s.tellg (); if (end - contentPos >= contentLength) break; // we are beyond contentLength } return numFiles; } const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 }; bool Reseeder::FindZipDataDescriptor (std::istream& s) { size_t nextInd = 0; while (!s.eof ()) { uint8_t nextByte; s.read ((char *)&nextByte, 1); if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd]) { nextInd++; if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE)) return true; } else nextInd = 0; } return false; } void Reseeder::LoadCertificate (const std::string& filename) { SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ()); int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) { SSL * ssl = SSL_new (ctx); X509 * cert = SSL_get_certificate (ssl); // verify if (cert) { // extract issuer name char name[100]; X509_NAME_oneline (X509_get_issuer_name(cert), name, 100); // extract RSA key (we need n only, e = 65537) RSA * key = X509_get_pubkey (cert)->pkey.rsa; PublicKey value; i2p::crypto::bn2buf (key->n, value, 512); m_SigningKeys[name] = value; } SSL_free (ssl); } else LogPrint (eLogError, "Can't open certificate file ", filename); SSL_CTX_free (ctx); } void Reseeder::LoadCertificates () { boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed"; if (!boost::filesystem::exists (reseedDir)) { LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist"); return; } int numCertificates = 0; boost::filesystem::directory_iterator end; // empty for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it) { if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt") { LoadCertificate (it->path ().string ()); numCertificates++; } } LogPrint (eLogInfo, numCertificates, " certificates loaded"); } std::string Reseeder::HttpsRequest (const std::string& address) { i2p::util::http::url u(address); if (u.port_ == 80) u.port_ = 443; boost::asio::io_service service; boost::system::error_code ecode; auto it = boost::asio::ip::tcp::resolver(service).resolve ( boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode); if (!ecode) { boost::asio::ssl::context ctx(service, boost::asio::ssl::context::tlsv12); ctx.set_verify_mode(boost::asio::ssl::context::verify_none); boost::asio::ssl::stream s(service, ctx); s.lowest_layer().connect (*it, ecode); if (!ecode) { s.handshake (boost::asio::ssl::stream_base::client, ecode); if (!ecode) { LogPrint (eLogInfo, "Connected to ", u.host_, ":", u.port_); // send request std::stringstream ss; ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_ << "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n"; s.write_some (boost::asio::buffer (ss.str ())); // read response std::stringstream rs; char response[1024]; size_t l = 0; do { l = s.read_some (boost::asio::buffer (response, 1024), ecode); if (l) rs.write (response, l); } while (!ecode && l); // process response return i2p::util::http::GetHttpContent (rs); } else LogPrint (eLogError, "SSL handshake failed: ", ecode.message ()); } else LogPrint (eLogError, "Couldn't connect to ", u.host_, ": ", ecode.message ()); } else LogPrint (eLogError, "Couldn't resolve address ", u.host_, ": ", ecode.message ()); return ""; } } }