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;  }; |