aboutsummaryrefslogtreecommitdiffstats
path: root/serve/deliver.c
diff options
context:
space:
mode:
Diffstat (limited to 'serve/deliver.c')
-rw-r--r--serve/deliver.c94
1 files changed, 65 insertions, 29 deletions
diff --git a/serve/deliver.c b/serve/deliver.c
index 99f2f53..36b39f4 100644
--- a/serve/deliver.c
+++ b/serve/deliver.c
@@ -49,8 +49,13 @@ struct PureWire {
size_t answer_head, answer_count, answer_capacity;
Answer taken; /* the one the owner took last, kept until it takes the next */
uint32_t placed[2 * PURE_MODULE_FIELDS];
- bool sealed; /* the sends go sealed to peer's key */
- unsigned char peer[PURE_SEAL_KEY];
+ /* Sealed sends: to peer's key, authenticated by the node's own key when
+ * it has one (its public half is from), in one session made at the first
+ * seal (and again after one fails); enc opens it. */
+ bool sealed;
+ unsigned char peer[PURE_SEAL_KEY], from[PURE_SEAL_KEY], enc[PURE_SEAL_KEY];
+ const PureSealKey *own;
+ PureSealer *sealer;
};
/* TLS verifies a receiver's certificate against the system's CAs or the
@@ -518,11 +523,57 @@ static bool form_answer(const PureWire *w, int status, const Text *body, const P
static int send_request(PureWire *w, const char *request, size_t size, const Pending *p, Answer *answer);
-void pure_wire_seal(PureWire *w, const unsigned char peer[PURE_SEAL_KEY]) {
+void pure_wire_seal(PureWire *w, const unsigned char peer[PURE_SEAL_KEY], const PureSealKey *own,
+ const unsigned char *from) {
memcpy(w->peer, peer, PURE_SEAL_KEY);
+ w->own = own;
+ if (own) memcpy(w->from, from, PURE_SEAL_KEY);
w->sealed = true;
}
+/* count sends from p as one body: one object, or an array of them. */
+static void put_body(unsigned char *at, const Pending *p, unsigned count) {
+ for (unsigned i = 0; i < count; ++i) {
+ if (count > 1) *at++ = i ? ',' : '[';
+ memcpy(at, p[i].body, p[i].size);
+ at += p[i].size;
+ }
+ if (count > 1) *at = ']';
+}
+
+static void hex(const unsigned char *bytes, size_t size, char *out) {
+ static const char digits[] = "0123456789abcdef";
+ for (size_t i = 0; i < size; ++i) out[2 * i] = digits[bytes[i] >> 4], out[2 * i + 1] = digits[bytes[i] & 15];
+ out[2 * size] = 0;
+}
+
+/* The body sealed whole in the wire's session, bound to its source, first
+ * number and count; the X-Pure-Seal line that opens it (suite, session,
+ * number, and the sender's key when it sealed with its own) into line. */
+static unsigned char *seal_body(PureWire *w, const Pending *p, unsigned count, size_t body, char *line, size_t size) {
+ unsigned char *plain = malloc(body), *sealed = malloc(body + PURE_SEAL_OVERHEAD);
+ char aad[PURE_SOURCE + 48];
+ snprintf(aad, sizeof(aad), "%s %llu %u", w->source, (unsigned long long)p->seq, count);
+ uint64_t n = 0;
+ if (plain && sealed) {
+ put_body(plain, p, count);
+ if (!w->sealer) w->sealer = pure_sealer(w->peer, w->own, w->enc);
+ }
+ bool ok = plain && sealed && w->sealer && pure_seal(w->sealer, aad, plain, body, sealed, &n);
+ free(plain);
+ if (!ok) {
+ pure_sealer_free(w->sealer); /* the next try makes a new session */
+ w->sealer = NULL;
+ free(sealed);
+ return NULL;
+ }
+ char enc[2 * PURE_SEAL_KEY + 1], from[2 * PURE_SEAL_KEY + 2] = "";
+ hex(w->enc, PURE_SEAL_KEY, enc);
+ if (w->own) from[0] = ' ', hex(w->from, PURE_SEAL_KEY, from + 1);
+ snprintf(line, size, "X-Pure-Seal: %s %s %llu%s\r\n", pure_seal_suite(), enc, (unsigned long long)n, from);
+ return sealed;
+}
+
/* One request for count sends from p (numbered in a row): one object, or
* an array of them for a receiver that takes batches. A final answer to a
* send whose answers are wired is made into *answer. */
@@ -530,19 +581,17 @@ static int post(PureWire *w, const Pending *p, unsigned count, Answer *answer) {
size_t body = count > 1 ? count + 1 : 0;
for (unsigned i = 0; i < count; ++i) body += p[i].size;
size_t length = body + (w->sealed ? PURE_SEAL_OVERHEAD : 0);
- char batch[32] = "", seal[48] = "";
+ char batch[32] = "", seal[256] = "";
if (count > 1) snprintf(batch, sizeof(batch), "X-Pure-Count: %u\r\n", count);
- if (w->sealed) snprintf(seal, sizeof(seal), "X-Pure-Seal: %s\r\n", pure_seal_suite());
+ unsigned char *sealed = w->sealed ? seal_body(w, p, count, body, seal, sizeof(seal)) : NULL;
+ if (w->sealed && !sealed) return AGAIN;
/* The request in one piece, its head and then a batch in its brackets:
* one write where there were two a send (a batch drained at 91k sends a
- * second, and at 470k in one piece). Sealed, the body is sealed whole,
- * bound to its source, first number and count. */
- enum { HEAD = 2048 }; /* the longest path, host and source leave room to spare */
+ * second, and at 470k in one piece). */
+ enum { HEAD = 2048 }; /* the longest path, host, source and seal leave room to spare */
char *request = malloc(HEAD + length);
- unsigned char *plain = w->sealed ? malloc(body) : NULL;
- if (!request || (w->sealed && !plain)) {
- free(request);
- free(plain);
+ if (!request) {
+ free(sealed);
return AGAIN;
}
int head = snprintf(request, HEAD,
@@ -550,23 +599,9 @@ static int post(PureWire *w, const Pending *p, unsigned count, Answer *answer) {
"X-Pure-Source: %s\r\nX-Pure-Sequence: %llu\r\n%s%sIdempotency-Key: %s-%llu\r\n\r\n",
w->path, w->host, w->port, length, w->source, (unsigned long long)p->seq, batch, seal, w->source,
(unsigned long long)p->seq);
- unsigned char *at = plain ? plain : (unsigned char *)request + head;
- for (unsigned i = 0; i < count; ++i) {
- if (count > 1) *at++ = i ? ',' : '[';
- memcpy(at, p[i].body, p[i].size);
- at += p[i].size;
- }
- if (count > 1) *at++ = ']';
- if (plain) {
- char aad[PURE_SOURCE + 48];
- snprintf(aad, sizeof(aad), "%s %llu %u", w->source, (unsigned long long)p->seq, count);
- bool sealed = pure_seal(w->peer, aad, plain, body, (unsigned char *)request + head);
- free(plain);
- if (!sealed) {
- free(request);
- return AGAIN;
- }
- }
+ if (sealed) memcpy(request + head, sealed, length);
+ else put_body((unsigned char *)request + head, p, count);
+ free(sealed);
int result = send_request(w, request, (size_t)head + length, p, answer);
free(request);
return result;
@@ -706,6 +741,7 @@ void pure_wire_close(PureWire *w) {
pthread_mutex_unlock(&w->lock);
if (w->started) pthread_join(w->thread, NULL);
pure_tls_close(w->session);
+ pure_sealer_free(w->sealer);
if (w->fd >= 0) close(w->fd);
for (size_t i = 0; i < w->count; ++i) {
free(w->queue[w->head + i].body);