heartbeat

Simple server monitor system using encrypted messages over udp
git clone https://noulin.net/git/heartbeat.git
Log | Files | Refs | README

commit 8e979e27d2cf1f7e266695bca0ce824183ebb28f
parent 4a8eea99501a747ffbde7a3f0f428bd09b02727b
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu, 13 Jul 2023 11:25:03 +0200

Encrypt messages with libsodium (public key encryption), change bin define because of conflict with libsodium

README.md           |  11 +++
heartbeat.c         | 255 +++++++++++++++++++++++++++++++++++++++++-----------
heartbeatConfig.yml |  29 ++++++
package.yml         |   1 +
sel.c               |  62 +++++++++++++
sel.h               |  23 +++++
6 files changed, 328 insertions(+), 53 deletions(-)

Diffstat:
MREADME.md | 11+++++++++++
Mheartbeat.c | 255++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
AheartbeatConfig.yml | 29+++++++++++++++++++++++++++++
Mpackage.yml | 1+
Asel.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asel.h | 23+++++++++++++++++++++++
6 files changed, 328 insertions(+), 53 deletions(-)

diff --git a/README.md b/README.md @@ -4,6 +4,17 @@ # Install +Install libsodium in debian like this: + +``` +git clone https://github.com/jedisct1/libsodium +git checkout stable +./configure +make +make install +``` + + # Configuration # Setup diff --git a/heartbeat.c b/heartbeat.c @@ -45,7 +45,13 @@ change (event). #define agentCfgPath home"/config.yml" #define agentCfgFile uHome agentCfgPath #define binPath "bin/" -#define bin uHome binPath +#define BIN uHome binPath + +// keys +#define SECRET_KEY "secret.bin" +#define PUBLIC_KEY "public.bin" +#define LOGGER_PUBLIC_KEY "loggerPublic.bin" + #define defaultPeriod 1 // TODO #define defaultPeriod 120 @@ -68,7 +74,7 @@ void runcommand(void); // show state void monitor(void); // send state to logger, as logger collect packets from agents -void probe(char *cfgfile); +void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublicFile); /* process arguments on command line */ @@ -78,10 +84,10 @@ int main(int ARGC, char** ARGV) { initLibsheepy(ARGV[0]); //setLogMode(LOG_VERBOSE); - setLogMode(LOG_DATE); + setLogMode(LOG_FUNC); //openProgLogFile(); setLogSymbols(LOG_UTF8); - disableLibsheepyErrorLogs; + //disableLibsheepyErrorLogs; if (argc == 1) { runProbes:; @@ -91,7 +97,21 @@ int main(int ARGC, char** ARGV) { logE("Missing configuration: %s", p); ret 1; } - probe(p); + cleanCharP(sf) = expandHome(uHome home "/" SECRET_KEY); + if (!isPath(sf)) { + logE("Missing configuration: %s", sf); + ret 1; + } + cleanCharP(pf) = expandHome(uHome home "/" PUBLIC_KEY); + if (!isPath(pf)) { + logE("Missing configuration: %s", pf); + ret 1; + } + cleanCharP(lf) = expandHome(uHome home "/" LOGGER_PUBLIC_KEY); + if (!isPath(lf)) { + freen(lf); + } + probe(p, sf, pf, lf); ret 0; } elif (eqG(argv[1], "daemon")) { @@ -146,6 +166,8 @@ void printHelp(void) { TODO("write help"); } +#include "sel.h" + /* generate setup for all agents */ void config(smallJsont *cfg) { @@ -166,12 +188,19 @@ void config(smallJsont *cfg) { // add shell commands to copyScript cleanAllocateSmallArray(copyScript); + const char *loggerName = null; + char *loggerTransfer = null; + char *loggerPublicKey = null; + cleanAllocateSmallArray(keyFilenames); + // id is stored in the agent config and sent in the messages // it is the index of the machine in the list u32 id = 0; iter(cfg, D) { cast(smallDictt*, d, D); //logD("key %s", iK(cfg)); + + // generate configuration file for agent cleanCharP(thisCfgFile) = catS(iK(cfg), "Config.yml"); //lv(thisCfgFile); @@ -185,6 +214,7 @@ void config(smallJsont *cfg) { if (hasG(d, "logger")) { setG(agentCfg, "logger", TRUE); // TODO("add monitor ip and port in logger agent"); + loggerName = iK(cfg); } // set bridge address @@ -204,6 +234,17 @@ void config(smallJsont *cfg) { //lv(thisCfg); writeFileG(thisCfg, thisCfgFile); + // generate keys + cleanCharP(secretFilename) = catS(iK(cfg), "Secret.bin"); + cleanCharP(publicFilename) = catS(iK(cfg), "Public.bin"); + pushG(keyFilenames, secretFilename); + pushG(keyFilenames, publicFilename); + keyst keys = init0Var; + newKeysBuf(&keys); + pError0(writeFile(secretFilename, keys.secretKey, sizeof(keys.secretKey))); + pError0(writeFile(publicFilename, keys.publicKey, sizeof(keys.publicKey))); + + // generate commands to setup the agents pushNFreeG(copyScript, formatS("# %s", iK(cfg))); if (!$(d, "transfers")) { @@ -213,7 +254,22 @@ void config(smallJsont *cfg) { if (hasG(d, "logger")) { pushNFreeG(copyScript, formatS("scp "cfgFile" %s:" home "/" cfgFile, iK(cfg))); + loggerTransfer = "scp"; + loggerPublicKey = $(keyFilenames, -1); } + else { + if (!loggerPublicKey) { + logC("Put logger configuration first in the `"cfgFile"` yml file."); + XFailure; + } + // copy logger public key to agent + pushNFreeG(copyScript, + formatS("scp %s %s:" home "/" LOGGER_PUBLIC_KEY, loggerPublicKey, iK(cfg))); + } + pushNFreeG(copyScript, + formatS("scp %s %s:" home "/" SECRET_KEY, secretFilename, iK(cfg))); + pushNFreeG(copyScript, + formatS("scp %s %s:" home "/" PUBLIC_KEY, publicFilename, iK(cfg))); pushNFreeG(copyScript, formatS("scp %s %s:" agentCfgPath, thisCfgFile, iK(cfg))); pushNFreeG(copyScript, @@ -230,11 +286,26 @@ void config(smallJsont *cfg) { if (hasG(d, "logger")) { pushG(copyScript, "cp "cfgFile" "uHome home "/" cfgFile); + loggerTransfer = "ssh"; + loggerPublicKey = $(keyFilenames, -1); + } + else { + if (!loggerPublicKey) { + logC("Put logger configuration first in the `"cfgFile"` yml file."); + XFailure; + } + // copy logger public key to agent + pushNFreeG(copyScript, + formatS("cp %s " uHome home "/" LOGGER_PUBLIC_KEY, loggerPublicKey)); } pushNFreeG(copyScript, + formatS("cp %s " uHome home "/" SECRET_KEY, secretFilename)); + pushNFreeG(copyScript, + formatS("cp %s " uHome home "/" PUBLIC_KEY, publicFilename)); + pushNFreeG(copyScript, formatS("cp %s " agentCfgFile, thisCfgFile)); pushNFreeG(copyScript, - formatS("cp heartbeat " bin)); + formatS("cp heartbeat " BIN)); pushG(copyScript, "cp heartbeat.service /etc/systemd/system/"); pushG(copyScript, @@ -243,6 +314,14 @@ void config(smallJsont *cfg) { inc id; } + // copy all keys to logger + // logger needs to know the public key for all agents + iter(keyFilenames, F) { + pushNFreeG(copyScript, + eqG(loggerTransfer, "scp") ? formatS("scp %s %s:" home "/", ssGet(F), loggerName) : + formatS("cp %s " uHome home "/", ssGet(F))); + } + logG(copyScript); } @@ -321,7 +400,7 @@ void monitor(void) { server.sin_port = htons(port); if (bind(sock, (struct sockaddr *) &server, sizeof(server))){ - logE("bind failed: %s", strerror(errno)); + logE("bind failed: %s, port %d", strerror(errno), port); XFailure; } @@ -489,7 +568,7 @@ void setTimeout(int bridgesock, u32 period, u32 usec) { /* agent, bridge and logger */ -void probe(char *cfgfile) { +void probe(char *cfgfile, char *secretFile, char *publicFile, char *loggerPublicFile) { // load agent config // get port and agent name @@ -499,7 +578,7 @@ void probe(char *cfgfile) { // create socket for brigde (only if not logger) // start server or bridge for the agents // message structures - // inifinite loop + // infinite loop // check probes // send message // this is the logger @@ -515,6 +594,15 @@ void probe(char *cfgfile) { // send mail // when agent is not logger, forward message + keyst keys = init0Var; + + // load keys + pError(bLReadFile(secretFile, keys.secretKey, sizeof(keys.secretKey))); + pError(bLReadFile(publicFile, keys.publicKey, sizeof(keys.publicKey))); + if (loggerPublicFile) { + pError(bLReadFile(loggerPublicFile, keys.remotePublicKey, sizeof(keys.remotePublicKey))); + } + // complete configuration in logger cleanSmallJsonP(completeCfg) = null; @@ -623,8 +711,9 @@ void probe(char *cfgfile) { pError0(chDir(h)); mailSubject = catS("Heatbeat on ", name); } - // open log files + // open log files and load agent public keys int agentf[lenG(agents)]; + u8 agentPublicKeys[lenG(agents)][sizeof(keys.remotePublicKey)]; if (logger) { arange(i, agentf) { agentf[i] = open(agentdbs[i], O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); @@ -632,17 +721,25 @@ void probe(char *cfgfile) { logE("Failed to open %s: %s", agentdbs[i], strerror(errno)); XFailure; } + cleanCharP(publicFilename) = catS(uHome home "/", agents[i], "Public.bin"); + cleanCharP(pfn) = expandHome(publicFilename); + pError(bLReadFile(pfn, agentPublicKeys[i], sizeof(agentPublicKeys[i]))); } // setup objects for monitor setG(completeCfg, "startTime", startTime); iter(completeCfg, D) { if (!isOSmallDict(D)) continue; cast(smallDictt*,d,D); - setG(d, "state", "down"); - setG(d, "last", startTime); - setG(d, "mId", 0); + setG(d, "state", "down"); // network state + setG(d, "last", startTime); // network state change + setG(d, "mId", 0); // last message id, if next mId under last one, the agent rebooted setG(d, "rebooted", FALSE); setG(d, "lastBoot", startTime); + setG(d, "time", 0); // last incoming message time + setG(d, "c", 0); // packet counter under a period + setG(d, "mono", 0); // current period start time + setG(d, "net", FALSE); // there is a network issue, when agent goes from down to alive and next mId is higher than last mId + setG(d, "lastNet", startTime); cleanFinishSmallArrayP(probes) = getG(d, rtSmallArrayt, "probes"); if (probes) { iter(probes, P) { @@ -686,7 +783,6 @@ void probe(char *cfgfile) { int bridgesock; struct sockaddr_in bridge; struct sockaddr_in client; - char buf[1024]; bridgesock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (bridgesock < 0){ @@ -699,13 +795,19 @@ void probe(char *cfgfile) { bridge.sin_port = htons(port); if (bind(bridgesock, (struct sockaddr *) &bridge, sizeof(bridge))){ - logE("bind failed: %s", strerror(errno)); + logE("Bridge bind failed: %s, port %d", strerror(errno), port); XFailure; } - // message structures + // packet structure typ struct PACKED { + u8 nonce[crypto_box_NONCEBYTES]; u32 id; + i32 len; + u8 buf[64*1024]; // buf holds the encrypted data (it is slightly bigger than the payload) + } packett; + // message structures + typ struct PACKED { u64 messageId; u64 status; u32 info; @@ -713,16 +815,20 @@ void probe(char *cfgfile) { typ struct PACKED { u64 time; msgt m; + } payloadt; + typ struct PACKED { + u64 time; + u32 id; + msgt m; u32 levels[32]; } dbt; - dbt record = init0Var; - - msgt msg = {.id = id}; + packett packet = {.id = id}; // packet to send, id is the agent id + packett data = init0Var; // received packet + msgt msg = init0Var; // message to send + dbt record = init0Var; // record to write on disk in logger //lv(sizeof(msg)); - msg.id = id; - - // inifinite loop + // infinite loop rangeInf(messageId) { //lv(messageId); @@ -769,7 +875,16 @@ void probe(char *cfgfile) { if (not logger) { // send message - if (sendto(sock, &msg, sizeof(msg), 0, (const struct sockaddr *)&server, sizeof(server)) < 0) { + // encrypt message + randombytes_buf(keys.nonce, sizeof(keys.nonce)); + memcpy(packet.nonce, keys.nonce, sizeof(keys.nonce)); + payloadt payload; + // set timestamp in encrypted message to avoid replay attacks + payload.time = getCurrentUnixTime() /*- 3 */; // uncomment to introduce an error + payload.m = msg; + packet.len = selPublicEncrypt(packet.buf, sizeof(packet.buf), (u8*)&payload, sizeof(payload), &keys); + // send message + if (sendto(sock, &packet, packet.len + sizeof(packet.nonce) + sizeof(packet.id) + sizeof(packet.len), 0, (const struct sockaddr *)&server, sizeof(server)) < 0) { logE("send failed: %s", strerror(errno)); close(sock); XFailure; @@ -782,7 +897,7 @@ void probe(char *cfgfile) { record.m = msg; record.time = getCurrentUnixTime(); cleanFinishSmallDictP(logAgent) = getG(completeCfg, rtSmallDictt, name); - setG(logAgent, "time", getCurrentUnixTime()); + setG(logAgent, "time", record.time); char *newstate = messageId ? "alive" : "init"; if (!eqG($(logAgent, "state"), newstate)) { saveEvent = yes; @@ -791,7 +906,7 @@ void probe(char *cfgfile) { /* newstate, */ /* $(logAgent,"state")); */ setG(logAgent, "state", newstate); - setG(logAgent, "last", getCurrentUnixTime()); + setG(logAgent, "last", record.time); } cleanAllocateSmallArray(mailMsg); @@ -804,7 +919,7 @@ void probe(char *cfgfile) { if (getG(p, rtBool, "state") != newstate) { saveEvent = yes; setG(p, "state", newstate); - setG(p, "last", getCurrentUnixTime()); + setG(p, "last", record.time); // send mail when service is down char *state = newstate ? "down" : "up"; char *result = newstate ? "failed" : "ok"; @@ -869,40 +984,72 @@ void probe(char *cfgfile) { setTimeout(bridgesock, period, 0); listenToOthers:; socklen_t addr_size = sizeof(client); - memset(buf, 0, sizeof(buf)); - ssize_t r = recvfrom(bridgesock, buf, sizeof(buf), 0, (struct sockaddr *) &client, &addr_size); + memset(&data, 0, sizeof(data)); + ssize_t r = recvfrom(bridgesock, &data, sizeof(data), 0, (struct sockaddr *) &client, &addr_size); if (r == -1) { // timeout goto cont; } - // TODO check packet size is correct + elif (r != data.len + sizeof(data.nonce) + sizeof(data.id) + sizeof(data.len)) { + logE("Dropped packet with id %d. Incorrect size, received %d and expected %d", data.id, r, data.len + sizeof(data.nonce) + sizeof(data.id) + sizeof(data.len)); + goto cont; + } else { // got a message + // forward message when acting as a bridge //lv(r); - cast(msgt *, m, buf); - /* logD("got message from id %d", m->id); */ - /* logD("with message id %d", m->messageId); */ - /* logD("and status %x", m->status); */ if (logger) { // when agent is logger, store message //logD("store message"); - if (m->id >= ARRAY_SIZE(agentf)) { + if (data.id >= ARRAY_SIZE(agentf)) { char *ip = inet_ntoa((client).sin_addr); - logE("Invalid id: %d, ip %s, packet size %d, packet content:\n%s", m->id, ip, buf); + logE("Invalid id: %d, ip %s, packet size %d", data.id, ip, r); + // drop packet } - elif (m->id == id) { + elif (data.id == id) { char *ip = inet_ntoa((client).sin_addr); logE("Invalid id, the logger doesn't send packets, ip %s", ip); + // drop packet } else { + // agent id is valid + // decrypt message + // copy agent public key to keys.remotePublicKey + memcpy(keys.remotePublicKey, agentPublicKeys[data.id], sizeof(keys.remotePublicKey)); + + // copy nonce + memcpy(keys.nonce, data.nonce, sizeof(keys.nonce)); + + payloadt payload; + int len = selPublicDecrypt((u8*)&payload, sizeof(payload), (u8*)&data.buf, data.len, &keys); + + if (!len) { + logE("failed to decrypt"); + // drop packet + goto handleEventLoopSleep; + } + + // packet is valid for 2 seconds + u64 now = getCurrentUnixTime(); + if (payload.time < now - 2 or payload.time > now + 2) { + logW("Dropping packet. Wrong timestamp %"PRIu64" now %"PRIu64" diff %"PRIi64, payload.time, now, (i64)now - (i64)payload.time); + goto handleEventLoopSleep; + } + + // message is decrypted + msgt *m = &payload.m; + /* logD("got message from id %d", data.id); */ + /* logD("with message id %d", m->messageId); */ + /* logD("and status %x", m->status); */ + bool saveEvent = no; record.m = *m; - record.time = getCurrentUnixTime(); + record.time = now; cleanAllocateSmallArray(mailMsg); // update state for this agent - cleanFinishSmallDictP(agent) = getG(completeCfg, rtSmallDictt, agents[m->id]); + cleanFinishSmallDictP(agent) = getG(completeCfg, rtSmallDictt, agents[data.id]); // count packets in each period, if more than 2 packets arrive during a period, // something is wrong. // Don't measure time between packet because @@ -921,20 +1068,20 @@ void probe(char *cfgfile) { inc *c; if (*c > 2) { char *ip = inet_ntoa((client).sin_addr); - logW("Too many packets from agent %s and ip %s, drop packet.", agents[m->id], ip); + logW("Too many packets from agent %s and ip %s, drop packet.", agents[data.id], ip); goto cont; } } } - bool loggerRunning = hasG(agent, "time"); - setG(agent, "time", getCurrentUnixTime()); + bool loggerRunning = u$(agent, "time"); + setG(agent, "time", now); char *newstate = m->messageId ? "alive" : "init"; - if (m->messageId < u$(agent,"mId")) { + if (m->messageId < u$(agent," // last message id, if next mId under last one, the agent rebootedmId")) { saveEvent = yes; setG(agent, "rebooted", TRUE); - setG(agent, "lastBoot", getCurrentUnixTime()); + setG(agent, "lastBoot", now); // send mail when agent rebooted - cleanCharP(s) = formatS("%s rebooted", agents[m->id]); + cleanCharP(s) = formatS("%s rebooted", agents[data.id]); pushG(mailMsg, s); logW("%s", s); } @@ -949,9 +1096,9 @@ void probe(char *cfgfile) { // in that case mails are not sent because loggerRunning is false saveEvent = yes; setG(agent, "net", TRUE); - setG(agent, "lastNet", getCurrentUnixTime()); + setG(agent, "lastNet", now); // send mail when agent has network issues - cleanCharP(s) = formatS("%s has network issues", agents[m->id]); + cleanCharP(s) = formatS("%s has network issues", agents[data.id]); pushG(mailMsg, s); logW("%s", s); } @@ -960,7 +1107,7 @@ void probe(char *cfgfile) { if (!eqG($(agent, "state"), newstate)) { saveEvent = yes; setG(agent, "state", newstate); - setG(agent, "last", getCurrentUnixTime()); + setG(agent, "last", now); } cleanFinishSmallArrayP(probes) = getG(agent, rtSmallArrayt, "probes"); if (probes) { @@ -970,22 +1117,22 @@ void probe(char *cfgfile) { if (getG(p, rtBool, "state") != newstate) { saveEvent = yes; setG(p, "state", newstate); - setG(p, "last", getCurrentUnixTime()); + setG(p, "last", now); // send mail when service is down char *state = newstate ? "down" : "up"; char *result = newstate ? "failed" : "ok"; if (hasG(p, "port")) { - cleanCharP(s) = formatS("Service on port %d running in %s is %s", u$(p, "port"), agents[m->id], state); + cleanCharP(s) = formatS("Service on port %d running in %s is %s", u$(p, "port"), agents[data.id], state); pushG(mailMsg, s); logW("%s", s); } elif (hasG(p, "if")) { - cleanCharP(s) = formatS("Network interface %s in %s is down", u$(p, "if"), agents[m->id]); + cleanCharP(s) = formatS("Network interface %s in %s is down", u$(p, "if"), agents[data.id]); pushG(mailMsg, s); logW("%s", s); } elif (hasG(p, "cmd")) { - cleanCharP(s) = formatS("'%s' running in %s is %s", u$(p, "cmd"), agents[m->id], result); + cleanCharP(s) = formatS("'%s' running in %s is %s", u$(p, "cmd"), agents[data.id], result); pushG(mailMsg, s); logW("%s", s); } @@ -994,7 +1141,7 @@ void probe(char *cfgfile) { } // if probes if (saveEvent) { - write(agentf[m->id], &record, sizeof(record)); + write(agentf[data.id], &record, sizeof(record)); } // send mail @@ -1019,12 +1166,13 @@ void probe(char *cfgfile) { else { // when agent is not logger, forward message //logD("forward message"); - if (sendto(sock, &buf, r, 0, (const struct sockaddr *)&server, sizeof(server)) < 0) { + if (sendto(sock, &data, r, 0, (const struct sockaddr *)&server, sizeof(server)) < 0) { logE("send failed: %s", strerror(errno)); close(sock); XFailure; } } + handleEventLoopSleep: time = getMonotonicTime(); if (time < nextTime) { // adjust timeout to have period delay between the sent messages from this agent @@ -1051,6 +1199,7 @@ void probe(char *cfgfile) { } /* send mail in another process + * fork before sending mail to not block the event loop */ void sendMail(char *cmd) { pid_t pid; diff --git a/heartbeatConfig.yml b/heartbeatConfig.yml @@ -0,0 +1,29 @@ +port: 2000 +# each agent need to have a configuration in ~/.ssh/config +# gemini://gmi.noulin.net/2023-02-28-ssh-configurations.gmi +logger: # agent name + address: "192.168.1.2" + logger: true + # transfers is used in the config command + # to copy the agent setups + transfers: "copy" # default is ssh/scp + # monitorIp: localhost + monitorPort: 2001 + probes: + - port: 80 + mails: + - 'admin@example.com' +agent1: + address: "192.168.1.3" # used for bridge configuration + probes: + - port: 80 + bridge: "logger" + mails: + - 'admin@example.com' +agent2: + address: "192.168.1.4" + probes: + - port: 80 + bridge: "agent1" # send messages through agent1 + mails: + - 'admin@example.com' diff --git a/package.yml b/package.yml @@ -2,6 +2,7 @@ name: heartbeat version: 0.0.1 description: explanation + lflags: /usr/local/lib/libsodium.a bin: ./heartbeat.c repository: type: git diff --git a/sel.c b/sel.c @@ -0,0 +1,62 @@ +#include "sel.h" + +// detect entropy quality +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <linux/random.h> + +#include <iso646.h> /* and or not defines */ + +int selInit(void) { + // detect entropy quality + int urandomfd; + if ((urandomfd = open("/dev/urandom", O_RDONLY)) != -1) { + int c; + if (ioctl(urandomfd, RNDGETENTCNT, &c) == 0 && c < 160) { + /* logN("This system doesn't provide enough entropy to quickly generate high-quality random numbers.\n" */ + /* "Installing the rng-utils/rng-tools, jitterentropy or haveged packages may help.\n" */ + /* "On virtualized Linux environments, also consider using virtio-rng.\n" */ + /* "The service will not start until enough entropy has been collected.\n", stderr); */ + close(urandomfd); + return 0; + } + } + close(urandomfd); + if (sodium_init() == -1) { + /* logC("Panic! libsodium couldn't be initialized; it is not safe to use"); */ + return 0; + } + return 1; +} + +void newKeysBuf(keyst *keys) { + crypto_box_keypair(keys->publicKey, keys->secretKey); + /* logD("Public key"); */ + /* loghex(keys->publicKey, sizeof(keys->publicKey)); */ + /* put; */ + /* logD("Secret key"); */ + /* loghex(keys->secretKey, sizeof(keys->secretKey)); */ + /* put; */ +} + +// return ciphertext (encrypted message) length +int selPublicEncrypt(u8 *ciphertext/*result*/, size_t csize, const u8 *msg, size_t mlen, keyst *keys) { + // csize is ciphertext buffer size + // check is there is enough space in ciphertext + if (csize < mlen + crypto_box_MACBYTES) return 0; + if (crypto_box_easy(ciphertext, msg, mlen, keys->nonce, keys->remotePublicKey, keys->secretKey) != 0) return 0; + return mlen + crypto_box_MACBYTES; +} + +// return message length +int selPublicDecrypt(u8 *msg/*result*/, size_t msize, const u8 *ciphertext, size_t clen, keyst *keys) { + // msize is message buffer size + // check ciphertext has minimal length, the message has to be at least one byte + // check is there is enough space in message buffer + if (clen <= crypto_box_MACBYTES or msize < clen - crypto_box_MACBYTES) return 0; + if (crypto_box_open_easy(msg, ciphertext, clen, keys->nonce, keys->remotePublicKey, keys->secretKey) != 0) return 0; + return clen - crypto_box_MACBYTES; +} + +// vim: set expandtab ts=2 sw=2: diff --git a/sel.h b/sel.h @@ -0,0 +1,23 @@ +#pragma once + +#include "sodium.h" + +#ifndef u8 +#define u8 uint8_t +#endif + +typedef struct { + u8 publicKey[crypto_box_PUBLICKEYBYTES]; + u8 secretKey[crypto_box_SECRETKEYBYTES]; + u8 remotePublicKey[crypto_box_PUBLICKEYBYTES]; + u8 nonce[crypto_box_NONCEBYTES]; +} keyst; + +/* +These functions return 0 when they fail. +*/ + +int selInit(void); +void newKeysBuf(keyst *keys); +int selPublicEncrypt(u8 *ciphertext/*result*/, size_t csize, const u8 *msg, size_t mlen, keyst *keys); +int selPublicDecrypt(u8 *msg/*result*/, size_t msize, const u8 *ciphertext, size_t clen, keyst *keys);