diff --git a/libretroshare/src/rsserver/p3peers.cc b/libretroshare/src/rsserver/p3peers.cc index 35be7e728..0e2a04df4 100644 --- a/libretroshare/src/rsserver/p3peers.cc +++ b/libretroshare/src/rsserver/p3peers.cc @@ -1392,7 +1392,7 @@ bool p3Peers::parseShortInvite(const std::string& inviteStrUrl, RsPeerDetails& d case RsShortInviteFieldType::HIDDEN_LOCATOR: details.hiddenType = (((uint32_t)buf[0]) << 24)+(((uint32_t)buf[1])<<16)+(((uint32_t)buf[2])<<8) + (uint32_t)buf[3]; details.hiddenNodePort = (((uint32_t)buf[4]) << 8)+ (uint32_t)buf[5]; - + details.isHiddenNode = true; details.hiddenNodeAddress = std::string((char*)&buf[6],s-6); break; @@ -1538,7 +1538,7 @@ std::string p3Peers::GetRetroshareInvite( if (getPeerDetails(ssl_id, detail)) { - if(!includeExtraLocators) detail.ipAddressList.clear(); + if(!includeExtraLocators && !detail.isHiddenNode) detail.ipAddressList.clear(); unsigned char *mem_block = nullptr; size_t mem_block_size = 0; diff --git a/retroshare-gui/src/TorControl/AddOnionCommand.cpp b/retroshare-gui/src/TorControl/AddOnionCommand.cpp index f454b690b..fb2047dc7 100644 --- a/retroshare-gui/src/TorControl/AddOnionCommand.cpp +++ b/retroshare-gui/src/TorControl/AddOnionCommand.cpp @@ -53,10 +53,12 @@ QByteArray AddOnionCommand::build() QByteArray out("ADD_ONION"); if (m_service->privateKey().isLoaded()) { - out += " RSA1024:"; - out += m_service->privateKey().encodedPrivateKey(CryptoKey::DER).toBase64(); + out += " "; + out += m_service->privateKey().bytes(); } else { - out += " NEW:RSA1024"; + //out += " NEW:RSA1024"; // this is v2. For v3, use NEW:BEST, or NEW:ED25519-V3 + //out += " NEW:ED25519-V3"; // this is v3. + out += " NEW:BEST"; // this is v3, but without control of key type. Generates a RSA1024 key on older Tor versions. } foreach (const HiddenService::Target &target, m_service->targets()) { @@ -80,12 +82,21 @@ void AddOnionCommand::onReply(int statusCode, const QByteArray &data) return; } - const QByteArray keyPrefix("PrivateKey=RSA1024:"); + const QByteArray keyPrefix("PrivateKey="); + const QByteArray sidPrefix("ServiceID="); + + if(data.startsWith("ServiceID=")){ + QByteArray service_id = data.mid(sidPrefix.size()); + m_service->setServiceId(service_id); + } + if (data.startsWith(keyPrefix)) { - QByteArray keyData(QByteArray::fromBase64(data.mid(keyPrefix.size()))); + + QByteArray keyData(data.mid(keyPrefix.size())); CryptoKey key; - if (!key.loadFromData(keyData, CryptoKey::PrivateKey, CryptoKey::DER)) { - m_errorMessage = QStringLiteral("Key decoding failed"); + + if (!key.loadFromTorMessage(keyData)) { + m_errorMessage = QStringLiteral("Key structure check failed"); return; } diff --git a/retroshare-gui/src/TorControl/CryptoKey.cpp b/retroshare-gui/src/TorControl/CryptoKey.cpp index 8aa035754..9be9a6699 100644 --- a/retroshare-gui/src/TorControl/CryptoKey.cpp +++ b/retroshare-gui/src/TorControl/CryptoKey.cpp @@ -30,11 +30,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include + #include "CryptoKey.h" #include "SecureRNG.h" #include "Useful.h" #include #include +#include #include #include #include @@ -48,8 +51,10 @@ void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) #define RSA_bits(o) (BN_num_bits((o)->n)) #endif +#ifdef TO_REMOVE void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen); bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen); +#endif CryptoKey::CryptoKey() { @@ -60,6 +65,7 @@ CryptoKey::~CryptoKey() clear(); } +#ifdef TO_REMOVE CryptoKey::Data::~Data() { if (key) @@ -68,12 +74,14 @@ CryptoKey::Data::~Data() key = 0; } } +#endif void CryptoKey::clear() { - d = 0; + key_data.clear(); } +#ifdef TO_REMOVE bool CryptoKey::loadFromData(const QByteArray &data, KeyType type, KeyFormat format) { RSA *key = NULL; @@ -110,23 +118,91 @@ bool CryptoKey::loadFromData(const QByteArray &data, KeyType type, KeyFormat for d = new Data(key); return true; } +#endif -bool CryptoKey::loadFromFile(const QString &path, KeyType type, KeyFormat format) +bool CryptoKey::loadFromFile(const QString& path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { - qWarning() << "Failed to open" << (type == PrivateKey ? "private" : "public") << "key from" - << path << "-" << file.errorString(); + qWarning() << "Failed to open Tor key file " << path << ": " << file.errorString(); return false; } QByteArray data = file.readAll(); file.close(); - return loadFromData(data, type, format); + if(data.contains("-----BEGIN RSA PRIVATE KEY-----")) + { + std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl; + + // This to be compliant with old format. New format is oblivious to the type of key so we dont need a header + data = data.replace("-----BEGIN RSA PRIVATE KEY-----",nullptr); + data = data.replace("-----END RSA PRIVATE KEY-----",nullptr); + data = data.replace("\n",nullptr); + data = data.replace("\t",nullptr); + + data = "RSA1024:"+data; + } + + std::cerr << "Have read the following key: " << std::endl; + std::cerr << QString(data).toStdString() << std::endl; + + key_data = data; + + return true; } +bool CryptoKey::loadFromTorMessage(const QByteArray& b) +{ + // note: We should probably check the structure a bit more, for security. + + std::cerr << "Loading new key:" << std::endl; + + if(b.startsWith("RSA1024")) + std::cerr << " type: RSA-1024 (Tor v2)" << std::endl; + else if(b.startsWith("ED25519-V3")) + std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl; + else if(b.indexOf(':')) + { + std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toStdString() << "\". Not accepted." << std::endl; + return false; + } + + key_data = b; + return true; +} + +/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */ +QByteArray torControlHashedPassword(const QByteArray &password) +{ + QByteArray salt = SecureRNG::random(8); + if (salt.isNull()) + return QByteArray(); + + int count = ((quint32)16 + (96 & 15)) << ((96 >> 4) + 6); + + SHA_CTX hash; + SHA1_Init(&hash); + + QByteArray tmp = salt + password; + while (count) + { + int c = qMin(count, tmp.size()); + SHA1_Update(&hash, reinterpret_cast(tmp.constData()), c); + count -= c; + } + + unsigned char md[20]; + SHA1_Final(md, &hash); + + /* 60 is the hex-encoded value of 96, which is a constant used by Tor's algorithm. */ + return QByteArray("16:") + salt.toHex().toUpper() + QByteArray("60") + + QByteArray::fromRawData(reinterpret_cast(md), 20).toHex().toUpper(); +} + + +#ifdef TO_REMOVE bool CryptoKey::isPrivate() const { if (!isLoaded()) { @@ -326,34 +402,6 @@ bool CryptoKey::verifySHA256(const QByteArray &digest, QByteArray signature) con return true; } -/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */ -QByteArray torControlHashedPassword(const QByteArray &password) -{ - QByteArray salt = SecureRNG::random(8); - if (salt.isNull()) - return QByteArray(); - - int count = ((quint32)16 + (96 & 15)) << ((96 >> 4) + 6); - - SHA_CTX hash; - SHA1_Init(&hash); - - QByteArray tmp = salt + password; - while (count) - { - int c = qMin(count, tmp.size()); - SHA1_Update(&hash, reinterpret_cast(tmp.constData()), c); - count -= c; - } - - unsigned char md[20]; - SHA1_Final(md, &hash); - - /* 60 is the hex-encoded value of 96, which is a constant used by Tor's algorithm. */ - return QByteArray("16:") + salt.toHex().toUpper() + QByteArray("60") + - QByteArray::fromRawData(reinterpret_cast(md), 20).toHex().toUpper(); -} - /* Copyright (c) 2001-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson * Copyright (c) 2007-2010, The Tor Project, Inc. @@ -475,3 +523,5 @@ bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srcle delete[] tmp; return true; } + +#endif diff --git a/retroshare-gui/src/TorControl/CryptoKey.h b/retroshare-gui/src/TorControl/CryptoKey.h index 70ada1977..c99703444 100644 --- a/retroshare-gui/src/TorControl/CryptoKey.h +++ b/retroshare-gui/src/TorControl/CryptoKey.h @@ -51,14 +51,19 @@ public: }; CryptoKey(); - CryptoKey(const CryptoKey &other) : d(other.d) { } ~CryptoKey(); +#ifdef TO_REMOVE bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM); bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM); +#endif + bool loadFromFile(const QString &path); void clear(); - bool isLoaded() const { return d.data() && d->key != 0; } + const QByteArray bytes() const { return key_data; } + bool loadFromTorMessage(const QByteArray& b); + bool isLoaded() const { return !key_data.isNull(); } +#ifdef TO_REMOVE bool isPrivate() const; QByteArray publicKeyDigest() const; @@ -76,8 +81,10 @@ public: QByteArray signSHA256(const QByteArray &digest) const; // Verify a signature as per signSHA256 bool verifySHA256(const QByteArray &digest, QByteArray signature) const; +#endif private: +#ifdef TO_REMOVE struct Data : public QSharedData { typedef struct rsa_st RSA; @@ -86,8 +93,12 @@ private: Data(RSA *k = 0) : key(k) { } ~Data(); }; +#endif + QByteArray key_data; +#ifdef TO_REMOVE QExplicitlySharedDataPointer d; +#endif }; QByteArray torControlHashedPassword(const QByteArray &password); diff --git a/retroshare-gui/src/TorControl/HiddenService.cpp b/retroshare-gui/src/TorControl/HiddenService.cpp index 1dd1071c8..b2b58626a 100644 --- a/retroshare-gui/src/TorControl/HiddenService.cpp +++ b/retroshare-gui/src/TorControl/HiddenService.cpp @@ -90,6 +90,13 @@ void HiddenService::addTarget(quint16 servicePort, QHostAddress targetAddress, q m_targets.append(t); } +void HiddenService::setServiceId(const QByteArray& sid) +{ + m_service_id = sid; + m_hostname = sid + ".onion"; + + emit hostnameChanged(); +} void HiddenService::setPrivateKey(const CryptoKey &key) { if (m_privateKey.isLoaded()) { @@ -97,13 +104,15 @@ void HiddenService::setPrivateKey(const CryptoKey &key) return; } +#ifdef TO_REMOVE if (!key.isPrivate()) { BUG() << "Cannot create a hidden service with a public key"; return; } +#endif m_privateKey = key; - m_hostname = m_privateKey.torServiceID() + QStringLiteral(".onion"); + emit privateKeyChanged(); } @@ -112,13 +121,13 @@ void HiddenService::loadPrivateKey() if (m_privateKey.isLoaded() || m_dataPath.isEmpty()) return; - bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"), CryptoKey::PrivateKey); + bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key")); + if (!ok) { qWarning() << "Failed to load hidden service key"; return; } - m_hostname = m_privateKey.torServiceID(); emit privateKeyChanged(); } diff --git a/retroshare-gui/src/TorControl/HiddenService.h b/retroshare-gui/src/TorControl/HiddenService.h index 71c20a972..20fa1d851 100644 --- a/retroshare-gui/src/TorControl/HiddenService.h +++ b/retroshare-gui/src/TorControl/HiddenService.h @@ -70,11 +70,13 @@ public: Status status() const { return m_status; } - const QString &hostname() const { return m_hostname; } - const QString &dataPath() const { return m_dataPath; } + const QString& hostname() const { return m_hostname; } + const QString serviceId() const { return QString(m_service_id); } + const QString& dataPath() const { return m_dataPath; } CryptoKey privateKey() { return m_privateKey; } void setPrivateKey(const CryptoKey &privateKey); + void setServiceId(const QByteArray& sid); const QList &targets() const { return m_targets; } void addTarget(const Target &target); @@ -84,6 +86,7 @@ signals: void statusChanged(int newStatus, int oldStatus); void serviceOnline(); void privateKeyChanged(); + void hostnameChanged(); private slots: void servicePublished(); @@ -94,6 +97,7 @@ private: QString m_hostname; Status m_status; CryptoKey m_privateKey; + QByteArray m_service_id; void loadPrivateKey(); void setStatus(Status newStatus); diff --git a/retroshare-gui/src/TorControl/TorManager.cpp b/retroshare-gui/src/TorControl/TorManager.cpp index 2bda04647..221a55935 100644 --- a/retroshare-gui/src/TorControl/TorManager.cpp +++ b/retroshare-gui/src/TorControl/TorManager.cpp @@ -174,17 +174,16 @@ bool TorManager::setupHiddenService() std::cerr << "Attempting to load key from legacy filesystem format in " << legacyDir.toStdString() << std::endl; CryptoKey key; - if (!key.loadFromFile(legacyDir + QLatin1String("/private_key"), CryptoKey::PrivateKey)) + if (!key.loadFromFile(legacyDir + QLatin1String("/private_key"))) { qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion"; return false; } - keyData = QString::fromLatin1(key.encodedPrivateKey(CryptoKey::DER).toBase64()); d->hiddenService = new Tor::HiddenService(key, legacyDir, this); std::cerr << "Got key from legacy dir: " << std::endl; - std::cerr << keyData.toStdString() << std::endl; + std::cerr << key.bytes().toStdString() << std::endl; } else { @@ -193,6 +192,7 @@ bool TorManager::setupHiddenService() std::cerr << "Creating new hidden service." << std::endl; connect(d->hiddenService, SIGNAL(privateKeyChanged()), this, SLOT(hiddenServicePrivateKeyChanged())) ; + connect(d->hiddenService, SIGNAL(hostnameChanged()), this, SLOT(hiddenServiceHostnameChanged())) ; } Q_ASSERT(d->hiddenService); @@ -230,31 +230,40 @@ void TorManager::hiddenServiceStatusChanged(int old_status,int new_status) void TorManager::hiddenServicePrivateKeyChanged() { - QString key = QString::fromLatin1(d->hiddenService->privateKey().encodedPrivateKey(CryptoKey::DER).toBase64()); + QString key = QString::fromLatin1(d->hiddenService->privateKey().bytes()); QFile outfile(d->hiddenServiceDir + QLatin1String("/private_key")) ; outfile.open( QIODevice::WriteOnly | QIODevice::Text ); QTextStream s(&outfile); +#ifdef TO_REMOVE s << "-----BEGIN RSA PRIVATE KEY-----" << endl; - for(uint32_t i=0;ihiddenServiceDir + QLatin1String("/hostname")) ; +void TorManager::hiddenServiceHostnameChanged() +{ + QFile outfile2(d->hiddenServiceDir + QLatin1String("/hostname")) ; outfile2.open( QIODevice::WriteOnly | QIODevice::Text ); QTextStream t(&outfile2); - t << d->hiddenService->hostname() << endl; + QString hostname(d->hiddenService->hostname()); - outfile2.close(); + t << hostname << endl; + outfile2.close(); + + std::cerr << "Hidden service hostname changed: " << hostname.toStdString() << std::endl; } bool TorManager::configurationNeeded() const @@ -381,7 +390,7 @@ bool TorManager::getHiddenServiceInfo(QString& service_id,QString& service_onion for(auto it(hidden_services.begin());it!=hidden_services.end();++it) { service_onion_address = (*it)->hostname(); - service_id = (*it)->privateKey().torServiceID(); + service_id = (*it)->serviceId(); for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2) { diff --git a/retroshare-gui/src/TorControl/TorManager.h b/retroshare-gui/src/TorControl/TorManager.h index 3db1036d3..e9acc1208 100644 --- a/retroshare-gui/src/TorControl/TorManager.h +++ b/retroshare-gui/src/TorControl/TorManager.h @@ -93,7 +93,8 @@ public slots: private slots: void hiddenServicePrivateKeyChanged(); - void hiddenServiceStatusChanged(int old_status,int new_status); + void hiddenServiceHostnameChanged(); + void hiddenServiceStatusChanged(int old_status,int new_status); signals: void configurationNeededChanged(); diff --git a/retroshare-gui/src/gui/HomePage.cpp b/retroshare-gui/src/gui/HomePage.cpp index 0cfacb041..f9f77f9a9 100644 --- a/retroshare-gui/src/gui/HomePage.cpp +++ b/retroshare-gui/src/gui/HomePage.cpp @@ -189,8 +189,6 @@ void HomePage::updateOwnCert() void HomePage::updateOwnId() { - bool include_extra_locators = mIncludeAllIPs; - RsPeerDetails detail; if (!rsPeers->getPeerDetails(rsPeers->getOwnId(), detail)) @@ -199,11 +197,30 @@ void HomePage::updateOwnId() return ; } - std::string invite ; + bool include_extra_locators = mIncludeAllIPs || detail.isHiddenNode; + std::string invite ; - rsPeers->getShortInvite(invite,rsPeers->getOwnId(),true,!mIncludeAllIPs); + rsPeers->getShortInvite(invite,rsPeers->getOwnId(),true,!include_extra_locators); - ui->retroshareid->setText(QString::fromUtf8(invite.c_str())); +#ifdef TODO + QString S; + QString txt; + int i=0; + + for(uint32_t i=0;iretroshareid->width()) + S += invite[i++]; + else + { + txt += S + "\n"; + S.clear(); + } + + txt += S; + + ui->retroshareid->setText(txt); +#endif + ui->retroshareid->setText(QString::fromUtf8(invite.c_str())); } static void sendMail(QString sAddress, QString sSubject, QString sBody) { diff --git a/retroshare-gui/src/gui/HomePage.ui b/retroshare-gui/src/gui/HomePage.ui index 3e303b4e9..8079f3cac 100644 --- a/retroshare-gui/src/gui/HomePage.ui +++ b/retroshare-gui/src/gui/HomePage.ui @@ -174,7 +174,7 @@ private and secure decentralized communication platform. ... - + :/icons/help_64.png:/icons/help_64.png @@ -186,22 +186,16 @@ private and secure decentralized communication platform. - - - - 0 - 0 - - - - Qt::NoFocus - + - <html><head/><body><p>Share your RetroShare ID</p></body></html> + Show full certificate (old format) + + + ... - :/icons/svg/share.svg:/icons/svg/share.svg + :/icons/png/cert.png:/icons/png/cert.png @@ -209,8 +203,8 @@ private and secure decentralized communication platform. 24 - - QToolButton::InstantPopup + + true true @@ -218,7 +212,7 @@ private and secure decentralized communication platform. - + 10 @@ -232,12 +226,36 @@ private and secure decentralized communication platform. + + false + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - + + + + + 0 + 0 + + + + + 11 + + + + This is your Retroshare ID. Copy and share with your friends! + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + @@ -273,38 +291,23 @@ private and secure decentralized communication platform. - - + + - + 0 0 - - - 11 - + + Qt::NoFocus - - This is your Retroshare ID. Copy and share with your friends! - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - Show full certificate (old format) - - - ... + <html><head/><body><p>Share your RetroShare ID</p></body></html> - :/icons/png/cert.png:/icons/png/cert.png + :/icons/svg/share.svg:/icons/svg/share.svg @@ -312,8 +315,8 @@ private and secure decentralized communication platform. 24 - - true + + QToolButton::InstantPopup true @@ -337,9 +340,17 @@ private and secure decentralized communication platform. + + + ElidedLabel + QLabel +
gui/common/ElidedLabel.h
+ 1 +
+
- + diff --git a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp index a74740712..59871bde2 100644 --- a/retroshare-gui/src/gui/connect/ConfCertDialog.cpp +++ b/retroshare-gui/src/gui/connect/ConfCertDialog.cpp @@ -278,7 +278,7 @@ void ConfCertDialog::loadInvitePage() if(ui._shortFormat_CB->isChecked()) { - rsPeers->getShortInvite(invite,detail.id,true,!ui._includeIPHistory_CB->isChecked() ); + rsPeers->getShortInvite(invite,detail.id,true,!(ui._includeIPHistory_CB->isChecked()|| detail.isHiddenNode) ); ui.stabWidget->setTabText(1, tr("Retroshare ID")); } else