From 6cf5244bc514042fe419fffe1cd26a7f5e3c778f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Smr=C5=BE?= Date: Thu, 16 Apr 2020 21:42:45 +0200 Subject: Remove optional from load result Makes loading of data well-defined for arbitrary object contents. Introduce zero reference and object to represent missing or mismatched parts. --- include/erebos/message.h | 6 +-- include/erebos/storage.h | 16 ++++---- src/channel.cpp | 102 +++++++++++++++++++---------------------------- src/channel.h | 6 +-- src/identity.cpp | 45 ++++++++++----------- src/identity.h | 2 +- src/message.cpp | 39 ++++++++---------- src/message.h | 8 ++-- src/network.cpp | 14 ++++--- src/pubkey.cpp | 57 +++++++++++++------------- src/pubkey.h | 37 ++++++++--------- src/storage.cpp | 34 +++++++++++++--- 12 files changed, 184 insertions(+), 182 deletions(-) diff --git a/include/erebos/message.h b/include/erebos/message.h index 0b5e257..c94e8c3 100644 --- a/include/erebos/message.h +++ b/include/erebos/message.h @@ -14,9 +14,9 @@ class Identity; class DirectMessage { public: - const Identity & from() const; - const struct ZonedTime & time() const; - const std::string & text() const; + const std::optional & from() const; + const std::optional & time() const; + std::string text() const; private: friend class DirectMessageThread; diff --git a/include/erebos/storage.h b/include/erebos/storage.h index d5ae45a..d46c056 100644 --- a/include/erebos/storage.h +++ b/include/erebos/storage.h @@ -72,6 +72,7 @@ public: PartialStorage derivePartialStorage() const; std::optional ref(const Digest &) const; + Ref zref() const; std::optional loadObject(const Digest &) const; Ref storeObject(const Object &) const; @@ -103,6 +104,7 @@ public: explicit Digest(std::array value): value(value) {} explicit Digest(const std::string &); explicit operator std::string() const; + bool isZero() const; const std::array & arr() const { return value; } @@ -151,6 +153,7 @@ public: Ref & operator=(Ref &&) = default; static std::optional create(Storage, const Digest &); + static Ref zcreate(Storage); constexpr operator bool() const { return true; } const Object operator*() const; @@ -265,7 +268,8 @@ class ObjectT public: typedef std::variant< RecordT, - Blob> Variants; + Blob, + std::monostate> Variants; ObjectT(const ObjectT &) = default; ObjectT(Variants content): content(content) {} @@ -282,7 +286,7 @@ public: std::vector::const_iterator); static std::vector> decodeMany(const S &, const std::vector &); std::vector encode() const; - static std::optional> load(const typename S::Ref &); + static ObjectT load(const typename S::Ref &); std::optional> asRecord() const; std::optional asBlob() const; @@ -317,7 +321,7 @@ public: Stored & operator=(const Stored &) = default; Stored & operator=(Stored &&) = default; - static std::optional> load(const Ref &); + static Stored load(const Ref &); Ref store(const Storage &) const; bool operator==(const Stored & other) const @@ -354,11 +358,9 @@ Stored Storage::store(const T & val) const } template -std::optional> Stored::load(const Ref & ref) +Stored Stored::load(const Ref & ref) { - if (auto val = T::load(ref)) - return Stored(ref, std::make_shared(val.value())); - return std::nullopt; + return Stored(ref, std::make_shared(T::load(ref))); } template diff --git a/src/channel.cpp b/src/channel.cpp index 50c5f97..b13a71e 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -22,28 +22,25 @@ Ref ChannelRequestData::store(const Storage & st) const return st.storeObject(Record(std::move(items))); } -optional ChannelRequestData::load(const Ref & ref) +ChannelRequestData ChannelRequestData::load(const Ref & ref) { - auto rec = ref->asRecord(); - if (!rec) - return nullopt; - - remove_const::type peers; - for (const auto & i : rec->items("peer")) - if (auto p = i.as>()) - peers.push_back(*p); - - auto enc = rec->item("enc").asText(); - if (!enc || enc != "aes-128-gcm") - return nullopt; - - auto key = rec->item("key").as(); - if (!key) - return nullopt; + if (auto rec = ref->asRecord()) { + remove_const::type peers; + for (const auto & i : rec->items("peer")) + if (auto p = i.as>()) + peers.push_back(*p); + + if (rec->item("enc").asText() == "aes-128-gcm") + if (auto key = rec->item("key").as()) + return ChannelRequestData { + .peers = std::move(peers), + .key = *key, + }; + } return ChannelRequestData { - .peers = std::move(peers), - .key = *key, + .peers = {}, + .key = Stored::load(ref.storage().zref()), }; } @@ -58,27 +55,18 @@ Ref ChannelAcceptData::store(const Storage & st) const return st.storeObject(Record(std::move(items))); } -optional ChannelAcceptData::load(const Ref & ref) +ChannelAcceptData ChannelAcceptData::load(const Ref & ref) { - auto rec = ref->asRecord(); - if (!rec) - return nullopt; - - auto request = rec->item("req").as(); - if (!request) - return nullopt; - - auto enc = rec->item("enc").asText(); - if (!enc || enc != "aes-128-gcm") - return nullopt; - - auto key = rec->item("key").as(); - if (!key) - return nullopt; + if (auto rec = ref->asRecord()) + if (rec->item("enc").asText() == "aes-128-gcm") + return ChannelAcceptData { + .request = *rec->item("req").as(), + .key = *rec->item("key").as(), + }; return ChannelAcceptData { - .request = *request, - .key = *key, + .request = Stored::load(ref.storage().zref()), + .key = Stored::load(ref.storage().zref()), }; } @@ -114,26 +102,20 @@ Ref Channel::store(const Storage & st) const return st.storeObject(Record(std::move(items))); } -optional Channel::load(const Ref & ref) +Channel Channel::load(const Ref & ref) { - auto rec = ref->asRecord(); - if (!rec) - return nullopt; - - remove_const::type peers; - for (const auto & i : rec->items("peer")) - if (auto p = i.as>()) - peers.push_back(*p); - - auto enc = rec->item("enc").asText(); - if (!enc || enc != "aes-128-gcm") - return nullopt; - - auto key = rec->item("key").asBinary(); - if (!key) - return nullopt; - - return Channel(peers, std::move(*key)); + if (auto rec = ref->asRecord()) { + remove_const::type peers; + for (const auto & i : rec->items("peer")) + if (auto p = i.as>()) + peers.push_back(*p); + + if (rec->item("enc").asText() == "aes-128-gcm") + if (auto key = rec->item("key").asBinary()) + return Channel(peers, std::move(*key)); + } + + return Channel({}, {}); } Stored Channel::generateRequest(const Storage & st, @@ -146,12 +128,12 @@ Stored Channel::generateRequest(const Storage & st, return signKey->sign(st.store(ChannelRequestData { .peers = self.ref()->digest() < peer.ref()->digest() ? vector>> { - *Stored>::load(*self.ref()), - *Stored>::load(*peer.ref()), + Stored>::load(*self.ref()), + Stored>::load(*peer.ref()), } : vector>> { - *Stored>::load(*peer.ref()), - *Stored>::load(*self.ref()), + Stored>::load(*peer.ref()), + Stored>::load(*self.ref()), }, .key = SecretKexKey::generate(st).pub(), })); diff --git a/src/channel.h b/src/channel.h index 100003c..1c7df30 100644 --- a/src/channel.h +++ b/src/channel.h @@ -9,7 +9,7 @@ namespace erebos { struct ChannelRequestData { Ref store(const Storage & st) const; - static optional load(const Ref &); + static ChannelRequestData load(const Ref &); const vector>> peers; const Stored key; @@ -20,7 +20,7 @@ typedef Signed ChannelRequest; struct ChannelAcceptData { Ref store(const Storage & st) const; - static optional load(const Ref &); + static ChannelAcceptData load(const Ref &); Stored channel() const; @@ -40,7 +40,7 @@ public: {} Ref store(const Storage & st) const; - static optional load(const Ref &); + static Channel load(const Ref &); static Stored generateRequest(const Storage &, const Identity & self, const Identity & peer); diff --git a/src/identity.cpp b/src/identity.cpp index 3cba97c..2396b9f 100644 --- a/src/identity.cpp +++ b/src/identity.cpp @@ -24,12 +24,8 @@ optional Identity::load(const vector & refs) vector>> data; data.reserve(refs.size()); - for (const auto & ref : refs) { - auto d = Stored>::load(ref); - if (!d) - return nullopt; - data.push_back(*d); - } + for (const auto & ref : refs) + data.push_back(Stored>::load(ref)); if (auto ptr = Priv::validate(data)) return Identity(ptr); @@ -124,27 +120,30 @@ void Identity::Builder::owner(const Identity & val) p->owner.emplace(val); } -optional IdentityData::load(const Ref & ref) +IdentityData IdentityData::load(const Ref & ref) { - auto rec = ref->asRecord(); - if (!rec) - return nullopt; - - vector>> prev; - for (auto p : rec->items("SPREV")) - if (const auto & x = p.as>()) - prev.push_back(x.value()); + if (auto rec = ref->asRecord()) { + vector>> prev; + for (auto p : rec->items("SPREV")) + if (const auto & x = p.as>()) + prev.push_back(x.value()); - auto keyIdentity = rec->item("key-id").as(); - if (!keyIdentity) - return nullopt; + if (auto keyIdentity = rec->item("key-id").as()) + return IdentityData { + .prev = std::move(prev), + .name = rec->item("name").asText(), + .owner = rec->item("owner").as>(), + .keyIdentity = keyIdentity.value(), + .keyMessage = rec->item("key-msg").as(), + }; + } return IdentityData { - .prev = std::move(prev), - .name = rec->item("name").asText(), - .owner = rec->item("owner").as>(), - .keyIdentity = keyIdentity.value(), - .keyMessage = rec->item("key-msg").as(), + .prev = {}, + .name = nullopt, + .owner = nullopt, + .keyIdentity = Stored::load(ref.storage().zref()), + .keyMessage = nullopt, }; } diff --git a/src/identity.h b/src/identity.h index 92ac34f..1dfc193 100644 --- a/src/identity.h +++ b/src/identity.h @@ -13,7 +13,7 @@ namespace erebos { class IdentityData { public: - static optional load(const Ref &); + static IdentityData load(const Ref &); Ref store(const Storage & st) const; const vector>> prev; diff --git a/src/message.cpp b/src/message.cpp index b7d4bda..354703e 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -48,11 +48,11 @@ DirectMessage::DirectMessage(Priv * p): p(p) {} -optional DirectMessageData::load(const Ref & ref) +DirectMessageData DirectMessageData::load(const Ref & ref) { auto rec = ref->asRecord(); if (!rec) - return nullopt; + return DirectMessageData(); vector> prev; for (auto p : rec->items("PREV")) @@ -60,16 +60,10 @@ optional DirectMessageData::load(const Ref & ref) prev.push_back(*x); auto fref = rec->item("from").asRef(); - if (!fref) - return nullopt; - - auto from = Identity::load(*fref); - if (!from) - return nullopt; return DirectMessageData { .prev = std::move(prev), - .from = *from, + .from = fref ? Identity::load(*fref) : nullopt, .time = *rec->item("time").asDate(), .text = rec->item("text").asText().value(), }; @@ -81,27 +75,32 @@ Ref DirectMessageData::store(const Storage & st) const for (const auto prev : prev) items.emplace_back("PREV", prev.ref()); - items.emplace_back("from", from.ref().value()); - items.emplace_back("time", time); - items.emplace_back("text", text); + if (from) + items.emplace_back("from", from->ref().value()); + if (time) + items.emplace_back("time", *time); + if (text) + items.emplace_back("text", *text); return st.storeObject(Record(std::move(items))); } -const Identity & DirectMessage::from() const +const optional & DirectMessage::from() const { return p->data->from; } -const ZonedTime & DirectMessage::time() const +const optional & DirectMessage::time() const { return p->data->time; } -const string & DirectMessage::text() const +string DirectMessage::text() const { - return p->data->text; + if (p->data->text) + return p->data->text.value(); + return ""; } @@ -138,7 +137,7 @@ DirectMessageThread::Iterator & DirectMessageThread::Iterator::operator++() auto ncur = p->next[0]; for (const auto & m : p->next) - if (m->time.time >= ncur->time.time) + if (!ncur->time || (m->time && m->time->time >= ncur->time->time)) ncur = m; p->current.emplace(DirectMessage(new DirectMessage::Priv { @@ -216,10 +215,6 @@ UUID DirectMessageService::uuid() const void DirectMessageService::handle(Context & ctx) const { - auto dm = Stored::load(ctx.ref()); - if (!dm) - return; - auto pid = ctx.peer().identity(); if (!pid) return; @@ -228,7 +223,7 @@ void DirectMessageService::handle(Context & ctx) const unique_lock lock(threadLock); vector> head(DirectMessageThread::Priv::getThreadLocked(powner).p->head); - head.push_back(*dm); + head.push_back(Stored::load(ctx.ref())); filterAncestors(head); auto dmt = DirectMessageThread::Priv::updateThreadLocked(powner, std::move(head)); diff --git a/src/message.h b/src/message.h index 7e0f597..c4a3549 100644 --- a/src/message.h +++ b/src/message.h @@ -19,13 +19,13 @@ namespace erebos { struct DirectMessageData { - static optional load(const Ref &); + static DirectMessageData load(const Ref &); Ref store(const Storage &) const; vector> prev; - Identity from; - ZonedTime time; - string text; + optional from; + optional time; + optional text; }; struct DirectMessage::Priv diff --git a/src/network.cpp b/src/network.cpp index b31d949..91f1923 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -298,7 +298,7 @@ Server::Peer & Server::Priv::getPeer(const sockaddr_in & paddr) void Server::Priv::handlePacket(Server::Peer & peer, const TransportHeader & header, ReplyBuilder & reply) { unordered_set plaintextRefs; - for (const auto & obj : collectStoredObjects(*Stored::load(*self.ref()))) + for (const auto & obj : collectStoredObjects(Stored::load(*self.ref()))) plaintextRefs.insert(obj.ref().digest()); optional serviceType; @@ -397,9 +397,11 @@ void Server::Priv::handlePacket(Server::Peer & peer, const TransportHeader & hea auto cres = peer.tempStorage.copy(pref); if (auto r = std::get_if(&cres)) { - if (auto acc = ChannelAccept::load(*r)) { + auto acc = ChannelAccept::load(*r); + if (holds_alternative(peer.identity) && + acc.isSignedBy(std::get(peer.identity).keyMessage())) { reply.header({ TransportHeader::Type::Acknowledged, pref }); - peer.channel.emplace>(acc->data->channel()); + peer.channel.emplace>(acc.data->channel()); } } } @@ -485,8 +487,10 @@ void Server::Peer::updateChannel(ReplyBuilder & reply) if (holds_alternative>(channel)) { if (auto ref = std::get>(channel)->check(reply)) { - if (auto req = Stored::load(*ref)) { - if (auto acc = Channel::acceptRequest(server.self, std::get(identity), *req)) { + auto req = Stored::load(*ref); + if (holds_alternative(identity) && + req->isSignedBy(std::get(identity).keyMessage())) { + if (auto acc = Channel::acceptRequest(server.self, std::get(identity), req)) { channel.emplace>(*acc); reply.header({ TransportHeader::Type::ChannelAccept, acc->ref() }); reply.body(acc->ref()); diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 0e83136..6d85d33 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -8,11 +8,11 @@ using std::string; using namespace erebos; -optional PublicKey::load(const Ref & ref) +PublicKey PublicKey::load(const Ref & ref) { auto rec = ref->asRecord(); if (!rec) - return nullopt; + return PublicKey(nullptr); if (auto ktype = rec->item("type").asText()) if (ktype.value() != "ed25519") @@ -22,7 +22,7 @@ optional PublicKey::load(const Ref & ref) return PublicKey(EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pubkey.value().data(), pubkey.value().size())); - return nullopt; + return PublicKey(nullptr); } Ref PublicKey::store(const Storage & st) const @@ -31,12 +31,14 @@ Ref PublicKey::store(const Storage & st) const items.emplace_back("type", "ed25519"); - vector keyData; - size_t keyLen; - EVP_PKEY_get_raw_public_key(key.get(), nullptr, &keyLen); - keyData.resize(keyLen); - EVP_PKEY_get_raw_public_key(key.get(), keyData.data(), &keyLen); - items.emplace_back("pubkey", keyData); + if (key) { + vector keyData; + size_t keyLen; + EVP_PKEY_get_raw_public_key(key.get(), nullptr, &keyLen); + keyData.resize(keyLen); + EVP_PKEY_get_raw_public_key(key.get(), keyData.data(), &keyLen); + items.emplace_back("pubkey", keyData); + } return st.storeObject(Record(std::move(items))); } @@ -110,19 +112,14 @@ vector SecretKey::sign(const Digest & dgst) const return sigData; } -optional Signature::load(const Ref & ref) +Signature Signature::load(const Ref & ref) { - auto rec = ref->asRecord(); - if (!rec) - return nullopt; + if (auto rec = ref->asRecord()) + if (auto key = rec->item("key").as()) + if (auto sig = rec->item("sig").asBinary()) + return Signature(*key, *sig); - auto key = rec->item("key").as(); - auto sig = rec->item("sig").asBinary(); - - if (!key || !sig) - return nullopt; - - return Signature(*key, *sig); + return Signature(Stored::load(ref.storage().zref()), {}); } Ref Signature::store(const Storage & st) const @@ -151,11 +148,11 @@ bool Signature::verify(const Ref & ref) const } -optional PublicKexKey::load(const Ref & ref) +PublicKexKey PublicKexKey::load(const Ref & ref) { auto rec = ref->asRecord(); if (!rec) - return nullopt; + return PublicKexKey(nullptr); if (auto ktype = rec->item("type").asText()) if (ktype.value() != "x25519") @@ -165,7 +162,7 @@ optional PublicKexKey::load(const Ref & ref) return PublicKexKey(EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, nullptr, pubkey.value().data(), pubkey.value().size())); - return nullopt; + return PublicKexKey(nullptr); } Ref PublicKexKey::store(const Storage & st) const @@ -174,12 +171,14 @@ Ref PublicKexKey::store(const Storage & st) const items.emplace_back("type", "x25519"); - vector keyData; - size_t keyLen; - EVP_PKEY_get_raw_public_key(key.get(), nullptr, &keyLen); - keyData.resize(keyLen); - EVP_PKEY_get_raw_public_key(key.get(), keyData.data(), &keyLen); - items.emplace_back("pubkey", keyData); + if (key) { + vector keyData; + size_t keyLen; + EVP_PKEY_get_raw_public_key(key.get(), nullptr, &keyLen); + keyData.resize(keyLen); + EVP_PKEY_get_raw_public_key(key.get(), keyData.data(), &keyLen); + items.emplace_back("pubkey", keyData); + } return st.storeObject(Record(std::move(items))); } diff --git a/src/pubkey.h b/src/pubkey.h index c922dc7..7b80752 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -18,7 +18,7 @@ class PublicKey key(key, EVP_PKEY_free) {} friend class SecretKey; public: - static optional load(const Ref &); + static PublicKey load(const Ref &); Ref store(const Storage &) const; const shared_ptr key; @@ -49,7 +49,7 @@ private: class Signature { public: - static optional load(const Ref &); + static Signature load(const Ref &); Ref store(const Storage &) const; bool verify(const Ref &) const; @@ -67,7 +67,7 @@ template class Signed { public: - static optional> load(const Ref &); + static Signed load(const Ref &); Ref store(const Storage &) const; bool isSignedBy(const Stored &) const; @@ -90,23 +90,20 @@ Stored> SecretKey::sign(const Stored & val) const } template -optional> Signed::load(const Ref & ref) +Signed Signed::load(const Ref & ref) { - auto rec = ref->asRecord(); - if (!rec) - return nullopt; - - auto data = rec->item("SDATA").as(); - if (!data) - return nullopt; - - vector> sigs; - for (auto item : rec->items("sig")) - if (auto sig = item.as()) - if (sig.value()->verify(data.value().ref())) - sigs.push_back(sig.value()); - - return Signed(*data, sigs); + if (auto rec = ref->asRecord()) + if (auto data = rec->item("SDATA").as()) { + vector> sigs; + for (auto item : rec->items("sig")) + if (auto sig = item.as()) + if (sig.value()->verify(data.value().ref())) + sigs.push_back(sig.value()); + + return Signed(*data, sigs); + } + + return Signed(Stored::load(ref.storage().zref()), {}); } template @@ -137,7 +134,7 @@ class PublicKexKey key(key, EVP_PKEY_free) {} friend class SecretKexKey; public: - static optional load(const Ref &); + static PublicKexKey load(const Ref &); Ref store(const Storage &) const; const shared_ptr key; diff --git a/src/storage.cpp b/src/storage.cpp index 81a70e1..6a0669c 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -315,6 +315,11 @@ optional Storage::ref(const Digest & digest) const return Ref::create(*this, digest); } +Ref Storage::zref() const +{ + return Ref::zcreate(*this); +} + Digest PartialStorage::Priv::storeBytes(const vector & content) const { array arr; @@ -346,6 +351,8 @@ optional> PartialStorage::Priv::loadBytes(const Digest & digest) optional PartialStorage::loadObject(const Digest & digest) const { + if (digest.isZero()) + return PartialObject(monostate()); if (auto content = p->loadBytes(digest)) return PartialObject::decode(*this, *content); return nullopt; @@ -362,6 +369,8 @@ PartialRef PartialStorage::storeObject(const Blob & val) const optional Storage::loadObject(const Digest & digest) const { + if (digest.isZero()) + return Object(monostate()); if (auto content = p->loadBytes(digest)) return Object::decode(*this, *content); return nullopt; @@ -466,6 +475,13 @@ Digest::operator string() const return res; } +bool Digest::isZero() const +{ + for (uint8_t x : value) + if (x) return false; + return true; +} + PartialRef PartialRef::create(PartialStorage st, const Digest & digest) { @@ -517,6 +533,16 @@ optional Ref::create(Storage st, const Digest & digest) return Ref(shared_ptr(p)); } +Ref Ref::zcreate(Storage st) +{ + auto p = new Priv { + .storage = make_unique(st), + .digest = Digest(array {}), + }; + + return Ref(shared_ptr(p)); +} + const Object Ref::operator*() const { if (auto res = static_cast(p->storage.get())->loadObject(p->digest)) @@ -854,11 +880,9 @@ vector ObjectT::encode() const } template -optional> ObjectT::load(const typename S::Ref & ref) +ObjectT ObjectT::load(const typename S::Ref & ref) { - if (ref) - return *ref; - return nullopt; + return *ref; } template @@ -899,7 +923,7 @@ vector> erebos::collectStoredObjects(const Stored & from) if (auto rec = cur->asRecord()) for (const auto & item : rec->items()) if (auto ref = item.asRef()) - queue.push_back(*Stored::load(*ref)); + queue.push_back(Stored::load(*ref)); } return res; -- cgit v1.2.3