From 4dcf76ced02e55047cab035fc16a588b3d01dce1 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Wed, 29 Apr 2026 09:52:13 +0200 Subject: [PATCH] Fix GXS identity validation for pre-0.6.8 identities by reconstructing legacy mServiceString used at signing time --- src/gxs/gxssecurity.cc | 95 ++++++++++++++++++++++++++++++++++++++++++ src/gxs/rsgxsdata.cc | 8 ++-- src/gxs/rsgxsdata.h | 1 + 3 files changed, 100 insertions(+), 4 deletions(-) diff --git a/src/gxs/gxssecurity.cc b/src/gxs/gxssecurity.cc index 4874eeeb96..1b6e883a8b 100644 --- a/src/gxs/gxssecurity.cc +++ b/src/gxs/gxssecurity.cc @@ -1166,6 +1166,101 @@ bool GxsSecurity::validateNxsGrp(const RsNxsGrp& grp, const RsTlvKeySignature& s return true; } + // Legacy fallback: try to verify the signature using the mServiceString + // that was present at group creation time. Before commit c5135a210, + // mServiceString was included in the signed payload. We reconstruct it + // and retry verification to maintain backward compatibility with 0.6.7. + + std::cerr << "(WW) GxsSecurity::validateNxsGrp() Standard verification failed for group " + << grpMeta.mGroupId << ". mServiceString=\"" << grpMeta.mServiceString + << "\" (len=" << grpMeta.mServiceString.size() << ")" << std::endl; + + if (!grpMeta.mServiceString.empty()) + { + // Build a list of legacy mServiceString candidates to try. + // At creation time, p3idservice sets mServiceString = ssdata.save() + // which produces a deterministic string based on the identity type. + + std::vector legacyCandidates; + + // Candidate 1: the received mServiceString itself (works if it + // hasn't been modified by intermediate nodes since last signing) + legacyCandidates.push_back(grpMeta.mServiceString); + + // Candidate 2: creation-time string for anonymous identity + legacyCandidates.push_back("v2 {P:K:0 T:0 C:0}{T:F:0 P:0 T:0}{R:5 5 0 0}"); + + // Candidate 3: creation-time string for PGP-linked identity + // Extract PGP ID from the received mServiceString if present + std::string pgpId; + std::string::size_type ipos = grpMeta.mServiceString.find("I:"); + if (ipos != std::string::npos) + { + ipos += 2; // skip "I:" + std::string::size_type end = grpMeta.mServiceString.find_first_of(" }", ipos); + if (end != std::string::npos) + pgpId = grpMeta.mServiceString.substr(ipos, end - ipos); + else + pgpId = grpMeta.mServiceString.substr(ipos); + + if (!pgpId.empty()) + legacyCandidates.push_back("v2 {P:K:1 I:" + pgpId + "}{T:F:0 P:0 T:0}{R:5 5 0 0}"); + } + + // Save original state and prepare for legacy verification + std::string savedServiceString = grpMeta.mServiceString; + grpMeta.signSet.TlvClear(); + + const unsigned char *keyptr2 = (const unsigned char *) key.keyData.bin_data; + RSA *rsakey2 = d2i_RSAPublicKey(NULL, &(keyptr2), keylen); + + if (rsakey2) + { + EVP_PKEY *signKey2 = EVP_PKEY_new(); + EVP_PKEY_assign_RSA(signKey2, rsakey2); + + for (uint32_t c = 0; c < legacyCandidates.size() && signOk != 1; ++c) + { + grpMeta.mServiceString = legacyCandidates[c]; + grpMeta.mUseLegacyServiceString = true; + + for (uint32_t i = 0; i < api_versions_to_check.size() && signOk != 1; ++i) + { + uint32_t metaDataLen = grpMeta.serial_size(api_versions_to_check[i]); + uint32_t allGrpDataLen = metaDataLen + grp.grp.bin_len; + + RsTemporaryMemory metaData(metaDataLen); + RsTemporaryMemory allGrpData(allGrpDataLen); + + grpMeta.serialise(metaData, metaDataLen, api_versions_to_check[i]); + + memcpy(allGrpData, grp.grp.bin_data, grp.grp.bin_len); + memcpy(allGrpData + grp.grp.bin_len, metaData, metaDataLen); + + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + EVP_VerifyInit(mdctx, EVP_sha1()); + EVP_VerifyUpdate(mdctx, allGrpData, allGrpDataLen); + signOk = EVP_VerifyFinal(mdctx, sigbuf, siglen, signKey2); + EVP_MD_CTX_destroy(mdctx); + } + } + + EVP_PKEY_free(signKey2); + } + + // Restore original state + grpMeta.mServiceString = savedServiceString; + grpMeta.mUseLegacyServiceString = false; + grpMeta.signSet = signSet; + + if (signOk == 1) + { + std::cerr << "(II) GxsSecurity::validateNxsGrp() Legacy signature verified for group " + << grpMeta.mGroupId << std::endl; + return true; + } + } + #ifdef GXS_SECURITY_DEBUG std::cerr << "GxsSecurity::validateNxsGrp() Signature invalid"; std::cerr << std::endl; diff --git a/src/gxs/rsgxsdata.cc b/src/gxs/rsgxsdata.cc index f75c5f9f7f..c72d83568b 100644 --- a/src/gxs/rsgxsdata.cc +++ b/src/gxs/rsgxsdata.cc @@ -42,7 +42,7 @@ uint32_t RsGxsGrpMetaData::serial_size(uint32_t api_version) const s += 4; // mCircleType s += 4; // mAuthenFlag s += mAuthorId.serial_size(); - s += GetTlvStringSize(""); + s += GetTlvStringSize(mUseLegacyServiceString ? mServiceString : ""); s += mCircleId.serial_size(); s += signSet.TlvSize(); s += keys.TlvSize(); @@ -72,6 +72,7 @@ void RsGxsGrpMetaData::clear(){ keys.TlvClear(); mServiceString.clear(); + mUseLegacyServiceString = false; mAuthenFlags = 0; mParentGrpId.clear(); @@ -124,7 +125,7 @@ bool RsGxsGrpMetaData::serialise(void *data, uint32_t &pktsize,uint32_t api_vers ok &= setRawUInt32(data, tlvsize, &offset, mCircleType); ok &= setRawUInt32(data, tlvsize, &offset, mAuthenFlags); ok &= mAuthorId.serialise(data, tlvsize, offset); - ok &= SetTlvString(data, tlvsize, &offset, 0, ""); + ok &= SetTlvString(data, tlvsize, &offset, 0, mUseLegacyServiceString ? mServiceString : std::string()); ok &= mCircleId.serialise(data, tlvsize, offset); ok &= signSet.SetTlv(data, tlvsize, &offset); @@ -159,8 +160,7 @@ bool RsGxsGrpMetaData::deserialise(void *data, uint32_t &pktsize) ok &= mAuthorId.deserialise(data, pktsize, offset); - std::string temp; - ok &= GetTlvString(data, pktsize, &offset, 0, temp); + ok &= GetTlvString(data, pktsize, &offset, 0, mServiceString); ok &= mCircleId.deserialise(data, pktsize, offset); ok &= signSet.GetTlv(data, pktsize, &offset); ok &= keys.GetTlv(data, pktsize, &offset); diff --git a/src/gxs/rsgxsdata.h b/src/gxs/rsgxsdata.h index acc0c71d83..858fa89853 100644 --- a/src/gxs/rsgxsdata.h +++ b/src/gxs/rsgxsdata.h @@ -70,6 +70,7 @@ class RsGxsGrpMetaData uint32_t mSignFlags; // BELOW HERE IS LOCAL DATA, THAT IS NOT FROM MSG. + bool mUseLegacyServiceString; // Temporary flag for legacy signature verification uint32_t mSubscribeFlags;