From 93ad67c240d3d1071d2c7ec3f756825965088e26 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 25 Jun 2012 09:13:18 -0700 Subject: [PATCH] Close SHAMap node security hole Use new hash prefixes. Use new wire/prefix formats. Remove SHAMapException --- src/SHAMap.cpp | 56 +++++------ src/SHAMap.h | 16 ++-- src/SHAMapNodes.cpp | 229 +++++++++++++++++++++++++++++--------------- src/SHAMapSync.cpp | 17 ++-- src/Version.h | 6 +- 5 files changed, 200 insertions(+), 124 deletions(-) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index ea592d60a5..0da1f50e85 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -79,7 +79,7 @@ std::stack SHAMap::getStack(const uint256& id, bool inc if (!node) { if (isSynching()) return stack; - throw SHAMapException(MissingNode); + throw std::runtime_error("missing node"); } } @@ -139,7 +139,7 @@ SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify) uint256 childHash = inNode->getChildHash(branch); SHAMapTreeNode::pointer nextNode = getNode(inNode->getChildNodeID(branch), childHash, false); - if (!nextNode) throw SHAMapException(MissingNode); + if (!nextNode) throw std::runtime_error("missing node"); inNode = nextNode; } if (inNode->getTag() != id) return SHAMapTreeNode::pointer(); @@ -156,7 +156,7 @@ SHAMapTreeNode* SHAMap::walkToPointer(const uint256& id) const uint256& nextHash = inNode->getChildHash(branch); if (!nextHash) return NULL; inNode = getNodePointer(inNode->getChildNodeID(branch), nextHash); - if (!inNode) throw SHAMapException(MissingNode); + if (!inNode) throw std::runtime_error("missing node"); } return (inNode->getTag() == id) ? inNode : NULL; } @@ -175,17 +175,17 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& has std::cerr << "NodHash " << node->getNodeHash().GetHex() << std::endl; dump(); #endif - throw SHAMapException(InvalidNode); + throw std::runtime_error("invalid node"); } returnNode(node, modify); return node; } - std::vector nodeData; - if (!fetchNode(hash, nodeData)) return SHAMapTreeNode::pointer(); + node = fetchNode(id, hash); + if (!node) return node; - node = boost::make_shared(id, nodeData, mSeq); - if (node->getNodeHash() != hash) throw SHAMapException(InvalidNode); + if (node->getNodeHash() != hash) + throw std::runtime_error("invalid node hash"); if (!mTNByID.insert(std::make_pair(id, node)).second) assert(false); @@ -198,12 +198,11 @@ SHAMapTreeNode* SHAMap::getNodePointer(const SHAMapNode& id, const uint256& hash if (it != mTNByID.end()) return &*it->second; - SHAMapTreeNode::pointer node; - std::vector nodeData; - if (!fetchNode(hash, nodeData)) return NULL; + SHAMapTreeNode::pointer node = fetchNode(id, hash); + if (!node) return NULL; - node = boost::make_shared(id, nodeData, mSeq); - if (node->getNodeHash() != hash) throw SHAMapException(InvalidNode); + if (node->getNodeHash() != hash) + throw std::runtime_error("invalid node fetched"); if (!mTNByID.insert(std::make_pair(id, node)).second) assert(false); @@ -367,9 +366,9 @@ SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id) if(!node->isEmptyBranch(i)) { node = getNode(node->getChildNodeID(i), node->getChildHash(i), false); - if (!node) throw SHAMapException(MissingNode); + if (!node) throw std::runtime_error("missing node"); SHAMapItem::pointer item = firstBelow(&*node); - if (!item) throw SHAMapException(MissingNode); + if (!item) throw std::runtime_error("missing node"); return item; } } @@ -396,9 +395,9 @@ SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id) if(!node->isEmptyBranch(i)) { node = getNode(node->getChildNodeID(i), node->getChildHash(i), false); - if(!node) throw SHAMapException(MissingNode); + if(!node) throw std::runtime_error("missing node"); SHAMapItem::pointer item = firstBelow(&*node); - if (!item) throw SHAMapException(MissingNode); + if (!item) throw std::runtime_error("missing node"); return item; } } @@ -428,7 +427,7 @@ bool SHAMap::delItem(const uint256& id) assert(mState != Immutable); std::stack stack=getStack(id, true); - if(stack.empty()) throw SHAMapException(MissingNode); + if(stack.empty()) throw std::runtime_error("missing node"); SHAMapTreeNode::pointer leaf=stack.top(); stack.pop(); @@ -503,7 +502,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) assert(mState != Immutable); std::stack stack = getStack(tag, true); - if (stack.empty()) throw SHAMapException(MissingNode); + if (stack.empty()) throw std::runtime_error("missing node"); SHAMapTreeNode::pointer node = stack.top(); stack.pop(); @@ -529,7 +528,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) std::cerr << "NewNode: " << newNode->getString() << std::endl; dump(); assert(false); - throw SHAMapException(InvalidNode); + throw std::runtime_error("invalid inner node"); } node->setChildHash(branch, newNode->getNodeHash()); } @@ -594,7 +593,7 @@ bool SHAMap::updateGiveItem(SHAMapItem::pointer item, bool isTransaction) assert(mState != Immutable); std::stack stack = getStack(tag, true); - if (stack.empty()) throw SHAMapException(MissingNode); + if (stack.empty()) throw std::runtime_error("missing node"); SHAMapTreeNode::pointer node = stack.top(); stack.pop(); @@ -621,12 +620,15 @@ void SHAMapItem::dump() std::cerr << "SHAMapItem(" << mTag.GetHex() << ") " << mData.size() << "bytes" << std::endl; } -bool SHAMap::fetchNode(const uint256& hash, std::vector& data) +SHAMapTreeNode::pointer SHAMap::fetchNode(const SHAMapNode& id, const uint256& hash) { + if (!theApp->running()) return SHAMapTreeNode::pointer(); + HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash)); - if(!obj) return false; - data = obj->getData(); - return true; + if(!obj) return SHAMapTreeNode::pointer(); + assert(Serializer::getSHA512Half(obj->getData()) == hash); + + return boost::make_shared(id, obj->getData(), mSeq, STN_ARF_PREFIXED); } void SHAMap::armDirty() @@ -647,7 +649,7 @@ int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq) while (it != dirtyNodes.end()) { s.erase(); - it->second->addRaw(s); + it->second->addRaw(s, STN_ARF_PREFIXED); theApp->getHashedObjectStore().store(t, seq, s.peekData(), s.getSHA512Half()); if (flushed++ >= maxNodes) return flushed; @@ -679,7 +681,7 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& nodeID) return SHAMapTreeNode::pointer(); node = getNode(node->getChildNodeID(branch), node->getChildHash(branch), false); - if (!node) throw SHAMapException(MissingNode); + if (!node) throw std::runtime_error("missing node"); } return node; } diff --git a/src/SHAMap.h b/src/SHAMap.h index 8dda9db8d0..6c6770a122 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -158,8 +158,11 @@ public: SHAMapTreeNode(const SHAMapNode& nodeID, SHAMapItem::pointer item, TNType type, uint32 seq); // raw node functions - SHAMapTreeNode(const SHAMapNode& id, const std::vector& contents, uint32 seq); // raw node - void addRaw(Serializer &); + SHAMapTreeNode(const SHAMapNode& id, const std::vector& contents, uint32 seq, int format); + +#define STN_ARF_PREFIXED 1 +#define STN_ARF_WIRE 2 + void addRaw(Serializer &, int format); virtual bool isPopulated() const { return true; } @@ -205,13 +208,6 @@ public: virtual std::string getString() const; }; -enum SHAMapException -{ - MissingNode = 1, - InvalidNode = 2, - InvalidMap = 3, -}; - enum SHAMapState { Modifying = 0, // Objects can be added and removed (like an open ledger) @@ -337,7 +333,7 @@ public: uint32 getSeq() { return mSeq; } // overloads for backed maps - bool fetchNode(const uint256& hash, std::vector& rawNode); + boost::shared_ptr fetchNode(const SHAMapNode& id, const uint256& hash); bool operator==(const SHAMap& s) { return getHash() == s.getHash(); } diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index acd27eb271..dbdc8ee109 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -14,6 +14,7 @@ #include "Serializer.h" #include "BitcoinUtil.h" #include "Log.h" +#include "HashPrefixes.h" std::string SHAMapNode::getString() const { @@ -188,90 +189,99 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::pointer item, updateHash(); } -SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& rawNode, uint32 seq) +SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& rawNode, uint32 seq, int format) : SHAMapNode(id), mSeq(seq), mType(tnERROR), mFullBelow(false) { - Serializer s(rawNode); - - int type = s.removeLastByte(); - int len = s.getLength(); - if ((type < 0) || (type > 3)) throw SHAMapException(InvalidNode); - assert(len >= 33); - - if (type == 0) - { // transaction - mItem = boost::make_shared(s.getSHA512Half(), s.peekData()); - mType = tnTRANSACTION; - } - else if (type == 1) - { // account state - uint256 u; - s.get256(u, len - 32); - s.chop(256 / 8); - if (u.isZero()) throw SHAMapException(InvalidNode); - mItem = boost::make_shared(u, s.peekData()); - mType = tnACCOUNT_STATE; - } - else if (type == 2) - { // full inner - if (len != 512) throw SHAMapException(InvalidNode); - for (int i = 0; i < 16; ++i) - s.get256(mHashes[i], i * 32); - mType = tnINNER; - } - else if (type == 3) - { // compressed inner - for (int i = 0; i < (len / 33); ++i) + if (format == STN_ARF_WIRE) + { + Serializer s(rawNode); + int type = s.removeLastByte(); + int len = s.getLength(); + if ((type < 0) || (type > 3)) { - int pos; - s.get8(pos, 32 + (i * 33)); - if ((pos < 0) || (pos >= 16)) throw SHAMapException(InvalidNode); - s.get256(mHashes[pos], i * 33); +#ifdef DEBUG + std::cerr << "Invalid wire format node" << std::endl; + std::cerr << strHex(rawNode) << std::endl; + assert(false); +#endif + throw std::runtime_error("invalid node AW type"); } - mType = tnINNER; + + if (type == 0) + { // transaction + mItem = boost::make_shared(s.getPrefixHash(sHP_TransactionID), s.peekData()); + mType = tnTRANSACTION; + } + else if (type == 1) + { // account state + if (len < (256 / 8)) + throw std::runtime_error("short AS node"); + uint256 u; + s.get256(u, len - 32); + s.chop(256 / 8); + if (u.isZero()) throw std::runtime_error("invalid AS node"); + mItem = boost::make_shared(u, s.peekData()); + mType = tnACCOUNT_STATE; + } + else if (type == 2) + { // full inner + if (len != 512) + throw std::runtime_error("invalid FI node"); + for (int i = 0; i < 16; ++i) + s.get256(mHashes[i], i * 32); + mType = tnINNER; + } + else if (type == 3) + { // compressed inner + for (int i = 0; i < (len / 33); ++i) + { + int pos; + s.get8(pos, 32 + (i * 33)); + if ((pos < 0) || (pos >= 16)) throw std::runtime_error("invalid CI node"); + s.get256(mHashes[pos], i * 33); + } + mType = tnINNER; + } + } + + if (format == STN_ARF_PREFIXED) + { + if (rawNode.size() < 4) + throw std::runtime_error("invalid P node"); + + uint32 prefix = rawNode[0]; prefix <<= 8; prefix |= rawNode[1]; prefix <<= 8; + prefix |= rawNode[2]; prefix <<= 8; prefix |= rawNode[3]; + Serializer s(rawNode.begin() + 4, rawNode.end()); + + if (prefix == sHP_TransactionID) + { + mItem = boost::make_shared(Serializer::getSHA512Half(rawNode), s.peekData()); + mType = tnTRANSACTION; + } + if (prefix == sHP_LeafNode) + { + uint256 u; + s.get256(u, s.getLength() - 32); + s.chop(256 / 8); + if (u.isZero()) throw std::runtime_error("invalid PLN node"); + mItem = boost::make_shared(u, s.peekData()); + mType = tnACCOUNT_STATE; + } + if (prefix == sHP_InnerNode) + { + if (rawNode.size() != (512 + 4)) + throw std::runtime_error("invalid PIN node"); + for (int i = 0; i < 16; ++i) + s.get256(mHashes[i] , i * 32); + mType = tnINNER; + } + else + throw std::runtime_error("invalid node prefix"); } updateHash(); } -void SHAMapTreeNode::addRaw(Serializer &s) -{ - if (mType == tnERROR) throw SHAMapException(InvalidNode); - - if (mType == tnTRANSACTION) - { - mItem->addRaw(s); - s.add8(0); - assert(s.getLength() > 32); - return; - } - - if (mType == tnACCOUNT_STATE) - { - mItem->addRaw(s); - s.add256(mItem->getTag()); - s.add8(1); - return; - } - - if (getBranchCount() < 12) - { // compressed node - for (int i = 0; i < 16; ++i) - if (mHashes[i].isNonZero()) - { - s.add256(mHashes[i]); - s.add8(i); - } - s.add8(3); - return; - } - - for (int i = 0; i < 16; ++i) - s.add256(mHashes[i]); - - s.add8(2); -} - bool SHAMapTreeNode::updateHash() { uint256 nh; @@ -286,18 +296,19 @@ bool SHAMapTreeNode::updateHash() break; } if(!empty) - nh = Serializer::getSHA512Half(reinterpret_cast(mHashes), sizeof(mHashes)); + nh = Serializer::getPrefixHash(sHP_InnerNode, reinterpret_cast(mHashes), sizeof(mHashes)); } else if (mType == tnACCOUNT_STATE) { Serializer s; + s.add32(sHP_LeafNode); mItem->addRaw(s); s.add256(mItem->getTag()); nh = s.getSHA512Half(); } else if (mType == tnTRANSACTION) { - nh = Serializer::getSHA512Half(mItem->peekData()); + nh = Serializer::getPrefixHash(sHP_TransactionID, mItem->peekData()); } else assert(false); @@ -306,6 +317,70 @@ bool SHAMapTreeNode::updateHash() return true; } +void SHAMapTreeNode::addRaw(Serializer& s, int format) +{ + assert((format == STN_ARF_PREFIXED) || (format == STN_ARF_WIRE)); + if (mType == tnERROR) throw std::runtime_error("invalid I node type"); + + if (mType == tnINNER) + { + if (format == STN_ARF_PREFIXED) + { + s.add32(sHP_InnerNode); + for (int i = 0; i < 16; ++i) + s.add256(mHashes[i]); + } + else + { + if (getBranchCount() < 12) + { // compressed node + for (int i = 0; i < 16; ++i) + if (mHashes[i].isNonZero()) + { + s.add256(mHashes[i]); + s.add8(i); + } + s.add8(3); + } + else + { + for (int i = 0; i < 16; ++i) + s.add256(mHashes[i]); + s.add8(2); + } + } + } + else if (mType == tnACCOUNT_STATE) + { + if (format == STN_ARF_PREFIXED) + { + s.add32(sHP_LeafNode); + mItem->addRaw(s); + s.add256(mItem->getTag()); + } + else + { + mItem->addRaw(s); + s.add256(mItem->getTag()); + s.add8(1); + } + } + else if (mType == tnTRANSACTION) + { + if (format == STN_ARF_PREFIXED) + { + s.add32(sHP_TransactionID); + mItem->addRaw(s); + } + else + { + mItem->addRaw(s); + s.add8(0); + } + } + else assert(false); +} + bool SHAMapTreeNode::setItem(SHAMapItem::pointer& i, TNType type) { uint256 hash = getNodeHash(); diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index 089ca444b4..722c179b24 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -52,7 +52,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector nodeData; if (filter->haveNode(childID, childHash, nodeData)) { - d = boost::make_shared(childID, nodeData, mSeq); + d = boost::make_shared(childID, nodeData, mSeq, STN_ARF_WIRE); if (childHash != d->getNodeHash()) { Log(lsERROR) << "Wrong hash from cached object"; @@ -92,7 +92,7 @@ bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector& nodeI nodeIDs.push_back(*node); Serializer s; - node->addRaw(s); + node->addRaw(s, STN_ARF_WIRE); rawNodes.push_back(s.peekData()); if (node->isRoot() || node->isLeaf()) // don't get a fat root, can't get a fat leaf @@ -107,7 +107,7 @@ bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector& nodeI { nodeIDs.push_back(*nextNode); Serializer s; - nextNode->addRaw(s); + nextNode->addRaw(s, STN_ARF_WIRE); rawNodes.push_back(s.peekData()); } } @@ -126,7 +126,7 @@ bool SHAMap::addRootNode(const std::vector& rootNode) return true; } - SHAMapTreeNode::pointer node = boost::make_shared(SHAMapNode(), rootNode, 0); + SHAMapTreeNode::pointer node = boost::make_shared(SHAMapNode(), rootNode, 0, STN_ARF_WIRE); if (!node) return false; #ifdef DEBUG @@ -158,7 +158,7 @@ bool SHAMap::addRootNode(const uint256& hash, const std::vector& return true; } - SHAMapTreeNode::pointer node = boost::make_shared(SHAMapNode(), rootNode, 0); + SHAMapTreeNode::pointer node = boost::make_shared(SHAMapNode(), rootNode, 0, STN_ARF_WIRE); if (!node) return false; if (node->getNodeHash() != hash) return false; @@ -217,7 +217,7 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectorgetChildHash(branch); if (!hash) return false; - SHAMapTreeNode::pointer newNode = boost::make_shared(node, rawNode, mSeq); + SHAMapTreeNode::pointer newNode = boost::make_shared(node, rawNode, mSeq, STN_ARF_WIRE); if (hash != newNode->getNodeHash()) // these aren't the droids we're looking for return false; @@ -368,7 +368,7 @@ std::list > SHAMap::getTrustedPath(const uint256& ind Serializer s; while (!stack.empty()) { - stack.top()->addRaw(s); + stack.top()->addRaw(s, STN_ARF_WIRE); path.push_back(s.getData()); s.erase(); stack.pop(); @@ -380,13 +380,16 @@ BOOST_AUTO_TEST_SUITE( SHAMapSync ) BOOST_AUTO_TEST_CASE( SHAMapSync_test ) { + Log(lsTRACE) << "being sync test"; unsigned int seed; RAND_pseudo_bytes(reinterpret_cast(&seed), sizeof(seed)); srand(seed); + Log(lsTRACE) << "Constructing maps"; SHAMap source, destination; // add random data to the source map + Log(lsTRACE) << "Adding random data"; int items = 10000; for (int i = 0; i < items; ++i) source.addItem(*makeRandomAS(), false); diff --git a/src/Version.h b/src/Version.h index e9fe8bc151..1450ae1316 100644 --- a/src/Version.h +++ b/src/Version.h @@ -4,7 +4,7 @@ #ifndef SERVER_VERSION_MAJ #define SERVER_VERSION_MAJ 0 -#define SERVER_VERSION_MIN 1 +#define SERVER_VERSION_MIN 2 #define SERVER_VERSION_SUB "-a" #define SERVER_NAME "NewCoin" @@ -14,9 +14,9 @@ (SERVER_NAME "-" SV_STRINGIZE(SERVER_VERSION_MAJ) "." SV_STRINGIZE(SERVER_VERSION_MIN) SERVER_VERSION_SUB) #define PROTO_VERSION_MAJ 0 -#define PROTO_VERSION_MIN 0 +#define PROTO_VERSION_MIN 1 #define MIN_PROTO_MAJ 0 -#define MIN_PROTO_MIN 0 +#define MIN_PROTO_MIN 1 #endif