#include #include namespace llarp { namespace service { IntroSet::~IntroSet() { if(W) delete W; } util::StatusObject IntroSet::ExtractStatus() const { util::StatusObject obj{{"published", T}}; std::vector< util::StatusObject > introsObjs; std::transform(I.begin(), I.end(), std::back_inserter(introsObjs), [](const auto& intro) -> util::StatusObject { return intro.ExtractStatus(); }); obj.Put("intros", introsObjs); if(!topic.IsZero()) obj.Put("topic", topic.ToString()); return obj; } bool IntroSet::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) { bool read = false; if(!BEncodeMaybeReadDictEntry("a", A, read, key, buf)) return false; if(key == "i") { return BEncodeReadList(I, buf); } if(!BEncodeMaybeReadDictEntry("k", K, read, key, buf)) return false; if(!BEncodeMaybeReadDictEntry("n", topic, read, key, buf)) return false; if(!BEncodeMaybeReadDictInt("t", T, read, key, buf)) return false; if(key == "w") { if(W) delete W; W = new PoW(); return W->BDecode(buf); } if(!BEncodeMaybeReadDictInt("v", version, read, key, buf)) return false; if(!BEncodeMaybeReadDictEntry("z", Z, read, key, buf)) return false; return read; } bool IntroSet::BEncode(llarp_buffer_t* buf) const { if(!bencode_start_dict(buf)) return false; if(!BEncodeWriteDictEntry("a", A, buf)) return false; // start introduction list if(!bencode_write_bytestring(buf, "i", 1)) return false; if(!BEncodeWriteList(I.begin(), I.end(), buf)) return false; // end introduction list // pq pubkey if(!BEncodeWriteDictEntry("k", K, buf)) return false; // topic tag if(topic.ToString().size()) { if(!BEncodeWriteDictEntry("n", topic, buf)) return false; } // Timestamp published if(!BEncodeWriteDictInt("t", T, buf)) return false; // write version if(!BEncodeWriteDictInt("v", version, buf)) return false; if(W) { if(!BEncodeWriteDictEntry("w", *W, buf)) return false; } if(!BEncodeWriteDictEntry("z", Z, buf)) return false; return bencode_end(buf); } bool IntroSet::HasExpiredIntros(llarp_time_t now) const { for(const auto& i : I) if(now >= i.expiresAt) return true; return false; } bool IntroSet::IsExpired(llarp_time_t now) const { return GetNewestIntroExpiration() < now; } bool IntroSet::Verify(llarp::Crypto* crypto, llarp_time_t now) const { std::array< byte_t, MAX_INTROSET_SIZE > tmp; llarp_buffer_t buf(tmp); IntroSet copy; copy = *this; copy.Z.Zero(); if(!copy.BEncode(&buf)) { return false; } // rewind and resize buffer buf.sz = buf.cur - buf.base; buf.cur = buf.base; if(!A.Verify(crypto, buf, Z)) { return false; } // validate PoW using namespace std::placeholders; if(W && !W->IsValid(std::bind(&Crypto::shorthash, crypto, _1, _2), now)) { return false; } // valid timestamps // add max clock skew now += MAX_INTROSET_TIME_DELTA; for(const auto& intro : I) { if(intro.expiresAt > now && intro.expiresAt - now > path::default_lifetime) { if(W && intro.expiresAt - W->extendedLifetime > path::default_lifetime) return false; else if(W == nullptr) { llarp::LogWarn("intro has too high expire time"); return false; } } } if(IsExpired(now)) { llarp::LogWarn("introset expired: ", *this); return false; } return true; } llarp_time_t IntroSet::GetNewestIntroExpiration() const { llarp_time_t t = 0; for(const auto& intro : I) t = std::max(intro.expiresAt, t); return t; } std::ostream& IntroSet::print(std::ostream& stream, int level, int spaces) const { Printer printer(stream, level, spaces); printer.printAttribute("A", A); printer.printAttribute("I", I); printer.printAttribute("K", K); std::string _topic = topic.ToString(); if(!_topic.empty()) { printer.printAttribute("topic", _topic); } else { printer.printAttribute("topic", topic); } printer.printAttribute("T", T); printer.printAttribute("W", W); printer.printAttribute("V", version); printer.printAttribute("Z", Z); return stream; } } // namespace service } // namespace llarp