123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- /*
- This file is part of cpp-ethereum.
- cpp-ethereum is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- cpp-ethereum is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
- */
- /** @file Message.cpp
- * @author Gav Wood <i@gavwood.com>
- * @date 2014
- */
- #include "Message.h"
- #include "BloomFilter.h"
- using namespace std;
- using namespace dev;
- using namespace dev::p2p;
- using namespace dev::shh;
- Message::Message(Envelope const& _e, Topics const& _t, Secret const& _s)
- {
- try
- {
- bytes b;
- if (_s)
- if (!decrypt(_s, &(_e.data()), b))
- return;
- else{}
- else if (!openBroadcastEnvelope(_e, _t, b))
- return;
- if (populate(b))
- if (_s)
- m_to = KeyPair(_s).pub();
- }
- catch (...) // Invalid secret? TODO: replace ... with InvalidSecret
- {
- }
- }
- bool Message::openBroadcastEnvelope(Envelope const& _e, Topics const& _fk, bytes& o_b)
- {
- // retrieve the key using the known topic and topicIndex.
- unsigned topicIndex = 0;
- Secret topicSecret;
- // determine topicSecret/topicIndex from knowledge of the collapsed topics (which give the order) and our full-size filter topic.
- AbridgedTopics knownTopic = abridge(_fk);
- for (unsigned ti = 0; ti < _fk.size() && !topicSecret; ++ti)
- for (unsigned i = 0; i < _e.topic().size(); ++i)
- if (_e.topic()[i] == knownTopic[ti])
- {
- topicSecret = Secret(_fk[ti]);
- topicIndex = i;
- break;
- }
- if (_e.data().size() < _e.topic().size() * h256::size)
- return false;
- unsigned index = topicIndex * 2;
- Secret encryptedKey(bytesConstRef(&(_e.data())).cropped(h256::size * index, h256::size));
- h256 salt = h256(bytesConstRef(&(_e.data())).cropped(h256::size * ++index, h256::size));
- Secret key = Secret(generateGamma(topicSecret, salt).makeInsecure() ^ encryptedKey.makeInsecure());
- bytesConstRef cipherText = bytesConstRef(&(_e.data())).cropped(h256::size * 2 * _e.topic().size());
- return decryptSym(key, cipherText, o_b);
- }
- bool Message::populate(bytes const& _data)
- {
- if (!_data.size())
- return false;
- byte flags = _data[0];
- if (!!(flags & ContainsSignature) && _data.size() >= sizeof(Signature) + 1) // has a signature
- {
- bytesConstRef payload = bytesConstRef(&_data).cropped(1, _data.size() - sizeof(Signature) - 1);
- h256 h = sha3(payload);
- Signature const& sig = *(Signature const*)&(_data[1 + payload.size()]);
- m_from = recover(sig, h);
- if (!m_from)
- return false;
- m_payload = payload.toBytes();
- }
- else
- m_payload = bytesConstRef(&_data).cropped(1).toBytes();
- return true;
- }
- Envelope Message::seal(Secret const& _from, Topics const& _fullTopics, unsigned _ttl, unsigned _workToProve) const
- {
- AbridgedTopics topics = abridge(_fullTopics);
- Envelope ret(utcTime() + _ttl, _ttl, topics);
- bytes input(1 + m_payload.size());
- input[0] = 0;
- memcpy(input.data() + 1, m_payload.data(), m_payload.size());
- if (_from) // needs a signature
- {
- input.resize(1 + m_payload.size() + sizeof(Signature));
- input[0] |= ContainsSignature;
- *(Signature*)&(input[1 + m_payload.size()]) = sign(_from, sha3(m_payload));
- // If this fails, the something is wrong with the sign-recover round-trip.
- assert(recover(*(Signature*)&(input[1 + m_payload.size()]), sha3(m_payload)) == KeyPair(_from).pub());
- }
- if (m_to)
- encrypt(m_to, &input, ret.m_data);
- else
- {
- // this message is for broadcast (could be read by anyone who knows at least one of the topics)
- // create the shared secret for encrypting the payload, then encrypt the shared secret with each topic
- Secret s = Secret::random();
- for (h256 const& t: _fullTopics)
- {
- h256 salt = h256::random();
- ret.m_data += (generateGamma(Secret(t), salt).makeInsecure() ^ s.makeInsecure()).ref().toBytes();
- ret.m_data += salt.asBytes();
- }
- bytes d;
- encryptSym(s, &input, d);
- ret.m_data += d;
- }
- ret.proveWork(_workToProve);
- return ret;
- }
- Envelope::Envelope(RLP const& _m)
- {
- m_expiry = _m[0].toInt<unsigned>();
- m_ttl = _m[1].toInt<unsigned>();
- m_topic = _m[2].toVector<FixedHash<4>>();
- m_data = _m[3].toBytes();
- m_nonce = _m[4].toInt<u256>();
- }
- Message Envelope::open(Topics const& _t, Secret const& _s) const
- {
- return Message(*this, _t, _s);
- }
- unsigned Envelope::workProved() const
- {
- h256 d[2];
- d[0] = sha3(WithoutNonce);
- d[1] = m_nonce;
- return dev::sha3(bytesConstRef(d[0].data(), 64)).firstBitSet();
- }
- void Envelope::proveWork(unsigned _ms)
- {
- h256 d[2];
- d[0] = sha3(WithoutNonce);
- unsigned bestBitSet = 0;
- bytesConstRef chuck(d[0].data(), 64);
- chrono::high_resolution_clock::time_point then = chrono::high_resolution_clock::now() + chrono::milliseconds(_ms);
- while (chrono::high_resolution_clock::now() < then)
- // do it rounds of 1024 for efficiency
- for (unsigned i = 0; i < 1024; ++i, ++d[1])
- {
- auto fbs = dev::sha3(chuck).firstBitSet();
- if (fbs > bestBitSet)
- {
- bestBitSet = fbs;
- m_nonce = (h256::Arith)d[1];
- }
- }
- }
- bool Envelope::matchesBloomFilter(TopicBloomFilterHash const& f) const
- {
- for (AbridgedTopic t: m_topic)
- if (f.contains(TopicBloomFilter::bloom(t)))
- return true;
- return false;
- }
|