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:
| M | README.md | | | 11 | +++++++++++ |
| M | heartbeat.c | | | 255 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
| A | heartbeatConfig.yml | | | 29 | +++++++++++++++++++++++++++++ |
| M | package.yml | | | 1 | + |
| A | sel.c | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | sel.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);