diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/identity.cpp | 76 | ||||
-rw-r--r-- | src/identity.h | 12 | ||||
-rw-r--r-- | src/pubkey.cpp | 95 | ||||
-rw-r--r-- | src/pubkey.h | 53 | ||||
-rw-r--r-- | src/storage.cpp | 58 | ||||
-rw-r--r-- | src/storage.h | 1 |
6 files changed, 290 insertions, 5 deletions
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 <algorithm> #include <set> +#include <stdexcept> using namespace erebos; using std::async; using std::nullopt; +using std::runtime_error; using std::set; optional<Identity> Identity::load(const Ref & ref) @@ -41,6 +43,63 @@ optional<Identity> Identity::owner() const return p->owner; } +optional<Ref> 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> IdentityData::load(const Ref & ref) { auto rec = ref->asRecord(); @@ -65,6 +124,23 @@ optional<IdentityData> IdentityData::load(const Ref & ref) }; } +Ref IdentityData::store(const Storage & st) const +{ + vector<Record::Item> 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<Signed<IdentityData>> & 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<IdentityData> load(const Ref &); + Ref store(const Storage & st) const; const vector<Stored<Signed<IdentityData>>> prev; const optional<string> name; @@ -35,4 +36,15 @@ public: function<bool(const IdentityData &)> sel) const; }; +class Identity::Builder::Priv +{ +public: + Storage storage; + vector<Stored<Signed<IdentityData>>> prev = {}; + optional<string> name = nullopt; + optional<Identity> owner = nullopt; + Stored<PublicKey> keyIdentity; + optional<Stored<PublicKey>> 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> PublicKey::load(const Ref & ref) return nullopt; } +Ref PublicKey::store(const Storage & st) const +{ + vector<Record::Item> items; + + items.emplace_back("type", "ed25519"); + + vector<uint8_t> 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<EVP_PKEY_CTX, void(*)(EVP_PKEY_CTX*)> + 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<EVP_PKEY> seckey(pkey, EVP_PKEY_free); + + vector<uint8_t> 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> SecretKey::load(const Stored<PublicKey> & 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<uint8_t> SecretKey::sign(const Digest & dgst) const +{ + unique_ptr<EVP_MD_CTX, void(*)(EVP_MD_CTX*)> + 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<uint8_t> 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> Signature::load(const Ref & ref) { auto rec = ref->asRecord(); @@ -43,6 +128,16 @@ optional<Signature> Signature::load(const Ref & ref) }; } +Ref Signature::store(const Storage & st) const +{ + vector<Record::Item> 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<EVP_MD_CTX, void(*)(EVP_MD_CTX*)> 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<typename T> class Signed; + class PublicKey { PublicKey(EVP_PKEY * key): key(key, EVP_PKEY_free) {} + friend class SecretKey; public: static optional<PublicKey> load(const Ref &); + Ref store(const Storage &) const; + const shared_ptr<EVP_PKEY> key; }; class SecretKey { SecretKey(EVP_PKEY * key, const Stored<PublicKey> & pub): - key(key, EVP_PKEY_free), pub(pub) {} + key(key, EVP_PKEY_free), pub_(pub) {} + SecretKey(shared_ptr<EVP_PKEY> && key, const Stored<PublicKey> & pub): + key(key), pub_(pub) {} +public: + static SecretKey generate(const Storage & st); + static optional<SecretKey> load(const Stored<PublicKey> & st); + + Stored<PublicKey> pub() const { return pub_; } + + template<class T> + Stored<Signed<T>> sign(const Stored<T> &) const; private: + vector<uint8_t> sign(const Digest &) const; + const shared_ptr<EVP_PKEY> key; - Stored<PublicKey> pub; + Stored<PublicKey> pub_; }; class Signature { public: static optional<Signature> load(const Ref &); + Ref store(const Storage &) const; bool verify(const Ref &) const; Stored<PublicKey> key; vector<uint8_t> sig; + +private: + friend class SecretKey; + Signature(const Stored<PublicKey> & key, const vector<uint8_t> & sig): + key(key), sig(sig) {} }; template<typename T> @@ -45,13 +68,27 @@ class Signed { public: static optional<Signed<T>> load(const Ref &); + Ref store(const Storage &) const; bool isSignedBy(const Stored<PublicKey> &) const; const Stored<T> data; const vector<Stored<Signature>> sigs; + +private: + friend class SecretKey; + Signed(const Stored<T> & data, const vector<Stored<Signature>> & sigs): + data(data), sigs(sigs) {} }; +template<class T> +Stored<Signed<T>> SecretKey::sign(const Stored<T> & val) const +{ + auto st = val.ref.storage(); + auto sig = st.store(Signature(pub(), sign(val.ref.digest()))); + return st.store(Signed(val, { sig })); +} + template<typename T> optional<Signed<T>> Signed<T>::load(const Ref & ref) { @@ -76,6 +113,18 @@ optional<Signed<T>> Signed<T>::load(const Ref & ref) } template<typename T> +Ref Signed<T>::store(const Storage & st) const +{ + vector<Record::Item> items; + + items.emplace_back("SDATA", data); + for (const auto & sig : sigs) + items.emplace_back("sig", sig); + + return st.storeObject(Record(std::move(items))); +} + +template<typename T> bool Signed<T>::isSignedBy(const Stored<PublicKey> & key) const { for (const auto & sig : sigs) 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> Storage::open(fs::path path) return Storage(shared_ptr<const Priv>(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<Ref> Storage::ref(const Digest & digest) const { return Ref::create(*this, digest); @@ -115,7 +132,7 @@ optional<vector<uint8_t>> Storage::Priv::loadBytes(const Digest & digest) const return out; } -optional<Object> Storage::load(const Digest & digest) const +optional<Object> 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<uint8_t> & 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<uint8_t> & key) const +{ + ofstream file(p->keyPath(pubref.digest())); + file.write((const char *) key.data(), key.size()); +} + +optional<vector<uint8_t>> 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<uint8_t> 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> 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<Item> & from): ptr(new vector<Item>(from)) {} +Record::Record(vector<Item> && from): + ptr(new vector<Item>(std::move(from))) +{} + Record Record::decode(Storage st, vector<uint8_t>::const_iterator begin, vector<uint8_t>::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<vector<uint8_t>> loadBytes(const Digest &) const; void storeBytes(const Digest &, const vector<uint8_t> &) const; }; |