Initial commit
This commit is contained in:
commit
34be706660
|
@ -0,0 +1,16 @@
|
|||
.dub
|
||||
docs.json
|
||||
__dummy.html
|
||||
docs/
|
||||
configs/
|
||||
/dropper
|
||||
dropper.so
|
||||
dropper.dylib
|
||||
dropper.dll
|
||||
dropper.a
|
||||
dropper.lib
|
||||
dropper-test-*
|
||||
*.exe
|
||||
*.o
|
||||
*.obj
|
||||
*.lst
|
|
@ -0,0 +1,44 @@
|
|||
# Dropper
|
||||
|
||||
The dropper is part the of the deaddrop project.
|
||||
|
||||
It is used to post / fetch encrypted messages to / from the
|
||||
drop server.
|
||||
|
||||
## Required software:
|
||||
|
||||
- dmd
|
||||
- dub
|
||||
- libsodium
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
$ git clone http://repourl/dropper.git dropper
|
||||
$ cd dropper
|
||||
$ dub build --build=release
|
||||
```
|
||||
|
||||
## Simple usage:
|
||||
|
||||
Key generation:
|
||||
```bash
|
||||
$ ./dropper -w ./key_dir/alice
|
||||
```
|
||||
|
||||
Sending a message
|
||||
- specify the base64 encoded publickey of the recipient with -r
|
||||
- load the previously generated keys from disk with -c
|
||||
- specify the message to send with -m
|
||||
- specify the url of the drop server with -u
|
||||
|
||||
```bash
|
||||
$ ./dropper -c ./key_dir/alice -m "Hello Bob" -r "3e51N4....pHI=" -u "http://dropperserver.url"
|
||||
```
|
||||
|
||||
Getting all messages for a public key:
|
||||
- specify that we want to fetch messages with -f
|
||||
- load the previously generated keys from disk with -c
|
||||
```bash
|
||||
$ ./dropper -f -c ./key_dir/bob -u "http://dropperserver.url"
|
||||
```
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"authors": [
|
||||
"anon"
|
||||
],
|
||||
"copyright": "Copyright © 2021, anon",
|
||||
"dependencies": {
|
||||
"libsodiumd": "~>0.1.1"
|
||||
},
|
||||
"description": "A minimal D application.",
|
||||
"license": "proprietary",
|
||||
"name": "dropper"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fileVersion": 1,
|
||||
"versions": {
|
||||
"libsodiumd": "0.1.1+1.0.18"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
import std.stdio;
|
||||
import std.getopt;
|
||||
import userinfo;
|
||||
import message;
|
||||
import cryptoclass;
|
||||
import networker;
|
||||
|
||||
void generateAndSaveKeys(string write_path)
|
||||
{
|
||||
UserInfo user;
|
||||
|
||||
writeln("[*] Generating keys...");
|
||||
|
||||
generateKeysForUser(user);
|
||||
writeln("[+] Generated user-keys:");
|
||||
writeln(">> Public-Key: ", user.b64_publickey);
|
||||
writeln(">> Private-Key: ", user.b64_privatekey);
|
||||
|
||||
writeln("[*] Saving keys to files...");
|
||||
saveKeysToFile(user, write_path);
|
||||
writeln("[+] Saved keys into files: ");
|
||||
writeln("Public-Key -> ", write_path ~ ".pub");
|
||||
writeln("Private-Key -> ", write_path ~ ".priv");
|
||||
}
|
||||
|
||||
UserInfo loadConfigs(string config_path)
|
||||
{
|
||||
immutable string priv_key_file = config_path ~ ".priv";
|
||||
immutable string pub_key_file = config_path ~ ".pub";
|
||||
|
||||
// Load the users keys
|
||||
writeln("[*] Loading keys from ", config_path);
|
||||
UserInfo user = loadUserInfoFromFile(priv_key_file, pub_key_file);
|
||||
writeln("[+] Loaded keys:");
|
||||
writeln("\t->", priv_key_file);
|
||||
writeln("\t->", pub_key_file);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
void main(string[] args)
|
||||
{
|
||||
string write_path;
|
||||
string config_path;
|
||||
string message;
|
||||
string b64_recipient_pubkey;
|
||||
string server_url;
|
||||
|
||||
bool fetch = false;
|
||||
|
||||
auto flags = getopt(args,
|
||||
std.getopt.config.bundling,
|
||||
"w|write", "Path to generate and write keys to. ( ./config/alice )", &write_path,
|
||||
"c|config", "File prefix to load key files from. ( ./configs/alice )", &config_path,
|
||||
"m|messsage", "The message that should be sent. ( \"Hi bob how are you?\" ) ", &message,
|
||||
"f|fetch", "If this option is set the dropper only fetches messages for the public-key from -config", &fetch,
|
||||
"r|recipient", "Base64 encoded public-key of the recipient", &b64_recipient_pubkey,
|
||||
"u|url", "URL of the deaddrop server.", &server_url);
|
||||
|
||||
if (write_path != "")
|
||||
{
|
||||
generateAndSaveKeys(write_path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fetch && config_path != "" && server_url != "")
|
||||
{
|
||||
UserInfo user = loadConfigs(config_path);
|
||||
Cryptoclass crypto = new Cryptoclass(user);
|
||||
Networker networker = new Networker(server_url);
|
||||
|
||||
writeln("\n📧📧📧 You've got mail 📧📧📧\n");
|
||||
|
||||
auto list = networker.getMessagesForRecipient(user.b64_publickey);
|
||||
foreach (single_message; list)
|
||||
{
|
||||
auto bin_msg = crypto.decryptMessage(single_message["payload"]);
|
||||
writeln(cast(string)bin_msg);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (flags.helpWanted
|
||||
|| config_path == ""
|
||||
|| message == ""
|
||||
|| b64_recipient_pubkey == ""
|
||||
|| server_url == "")
|
||||
{
|
||||
defaultGetoptPrinter("Dropper to post and get messages from a deaddrop server.",
|
||||
flags.options);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the configs
|
||||
UserInfo user = loadConfigs(config_path);
|
||||
|
||||
// Create the crypto class
|
||||
Cryptoclass crypto = new Cryptoclass(user);
|
||||
Networker networker = new Networker(server_url);
|
||||
|
||||
// Timestamp the message
|
||||
string timestamped_msg = prependTimeStamp(message);
|
||||
|
||||
// Encrypt the message
|
||||
writeln("[*] Encrypting message...");
|
||||
auto binary_payload = crypto.encryptMessage(b64_recipient_pubkey, timestamped_msg);
|
||||
writeln("[*] Encoding payload...");
|
||||
string encoded_payload = encodePayload(binary_payload);
|
||||
|
||||
// Format the message for a post request
|
||||
writeln("[*] Formatting message...");
|
||||
Message msg = formatMessage(b64_recipient_pubkey, encoded_payload);
|
||||
|
||||
// Post message to server
|
||||
writeln("[!] Posting message to server ", server_url);
|
||||
networker.postMessage(msg);
|
||||
writeln("[+] Done :^)");
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
module cryptoclass;
|
||||
|
||||
import userinfo : UserInfo;
|
||||
|
||||
/// This class handles the encryption and
|
||||
/// decryption of messages to the `UserInfo`
|
||||
/// that was specified while creating this class
|
||||
class Cryptoclass
|
||||
{
|
||||
private:
|
||||
import libsodium;
|
||||
import std.stdio : stderr;
|
||||
import std.base64 : Base64;
|
||||
import core.stdc.stdlib : exit, EXIT_FAILURE;
|
||||
|
||||
|
||||
UserInfo p_me;
|
||||
|
||||
public:
|
||||
|
||||
/// This constructor sets the `UserInfo` for the crypto class
|
||||
/// that is used to decrypt messages. It also makes sure
|
||||
/// that libsodium is loaded and initialized
|
||||
this(UserInfo user)
|
||||
{
|
||||
// Load libsodium
|
||||
if (sodium_init() < 0)
|
||||
{
|
||||
stderr.writeln("[-] Failed to load libsodium! Is it installed?");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
this.p_me = user;
|
||||
}
|
||||
|
||||
/// This function encrypts messages for the base64 encoded public
|
||||
/// key that is passed to the function and returns a ubyte array
|
||||
/// containing the payload
|
||||
ubyte[] encryptMessage(string b64_recipientPublicKey, string message)
|
||||
{
|
||||
import std.conv : to;
|
||||
|
||||
ubyte[] messageBin = cast(ubyte[])message;
|
||||
ubyte[] cipherText = new ubyte[(crypto_box_SEALBYTES + messageBin.length)];
|
||||
ubyte[] recipientPublicKey = Base64.decode(b64_recipientPublicKey);
|
||||
|
||||
crypto_box_seal(cipherText.ptr, messageBin.ptr, messageBin.length, recipientPublicKey.ptr);
|
||||
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
/// This function decrypts a given base64 encoded
|
||||
/// payload string using the internal userinfos
|
||||
/// public and private key and returns
|
||||
/// the decrypted buffer as a ubyte array
|
||||
ubyte[] decryptMessage(string messagePayload)
|
||||
{
|
||||
ubyte[] payloadBinary = Base64.decode(messagePayload);
|
||||
ubyte[] decrypted = new ubyte[payloadBinary.length];
|
||||
|
||||
ubyte[crypto_box_PUBLICKEYBYTES] pubkey = p_me.getPublicKey();
|
||||
ubyte[crypto_box_SECRETKEYBYTES] seckey = p_me.getPrivateKey();
|
||||
|
||||
if (crypto_box_seal_open(
|
||||
decrypted.ptr,
|
||||
payloadBinary.ptr,
|
||||
payloadBinary.length,
|
||||
pubkey.ptr,
|
||||
seckey.ptr
|
||||
) != 0 ) {
|
||||
stderr.writeln("[!] Failed to open sealed message!!!");
|
||||
}
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
module message;
|
||||
|
||||
alias Message = string[string];
|
||||
|
||||
string encodePayload(ubyte[] payload)
|
||||
{
|
||||
import std.base64 : Base64;
|
||||
return Base64.encode(payload);
|
||||
}
|
||||
|
||||
Message formatMessage(string recipient, string payload)
|
||||
{
|
||||
return [
|
||||
"recipient": recipient,
|
||||
"payload": payload
|
||||
];
|
||||
}
|
||||
|
||||
string prependTimeStamp(string message)
|
||||
{
|
||||
import std.datetime : Clock;
|
||||
import std.conv : to;
|
||||
|
||||
string utc = to!string(Clock.currTime.toUTC());
|
||||
return utc ~ " >> " ~ message;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
module networker;
|
||||
|
||||
import message;
|
||||
|
||||
class Networker
|
||||
{
|
||||
private:
|
||||
import std.net.curl : get, post;
|
||||
|
||||
string p_serverUrl;
|
||||
public:
|
||||
|
||||
this(string serverUrl)
|
||||
{
|
||||
this.p_serverUrl = serverUrl;
|
||||
}
|
||||
|
||||
void postMessage(Message msg)
|
||||
{
|
||||
auto content = post(p_serverUrl ~ "/add-message", msg);
|
||||
}
|
||||
|
||||
Message[] getMessagesForRecipient(string b64_publickey)
|
||||
{
|
||||
import std.conv : to;
|
||||
import std.json : parseJSON, JSONValue, JSONException;
|
||||
|
||||
Message[] msgs;
|
||||
|
||||
auto queryParams = ["pubkey": b64_publickey];
|
||||
auto content = post(p_serverUrl ~ "/getmessages", queryParams);
|
||||
|
||||
auto json = parseJSON(to!string(content));
|
||||
foreach (msg_json; json.array)
|
||||
{
|
||||
try
|
||||
{
|
||||
Message single_msg = [
|
||||
"recipient": msg_json["recipient"].str,
|
||||
"payload": msg_json["payload"].str
|
||||
];
|
||||
|
||||
msgs ~= single_msg;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
module userinfo;
|
||||
|
||||
import std.base64 : Base64;
|
||||
|
||||
struct UserInfo
|
||||
{
|
||||
public:
|
||||
string b64_publickey;
|
||||
string b64_privatekey;
|
||||
|
||||
ubyte[] getPublicKey()
|
||||
{
|
||||
return Base64.decode(b64_publickey);
|
||||
}
|
||||
|
||||
ubyte[] getPrivateKey()
|
||||
{
|
||||
return Base64.decode(b64_privatekey);
|
||||
}
|
||||
}
|
||||
|
||||
void generateKeysForUser(out UserInfo user)
|
||||
{
|
||||
import libsodium : crypto_box_keypair, crypto_box_PUBLICKEYBYTES, crypto_box_SECRETKEYBYTES;
|
||||
import std.stdio : stderr;
|
||||
|
||||
ubyte[crypto_box_PUBLICKEYBYTES] publicKey;
|
||||
ubyte[crypto_box_SECRETKEYBYTES] privateKey;
|
||||
|
||||
if (crypto_box_keypair(publicKey.ptr, privateKey.ptr) != 0)
|
||||
{
|
||||
stderr.writeln("[-] Failed to generate keypair!");
|
||||
}
|
||||
|
||||
user.b64_publickey = Base64.encode(publicKey);
|
||||
user.b64_privatekey = Base64.encode(privateKey);
|
||||
}
|
||||
|
||||
UserInfo loadUserInfoFromFile(string privateKeyFile, string publicKeyFile)
|
||||
{
|
||||
import std.file : readText;
|
||||
|
||||
UserInfo user;
|
||||
|
||||
user.b64_publickey = readText(publicKeyFile);
|
||||
user.b64_privatekey = readText(privateKeyFile);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
void saveKeysToFile(UserInfo user, string file_name_prefix)
|
||||
{
|
||||
import std.file : write;
|
||||
|
||||
write(file_name_prefix ~ ".pub", user.b64_publickey);
|
||||
write(file_name_prefix ~ ".priv", user.b64_privatekey);
|
||||
}
|
||||
|
||||
UserInfo createRecipient(string b64_publicKey)
|
||||
{
|
||||
UserInfo recipient;
|
||||
recipient.b64_publickey = b64_publicKey;
|
||||
recipient.b64_privatekey = "";
|
||||
|
||||
return recipient;
|
||||
}
|
Loading…
Reference in New Issue