aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksi Lindeman <0xdec05eba@gmail.com>2018-02-14 22:18:48 +0100
committerAleksi Lindeman <0xdec05eba@gmail.com>2018-02-14 22:18:55 +0100
commit67957afb6ba01bcd85f1abd1a50ad2c1aa813c7c (patch)
tree12d3d015df767fd8ec239dd5c8941e50f20d2ef7
parent9c64764acc74b7f52c28110ede556098c7e5f116 (diff)
Sign messages/verify message signatures
-rw-r--r--.vscode/launch.json27
-rw-r--r--include/Database.hpp5
-rw-r--r--include/Signature.hpp28
-rw-r--r--include/StagedObject.hpp6
-rw-r--r--src/Database.cpp39
-rw-r--r--src/Signature.cpp19
-rw-r--r--tests/main.cpp60
7 files changed, 153 insertions, 31 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..956830f
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "tests/sibs-build/debug/test",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${workspaceFolder}",
+ "environment": [],
+ "externalConsole": true,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/include/Database.hpp b/include/Database.hpp
index 0104a6e..e8b35bb 100644
--- a/include/Database.hpp
+++ b/include/Database.hpp
@@ -12,7 +12,6 @@
namespace odhtdb
{
- class Group;
class LocalUser;
class Database
@@ -22,8 +21,8 @@ namespace odhtdb
~Database();
void seed();
- void create(const Key &key, Group *primaryAdminGroup);
- void add(const Key &key, DataView data, LocalUser *creator);
+ void create(LocalUser *owner, const Key &key);
+ void add(LocalUser *owner, const Key &key, DataView data);
void commit();
private:
void commitStagedCreateObject(const StagedCreateObject &stagedObject);
diff --git a/include/Signature.hpp b/include/Signature.hpp
index ea776ea..aace383 100644
--- a/include/Signature.hpp
+++ b/include/Signature.hpp
@@ -1,11 +1,13 @@
#pragma once
+#include "DataView.hpp"
#include <stdexcept>
namespace odhtdb
{
const int PUBLIC_KEY_NUM_BYTES = 32;
const int PRIVATE_KEY_NUM_BYTES = 64;
+ const int SIGNED_HASH_SIZE = 64;
class InvalidSignatureKeySize : public std::runtime_error
{
@@ -25,6 +27,25 @@ namespace odhtdb
DataSignException(const std::string &errMsg) : std::runtime_error(errMsg) {}
};
+ class UnsignException : public std::runtime_error
+ {
+ public:
+ UnsignException(const std::string &errMsg) : std::runtime_error(errMsg) {}
+ virtual ~UnsignException(){}
+ };
+
+ class UnsignInvalidSizeException : public UnsignException
+ {
+ public:
+ UnsignInvalidSizeException(const std::string &errMsg) : UnsignException(errMsg) {}
+ };
+
+ class UnsignWrongKeyException : public UnsignException
+ {
+ public:
+ UnsignWrongKeyException(const std::string &errMsg) : UnsignException(errMsg) {}
+ };
+
namespace Signature
{
class PublicKey
@@ -41,6 +62,11 @@ namespace odhtdb
const char* getData() const { return data; }
size_t getSize() const { return PUBLIC_KEY_NUM_BYTES; }
+ // Throws UnsignWrongKeyException if signed message was not signed using the matching private key of this public key.
+ // Throws UnsignInvalidSizeException if signed message is too small (< SIGNED_HASH_SIZE).
+ // Both exceptions are derived from UnsignException
+ std::string unsign(const DataView &signedMessage) const;
+
std::string toString() const;
private:
PublicKey(){}
@@ -61,7 +87,7 @@ namespace odhtdb
size_t getSize() const { return PRIVATE_KEY_NUM_BYTES; }
// Throws DataSignException if signing data failed for whatever reason. This wont happen unless there is an issue with the private key
- std::string sign(const std::string &dataToSign) const;
+ std::string sign(const DataView &dataToSign) const;
std::string toString() const;
private:
PrivateKey(){}
diff --git a/include/StagedObject.hpp b/include/StagedObject.hpp
index dc2aaf4..fccf4f6 100644
--- a/include/StagedObject.hpp
+++ b/include/StagedObject.hpp
@@ -26,13 +26,13 @@ namespace odhtdb
struct StagedAddObject
{
Key key;
- DataView data;
+ std::unique_ptr<std::string> data;
u64 timestamp; // In microseconds
Signature::PublicKey creatorPublicKey;
StagedAddObject() : key(), data(), timestamp(0), creatorPublicKey(Signature::PublicKey::ZERO) {}
- StagedAddObject(const Key &_key, const DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) :
- key(_key), data(_data), timestamp(_timestamp), creatorPublicKey(_creatorPublicKey)
+ StagedAddObject(const Key &_key, std::unique_ptr<std::string> &&_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) :
+ key(_key), data(std::move(_data)), timestamp(_timestamp), creatorPublicKey(_creatorPublicKey)
{
}
diff --git a/src/Database.cpp b/src/Database.cpp
index e3b9f3d..90e83c1 100644
--- a/src/Database.cpp
+++ b/src/Database.cpp
@@ -85,18 +85,21 @@ namespace odhtdb
node.listen(ADD_DATA_HASH, bind(&Database::listenAddData, this, _1));
}
- void Database::create(const Key &key, Group *primaryAdminGroup)
+ void Database::create(LocalUser *owner, const Key &key)
{
+ Group *primaryAdminGroup = new Group("admin");
+ primaryAdminGroup->addUser(owner);
// TODO: Append fractions to get real microseconds time
u64 timeMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull;
stagedCreateObjects.emplace_back(StagedCreateObject(key, primaryAdminGroup, timeMicroseconds));
}
- void Database::add(const Key &key, DataView data, LocalUser *creator)
+ void Database::add(LocalUser *owner, const Key &key, DataView data)
{
+ unique_ptr<string> signedData = make_unique<string>(owner->getPrivateKey().sign(data));
// TODO: Append fractions to get real microseconds time
u64 timeMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull;
- stagedAddObjects.emplace_back(StagedAddObject(key, data, timeMicroseconds, creator->getPublicKey()));
+ stagedAddObjects.emplace_back(StagedAddObject(key, move(signedData), timeMicroseconds, owner->getPublicKey()));
}
void Database::commit()
@@ -108,6 +111,7 @@ namespace odhtdb
for(StagedCreateObject &stagedObject : stagedCreateObjects)
{
commitStagedCreateObject(stagedObject);
+ delete stagedObject.primaryAdminGroup;
}
stagedCreateObjects.clear();
@@ -177,9 +181,9 @@ namespace odhtdb
serializer.add(stagedObject.key.hashedKey.data(), OPENDHT_INFOHASH_LEN);
serializer.add(stagedObject.timestamp);
serializer.add((u8*)stagedObject.creatorPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
- assert(stagedObject.data.size < 0xFFFF - 120);
- serializer.add((u16)stagedObject.data.size);
- serializer.add((u8*)stagedObject.data.data, stagedObject.data.size);
+ assert(stagedObject.data->size() < 0xFFFF - 120);
+ serializer.add((u16)stagedObject.data->size());
+ serializer.add((u8*)stagedObject.data->data(), stagedObject.data->size());
// TODO: Verify if serializer buffer needs to survive longer than this scope
Value addDataValue(serializer.getBuffer().data(), serializer.getBuffer().size());
@@ -262,11 +266,15 @@ namespace odhtdb
Signature::PublicKey creatorPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
u16 dataSize = deserializer.extract<u16>();
- char *data = (char*)malloc(dataSize);
- if(!data)
- throw sibs::DeserializeException("Failed to allocate memory for add request");
- result.data.data = data;
- result.data.size = dataSize;
+ if(dataSize < SIGNED_HASH_SIZE)
+ throw sibs::DeserializeException("Signed data is too small");
+
+ string signedData;
+ signedData.resize(dataSize);
+ deserializer.extract((u8*)&signedData[0], dataSize);
+ result.data = make_unique<string>();
+ result.data->resize(dataSize);
+ result.data = make_unique<string>(creatorPublicKey.unsign(DataView((void*)signedData.data(), signedData.size())));
return result;
}
@@ -279,7 +287,6 @@ namespace odhtdb
// TODO: Verify createObject timestamp is not in the future
StagedCreateObject createObject = deserializeCreateRequest(value);
databaseStorage.createStorage(createObject.key, { createObject.primaryAdminGroup }, createObject.timestamp);
- //delete createObject.primaryAdminGroup;
}
catch (sibs::DeserializeException &e)
{
@@ -299,8 +306,8 @@ namespace odhtdb
{
// TODO: Verify createObject timestamp is not in the future
StagedAddObject addObject = deserializeAddRequest(value);
- databaseStorage.appendStorage(addObject.key, addObject.data, addObject.timestamp, addObject.creatorPublicKey);
- //free(addObject.data.data);
+ DataView data((void*)addObject.data->data(), addObject.data->size());
+ databaseStorage.appendStorage(addObject.key, data, addObject.timestamp, addObject.creatorPublicKey);
}
catch (sibs::DeserializeException &e)
{
@@ -310,6 +317,10 @@ namespace odhtdb
{
fprintf(stderr, "Warning: Failed to deserialize 'add' request: %s\n", e.what());
}
+ catch (UnsignException &e)
+ {
+ fprintf(stderr, "Warning: Failed to deserialize 'add' request: %s\n", e.what());
+ }
return true;
}
}
diff --git a/src/Signature.cpp b/src/Signature.cpp
index 946d754..8d3654d 100644
--- a/src/Signature.cpp
+++ b/src/Signature.cpp
@@ -35,6 +35,19 @@ namespace odhtdb
return *this;
}
+ string PublicKey::unsign(const DataView &signedMessage) const
+ {
+ if(signedMessage.size < SIGNED_HASH_SIZE)
+ throw UnsignInvalidSizeException("Signed message is too small (corrupt or malicious signed message)");
+
+ string result;
+ result.resize(signedMessage.size - SIGNED_HASH_SIZE);
+ if(crypto_sign_ed25519_open((unsigned char*)&result[0], nullptr, (const unsigned char*)signedMessage.data, signedMessage.size, (unsigned char*)data) != 0)
+ throw UnsignWrongKeyException("Message was not signed with matching private key");
+
+ return result;
+ }
+
string PublicKey::toString() const
{
string result;
@@ -67,13 +80,13 @@ namespace odhtdb
return *this;
}
- string PrivateKey::sign(const string &dataToSign) const
+ string PrivateKey::sign(const DataView &dataToSign) const
{
string result;
- result.resize(crypto_sign_ed25519_BYTES + dataToSign.size());
+ result.resize(crypto_sign_ed25519_BYTES + dataToSign.size);
unsigned long long resultSize;
- if(crypto_sign_ed25519((unsigned char*)&result[0], &resultSize, (unsigned char*)dataToSign.data(), dataToSign.size(), (unsigned char*)data) != 0)
+ if(crypto_sign_ed25519((unsigned char*)&result[0], &resultSize, (unsigned char*)dataToSign.data, dataToSign.size, (unsigned char*)data) != 0)
throw DataSignException("Failed to sign data. Is private key invalid?");
if(resultSize != result.size())
diff --git a/tests/main.cpp b/tests/main.cpp
index 8818ff9..1940c1a 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -9,15 +9,63 @@ using namespace std;
using namespace chrono_literals;
using namespace odhtdb;
-int main()
+#define assertEquals(a, b) do { if((a) != (b)) { fprintf(stderr, "Assert failed:\n%s != %s\n", #a, #b); exit(1); } } while(0)
+void fail(const string &errMsg) { fprintf(stderr, "Fail:\n%.*s\n", errMsg.size(), errMsg.c_str()); }
+
+void testSignData(LocalUser *localUser)
{
- LocalUser *localUser = LocalUser::create(Signature::KeyPair(), "dec05eba");
-
std::string publicKeyStr = localUser->getPublicKey().toString();
printf("Local user public key: %s\n", publicKeyStr.c_str());
std::string privateKeyStr = localUser->getPrivateKey().toString();
printf("Local user private key: %s\n", privateKeyStr.c_str());
+
+ string expectedUnsignedData = "hello, world!";
+ string signedData = localUser->getPrivateKey().sign(DataView((void*)expectedUnsignedData.data(), expectedUnsignedData.size()));
+ assertEquals(SIGNED_HASH_SIZE + expectedUnsignedData.size(), signedData.size());
+
+ string unsignedData = localUser->getPublicKey().unsign(DataView((void*)signedData.data(), signedData.size()));
+ assertEquals(expectedUnsignedData, unsignedData);
+
+ try
+ {
+ Signature::KeyPair anotherKeyPair;
+ anotherKeyPair.getPublicKey().unsign(DataView((void*)signedData.data(), signedData.size()));
+ fail("Expected unsign to fail since the data was not signed with the private key matching given public key");
+ }
+ catch (UnsignWrongKeyException &e)
+ {
+
+ }
+ catch (exception &e)
+ {
+ string errMsg = "Expected unsign to fail with UnsignWrongKeyException, fail reason: ";
+ errMsg += e.what();
+ fail(errMsg);
+ }
+
+ try
+ {
+ Signature::KeyPair anotherKeyPair;
+ anotherKeyPair.getPublicKey().unsign(DataView((void*)signedData.data(), 3));
+ fail("Expected unsign to fail since the (signed) data is too small to have been signed (signed hash is 64 bytes)");
+ }
+ catch (UnsignInvalidSizeException &e)
+ {
+
+ }
+ catch (exception &e)
+ {
+ string errMsg = "Expected unsign to fail with UnsignInvalidSizeException, fail reason: ";
+ errMsg += e.what();
+ fail(errMsg);
+ }
+}
+
+int main()
+{
+ LocalUser *localUser = LocalUser::create(Signature::KeyPair(), "dec05eba");
+ testSignData(localUser);
// TODO: For tests, dont run against bootstrap.ring.cx.
// Run against a bootstrap node made only for testing which doesn't persist added data.
@@ -25,12 +73,10 @@ int main()
Database database("bootstrap.ring.cx", 4222, "storage");
database.seed();
- Group group("admin");
- group.addUser(localUser);
- database.create("galax.channel.latenight.chat", &group);
+ database.create(localUser, "galax.channel.latenight.chat");
const char *data = "hello, world!";
- database.add("galax.channel.latenight.chat", DataView{ (void*)data, strlen(data) }, localUser);
+ database.add(localUser, "galax.channel.latenight.chat", DataView{ (void*)data, strlen(data) });
database.commit();
auto start = chrono::high_resolution_clock::now();
while(chrono::high_resolution_clock::now() - start < 5s)