Browse Source

The client side of the toy protocol

Ian Goldberg 3 years ago
parent
commit
7652d7627b
5 changed files with 205 additions and 2 deletions
  1. 7 2
      Makefile
  2. 121 0
      pirclient.cc
  3. 27 0
      pirclient.h
  4. 50 0
      protocol_client
  5. 0 0
      protocol_server

+ 7 - 2
Makefile

@@ -1,10 +1,15 @@
 CXXFLAGS=-g -Wall
 
+all: toyserver toyclient
+
 toyserver: toyserver.o pirserver.o
 	$(CXX) -Wall -o $@ $^
 
+toyclient: toyclient.o pirclient.o
+	$(CXX) -Wall -o $@ $^
+
 clean:
-	-rm toyserver.o pirserver.o
+	-rm toyserver.o pirserver.o toyclient.o pirclient.o
 
 veryclean: clean
-	-rm toyserver
+	-rm toyserver toyclient

+ 121 - 0
pirclient.cc

@@ -0,0 +1,121 @@
+// Implementation of the main loop of the pirserver, responsible for the
+// communication with the tor process.  All the actual private lookup
+// work is done by an appropriate subclass of PIRServer.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include "pirclient.h"
+
+#define PIRCLIENT_HDR_SIZE 13
+
+#define PIRCLIENT_REQUEST_CREATE 0x41
+#define PIRCLIENT_REQUEST_EXTRACT 0x42
+
+#define PIRCLIENT_RESPONSE_CREATE 0xBF
+#define PIRCLIENT_RESPONSE_EXTRACT 0xBE
+
+static int
+read_all(char *buf, size_t len)
+{
+    int tot_read = 0;
+    while(len > 0) {
+        int res = read(0, buf, len);
+        if (res <= 0) return res;
+        buf += res;
+        len -= res;
+        tot_read += res;
+    }
+    return tot_read;
+}
+
+static int
+write_all(const char *buf, size_t len)
+{
+    int tot_written = 0;
+    while(len > 0) {
+        int res = write(1, buf, len);
+        if (res <= 0) return res;
+        buf += res;
+        len -= res;
+        tot_written += res;
+    }
+    return tot_written;
+}
+
+void
+PIRClient::mainloop()
+{
+    char header[PIRCLIENT_HDR_SIZE];
+    size_t bodylen = 0;
+    char *body = NULL;
+    string query, response;
+    size_t response_len;
+    while(1) {
+        // Read the request from stdin
+        int res = read_all(header, PIRCLIENT_HDR_SIZE);
+        if (res <= 0) return; // stdin has reached EOF (or error); we
+                              //  will terminate
+        bodylen = ntohl(*(uint32_t*)(header+PIRCLIENT_HDR_SIZE-4));
+        if (bodylen > 0) {
+            body = (char *)malloc(bodylen);
+            res = read_all(body, bodylen);
+            if (res <= 0) return;
+        }
+
+        // We have a complete request.  Dispatch it.
+        switch(header[8]) {
+        case PIRCLIENT_REQUEST_CREATE:
+            if (bodylen >= 32) {
+                string plainquery(body, 32);
+                string params(body+32, bodylen-32);
+                void *queryid = NULL;
+                string pirquery;
+                create(plainquery, params, queryid, pirquery);
+                size_t pirquery_len = pirquery.length();
+                header[8] = PIRCLIENT_RESPONSE_CREATE;
+                *(uint32_t*)(header+PIRCLIENT_HDR_SIZE-4) =
+                        htonl(8+pirquery_len);
+                res = write_all(header, PIRCLIENT_HDR_SIZE);
+                if (res <= 0) return;
+                res = write_all((const char *)&queryid, sizeof(queryid));
+                if (res <= 0) return;
+                if (sizeof(queryid) < 8) {
+                    res = write_all("\0\0\0\0\0\0\0\0", 8-sizeof(queryid));
+                    if (res <= 0) return;
+                }
+                if (pirquery_len > 0) {
+                    res = write_all(pirquery.c_str(), pirquery_len);
+                    if (res <= 0) return;
+                }
+            }
+            break;
+        case PIRCLIENT_REQUEST_EXTRACT:
+            if (bodylen >= 8) {
+                void *queryid;
+                memmove(&queryid, body, sizeof(queryid));
+                string pirresponse(body+8, bodylen-8);
+                string plainresponse;
+                if (extract(queryid, pirresponse, plainresponse)) {
+                    response_len = plainresponse.length();
+                } else {
+                    response_len = 0;
+                }
+                *(uint32_t*)(header+PIRCLIENT_HDR_SIZE-4) = htonl(response_len);
+                res = write_all(header, PIRCLIENT_HDR_SIZE);
+                if (res <= 0) return;
+                if (response_len > 0) {
+                    res = write_all(plainresponse.c_str(), response_len);
+                    if (res <= 0) return;
+                }
+            }
+            break;
+        }
+
+        // Clean up for the next request.
+        free(body);
+        body = NULL;
+        bodylen = 0;
+    }
+}

+ 27 - 0
pirclient.h

@@ -0,0 +1,27 @@
+#ifndef __PIRCLIENT_H__
+#define __PIRCLIENT_H__
+
+#include <string>
+
+using std::string;
+
+// Derive a class from this, implementing the virtual functions, and
+// call mainloop().
+
+class PIRClient {
+public:
+    // Create a PIR query.  The plainquery must be exactly 32 bytes
+    // long.
+    virtual void create(const string &plainquery, const string &params,
+        void *&queryid, string &pirquery) = 0;
+
+    // Extract the plaintext response from a PIR response.  Returns
+    // true if successful, false if unsuccessful.
+    virtual bool extract(void *&queryid, const string &pirresponse,
+        string &plainresponse) = 0;
+
+    // Call this to run the client, and exit when it returns
+    void mainloop();
+};
+
+#endif

+ 50 - 0
protocol_client

@@ -0,0 +1,50 @@
+The tor process will launch the pirclient process, and hold pipes to the
+pirclient's stdin, stdout, and stderr.  All communication between the
+processes is over those pipes.  Anything the pirclient outputs over the
+stderr pipe should be logged by the tor process.  If the pirclient finds
+its stdin has reached EOF, it should terminate.
+
+The general format of request messages (tor -> pirclient) is:
+
+request_id: 8 bytes
+request_type: 1 byte
+body_len: 4 bytes (big-endian unsigned int)
+body: (body_len) bytes
+
+The general format of response messages (pirclient -> tor) is:
+
+request_id: 8 bytes (must match the request_id being responded to)
+response_type: 1 byte
+body_len: 4 bytes (big-endian unsigned int)
+body: (body_len) bytes
+
+There are two request types:
+
+PIRCLIENT_REQUEST_CREATE (0x41)
+Request the creation of a private query
+    The body_len must be at least 32.  The first 32 bytes of the body
+    are the plaintext query, and the remaining bytes are the current
+    pirserver params for the server we're going to send this query to.
+
+PIRCLIENT_REQUEST_EXTRACT (0x42)
+Extract the plaintext data from a PIR response
+    The body_len must be at least 8.  The first 8 bytes of the body
+    are the Query ID returned in the PIRCLIENT_RESPONSE_CREATE message,
+    and the remaining bytes are the PIR response.  Any given Query ID
+    must be used in at most one PIRCLIENT_REQUEST_EXTRACT message (and
+    it should be exactly one).
+
+There are two response types:
+
+PIRCLIENT_RESPONSE_CREATE (0xBF)
+Reply with a PIR query
+    The body begins with an 8-byte Query ID (which the tor process
+    should hold on to, to match it to the corresponding PIR response in
+    the PIRCLIENT_REQUEST_EXTRACT message).  The remainder of the body
+    is the PIR query to send to the server.  A body_len of 0 indicates
+    an error.
+
+PIRCLIENT_RESPONSE_EXTRACT (0xBE)
+Reply with the plaintext data extracted from a PIR response
+    The body is the plaintext of the response received from the PIR
+    server.  A body_len of 0 indicates an error.

+ 0 - 0
protocol → protocol_server