From f94443c63dfd63300e5bd29889935fd1f451175e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Smr=C5=BE?= Date: Sat, 21 Dec 2019 21:42:20 +0100 Subject: Identity storage and modification --- src/identity.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++ src/identity.h | 12 +++++++ src/pubkey.cpp | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pubkey.h | 53 +++++++++++++++++++++++++++++-- src/storage.cpp | 58 ++++++++++++++++++++++++++++++++-- src/storage.h | 1 + 6 files changed, 290 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/identity.cpp b/src/identity.cpp index 0160a69..4833f71 100644 --- a/src/identity.cpp +++ b/src/identity.cpp @@ -2,11 +2,13 @@ #include #include +#include using namespace erebos; using std::async; using std::nullopt; +using std::runtime_error; using std::set; optional Identity::load(const Ref & ref) @@ -41,6 +43,63 @@ optional Identity::owner() const return p->owner; } +optional Identity::ref() const +{ + if (p->data.size() == 1) + return p->data[0].ref; + return nullopt; +} + +Identity::Builder Identity::create(const Storage & st) +{ + return Builder (new Builder::Priv { + .storage = st, + .keyIdentity = SecretKey::generate(st).pub(), + .keyMessage = SecretKey::generate(st).pub(), + }); +} + +Identity::Builder Identity::modify() const +{ + return Builder (new Builder::Priv { + .storage = p->data[0].ref.storage(), + .prev = p->data, + .keyIdentity = p->data[0]->data->keyIdentity, + .keyMessage = p->data[0]->data->keyMessage, + }); +} + + +Identity Identity::Builder::commit() const +{ + auto idata = p->storage.store(IdentityData { + .prev = p->prev, + .name = p->name, + .owner = p->owner && p->owner->p->data.size() == 1 ? + optional(p->owner->p->data[0]) : nullopt, + .keyIdentity = p->keyIdentity, + .keyMessage = p->keyMessage, + }); + + auto key = SecretKey::load(p->keyIdentity); + if (!key) + throw runtime_error("failed to load secret key"); + + auto sdata = key->sign(idata); + + return Identity(Identity::Priv::validate({ sdata })); +} + +void Identity::Builder::name(const string & val) +{ + p->name = val; +} + +void Identity::Builder::owner(const Identity & val) +{ + p->owner.emplace(val); +} + optional IdentityData::load(const Ref & ref) { auto rec = ref->asRecord(); @@ -65,6 +124,23 @@ optional IdentityData::load(const Ref & ref) }; } +Ref IdentityData::store(const Storage & st) const +{ + vector items; + + for (const auto p : prev) + items.emplace_back("SPREV", p.ref); + if (name) + items.emplace_back("name", *name); + if (owner) + items.emplace_back("owner", owner->ref); + items.emplace_back("key-id", keyIdentity.ref); + if (keyMessage) + items.emplace_back("key-msg", keyMessage->ref); + + return st.storeObject(Record(std::move(items))); +} + bool Identity::Priv::verifySignatures(const Stored> & sdata) { if (!sdata->isSignedBy(sdata->data->keyIdentity)) diff --git a/src/identity.h b/src/identity.h index d31951f..4335d32 100644 --- a/src/identity.h +++ b/src/identity.h @@ -14,6 +14,7 @@ class IdentityData { public: static optional load(const Ref &); + Ref store(const Storage & st) const; const vector>> prev; const optional name; @@ -35,4 +36,15 @@ public: function sel) const; }; +class Identity::Builder::Priv +{ +public: + Storage storage; + vector>> prev = {}; + optional name = nullopt; + optional owner = nullopt; + Stored keyIdentity; + optional> keyMessage; +}; + } diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 3247ce2..e26bead 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -25,6 +25,91 @@ optional PublicKey::load(const Ref & ref) return nullopt; } +Ref PublicKey::store(const Storage & st) const +{ + vector items; + + 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); + + return st.storeObject(Record(std::move(items))); +} + +SecretKey SecretKey::generate(const Storage & st) +{ + unique_ptr + pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL), &EVP_PKEY_CTX_free); + if (!pctx) + throw runtime_error("failed to generate key"); + + if (EVP_PKEY_keygen_init(pctx.get()) != 1) + throw runtime_error("failed to generate key"); + + EVP_PKEY *pkey = NULL; + if (EVP_PKEY_keygen(pctx.get(), &pkey) != 1) + throw runtime_error("failed to generate key"); + shared_ptr seckey(pkey, EVP_PKEY_free); + + vector keyData; + size_t keyLen; + + EVP_PKEY_get_raw_public_key(seckey.get(), nullptr, &keyLen); + keyData.resize(keyLen); + EVP_PKEY_get_raw_public_key(seckey.get(), keyData.data(), &keyLen); + auto pubkey = st.store(PublicKey(EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, + keyData.data(), keyData.size()))); + + EVP_PKEY_get_raw_private_key(seckey.get(), nullptr, &keyLen); + keyData.resize(keyLen); + EVP_PKEY_get_raw_private_key(seckey.get(), keyData.data(), &keyLen); + st.storeKey(pubkey.ref, keyData); + + return SecretKey(std::move(seckey), pubkey); +} + +optional SecretKey::load(const Stored & pub) +{ + auto keyData = pub.ref.storage().loadKey(pub.ref); + if (!keyData) + return nullopt; + + EVP_PKEY * key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, + keyData->data(), keyData->size()); + if (!key) + throw runtime_error("falied to parse secret key"); + return SecretKey(key, pub); +} + +vector SecretKey::sign(const Digest & dgst) const +{ + unique_ptr + mdctx(EVP_MD_CTX_create(), &EVP_MD_CTX_free); + if (!mdctx) + throw runtime_error("failed to create EVP_MD_CTX"); + + if (EVP_DigestSignInit(mdctx.get(), nullptr, EVP_md_null(), + nullptr, key.get()) != 1) + throw runtime_error("failed to initialize EVP_MD_CTX"); + + size_t sigLen; + if (EVP_DigestSign(mdctx.get(), nullptr, &sigLen, + dgst.arr().data(), Digest::size) != 1) + throw runtime_error("failed to sign data"); + + vector sigData(sigLen); + if (EVP_DigestSign(mdctx.get(), sigData.data(), &sigLen, + dgst.arr().data(), Digest::size) != 1) + throw runtime_error("failed to sign data"); + + return sigData; +} + optional Signature::load(const Ref & ref) { auto rec = ref->asRecord(); @@ -43,6 +128,16 @@ optional Signature::load(const Ref & ref) }; } +Ref Signature::store(const Storage & st) const +{ + vector items; + + items.emplace_back("key", key); + items.emplace_back("sig", sig); + + return st.storeObject(Record(std::move(items))); +} + bool Signature::verify(const Ref & ref) const { unique_ptr diff --git a/src/pubkey.h b/src/pubkey.h index 7fe37ec..80da3fa 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -10,34 +10,57 @@ using std::shared_ptr; namespace erebos { +template class Signed; + class PublicKey { PublicKey(EVP_PKEY * key): key(key, EVP_PKEY_free) {} + friend class SecretKey; public: static optional load(const Ref &); + Ref store(const Storage &) const; + const shared_ptr key; }; class SecretKey { SecretKey(EVP_PKEY * key, const Stored & pub): - key(key, EVP_PKEY_free), pub(pub) {} + key(key, EVP_PKEY_free), pub_(pub) {} + SecretKey(shared_ptr && key, const Stored & pub): + key(key), pub_(pub) {} +public: + static SecretKey generate(const Storage & st); + static optional load(const Stored & st); + + Stored pub() const { return pub_; } + + template + Stored> sign(const Stored &) const; private: + vector sign(const Digest &) const; + const shared_ptr key; - Stored pub; + Stored pub_; }; class Signature { public: static optional load(const Ref &); + Ref store(const Storage &) const; bool verify(const Ref &) const; Stored key; vector sig; + +private: + friend class SecretKey; + Signature(const Stored & key, const vector & sig): + key(key), sig(sig) {} }; template @@ -45,13 +68,27 @@ class Signed { public: static optional> load(const Ref &); + Ref store(const Storage &) const; bool isSignedBy(const Stored &) const; const Stored data; const vector> sigs; + +private: + friend class SecretKey; + Signed(const Stored & data, const vector> & sigs): + data(data), sigs(sigs) {} }; +template +Stored> SecretKey::sign(const Stored & val) const +{ + auto st = val.ref.storage(); + auto sig = st.store(Signature(pub(), sign(val.ref.digest()))); + return st.store(Signed(val, { sig })); +} + template optional> Signed::load(const Ref & ref) { @@ -75,6 +112,18 @@ optional> Signed::load(const Ref & ref) }; } +template +Ref Signed::store(const Storage & st) const +{ + vector items; + + items.emplace_back("SDATA", data); + for (const auto & sig : sigs) + items.emplace_back("sig", sig); + + return st.storeObject(Record(std::move(items))); +} + template bool Signed::isSignedBy(const Stored & key) const { diff --git a/src/storage.cpp b/src/storage.cpp index d549959..b5e71b7 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -24,6 +24,7 @@ using std::ifstream; using std::make_shared; using std::monostate; using std::nullopt; +using std::ofstream; using std::runtime_error; using std::shared_ptr; using std::string; @@ -43,6 +44,16 @@ optional Storage::open(fs::path path) return Storage(shared_ptr(new Priv { path })); } +bool Storage::operator==(const Storage & other) const +{ + return p == other.p; +} + +bool Storage::operator!=(const Storage & other) const +{ + return p != other.p; +} + fs::path Storage::Priv::objectPath(const Digest & digest) const { string name(digest); @@ -51,6 +62,12 @@ fs::path Storage::Priv::objectPath(const Digest & digest) const fs::path(name.begin() + 2, name.end()); } +fs::path Storage::Priv::keyPath(const Digest & digest) const +{ + string name(digest); + return root/"keys"/fs::path(name.begin(), name.end()); +} + optional Storage::ref(const Digest & digest) const { return Ref::create(*this, digest); @@ -115,7 +132,7 @@ optional> Storage::Priv::loadBytes(const Digest & digest) const return out; } -optional Storage::load(const Digest & digest) const +optional Storage::loadObject(const Digest & digest) const { auto ocontent = p->loadBytes(digest); if (!ocontent.has_value()) @@ -193,7 +210,7 @@ void Storage::Priv::storeBytes(const Digest & digest, const vector & in fs::rename(lock, path); } -Ref Storage::store(const Object & object) const +Ref Storage::storeObject(const Object & object) const { // TODO: ensure storage transitively auto content = object.encode(); @@ -209,6 +226,32 @@ Ref Storage::store(const Object & object) const return Ref::create(*this, digest).value(); } +Ref Storage::storeObject(const class Record & val) const +{ return storeObject(Object(val)); } + +Ref Storage::storeObject(const class Blob & val) const +{ return storeObject(Object(val)); } + +void Storage::storeKey(Ref pubref, const vector & key) const +{ + ofstream file(p->keyPath(pubref.digest())); + file.write((const char *) key.data(), key.size()); +} + +optional> Storage::loadKey(Ref pubref) const +{ + fs::path path = p->keyPath(pubref.digest()); + std::error_code err; + size_t size = fs::file_size(path, err); + if (err) + return nullopt; + + vector key(size); + ifstream file(p->keyPath(pubref.digest())); + file.read((char *) key.data(), size); + return key; +} + Digest::Digest(const string & str) { @@ -243,7 +286,7 @@ optional Ref::create(Storage st, const Digest & digest) }; p->object = std::async(std::launch::deferred, [p] { - auto obj = p->storage.load(p->digest); + auto obj = p->storage.loadObject(p->digest); if (!obj.has_value()) throw runtime_error("failed to decode bytes"); @@ -268,6 +311,11 @@ const Object * Ref::operator->() const return &p->object.get(); } +const Storage & Ref::storage() const +{ + return p->storage; +} + Record::Item::operator bool() const { @@ -307,6 +355,10 @@ Record::Record(const vector & from): ptr(new vector(from)) {} +Record::Record(vector && from): + ptr(new vector(std::move(from))) +{} + Record Record::decode(Storage st, vector::const_iterator begin, vector::const_iterator end) diff --git a/src/storage.h b/src/storage.h index 2a0ad7e..e675848 100644 --- a/src/storage.h +++ b/src/storage.h @@ -19,6 +19,7 @@ struct Storage::Priv fs::path root; fs::path objectPath(const Digest &) const; + fs::path keyPath(const Digest &) const; optional> loadBytes(const Digest &) const; void storeBytes(const Digest &, const vector &) const; }; -- cgit v1.2.3