Add unpackage() function to parse <v0.7 and ≥0.7 EMOTP message formats.
[easyotp.git] / libotp.c
blobf7400fe794dbf338fce6e669a9c3ff96fe344b13
1 /** Practical One-time Pad Library
3 * Created:20080514
4 * By Jeff Connelly
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <sysexits.h>
10 #include <string.h>
12 #include "libotp.h"
14 PAD *pads = NULL;
16 /** Show a list of all loaded pads. */
17 void show_pads()
19 PAD *p;
21 for (p = pads; p; p = p->next) {
22 printf("Pad: %s: %s\n", p->name, p->local_filename);
26 /** Open a companion offset file for given pad. Caller must close.
28 * @param mode Either "rt" or "wt", for reading or writing.
30 FILE *open_offset_file(PAD *p, char *mode)
32 FILE *ofp;
33 char *offset_filename;
34 size_t filename_length;
36 /* Offset is stored in a separate file; pad plus OFFSET_FILE_NAME_EXTENSION. */
37 filename_length = strlen(p->local_filename) + strlen(OFFSET_FILE_EXTENSION) + 1;
38 offset_filename = malloc(filename_length);
39 if (!offset_filename) {
40 perror("malloc");
41 exit(EX_UNAVAILABLE);
43 snprintf(offset_filename, filename_length, "%s" OFFSET_FILE_EXTENSION, p->local_filename);
45 /* Read offset from file. */
46 ofp = fopen(offset_filename, mode);
47 if (!ofp) {
48 fprintf(stderr, "opening offset file %s failed\n", offset_filename);
49 perror("fopen");
50 free(offset_filename);
51 exit(EX_IOERR);
53 free(offset_filename);
55 return ofp;
58 /** Read the pad offset of a given pad. */
59 unsigned long read_offset(PAD *p)
61 FILE *ofp;
62 char buffer[OFFSET_SIZE];
63 unsigned long offset;
65 ofp = open_offset_file(p, "rt");
67 memset(buffer, 0, OFFSET_SIZE);
68 if (fread(buffer, 1, OFFSET_SIZE - 1, ofp) < 1) {
69 fprintf(stderr, "could not read offset file for %s\n", p->local_filename);
70 exit(EX_IOERR);
73 if (fclose(ofp) != 0) {
74 fprintf(stderr, "error closing offset file for reading for %s\n", p->local_filename);
75 perror("fclose");
76 exit(EX_IOERR);
79 /* We finally got it! */
80 offset = strtoul(buffer, NULL, 10);
82 return offset;
85 /** Write the pad offset to the given pad. */
86 void write_offset(PAD *p, unsigned long offset)
88 FILE *ofp;
89 char buffer[OFFSET_SIZE];
91 ofp = open_offset_file(p, "wt");
93 memset(buffer, 0, OFFSET_SIZE);
94 snprintf(buffer, OFFSET_SIZE - 1, "%ld", offset);
95 printf("buffer=%s\n", buffer);
96 if (fwrite(buffer, strlen(buffer), 1, ofp) != 1) {
97 fprintf(stderr, "write error saving offset %ld for %s\n", offset, p->local_filename);
98 exit(EX_IOERR);
102 if (fclose(ofp) != 0) {
103 fprintf(stderr, "error closing offset file for writing for %s\n", p->local_filename);
104 perror("fclose");
105 exit(EX_IOERR);
109 /** Load a pad file from disk, adding to 'pads' global. */
110 void load_pad(char *local_filename)
112 FILE *fp;
113 PAD *new_pad;
115 fp = fopen("/Volumes/Not Backed Up/otp/otp-dazzlement", "rb");
116 if (!fp) {
117 perror("fopen");
118 exit(EXIT_FAILURE);
121 new_pad = malloc(sizeof(PAD));
122 if (!new_pad) {
123 perror("malloc");
124 exit(EX_UNAVAILABLE);
127 new_pad->local_filename = strdup(local_filename);
128 new_pad->name = strdup("dc"); /* TODO */
129 new_pad->fp = fp;
130 new_pad->next = NULL;
132 /* Add to linked list. */
133 if (!pads) {
134 pads = new_pad;
135 } else {
136 PAD *p, *tail;
138 /* Find tail */
139 for (p = pads; p; p = p->next)
140 tail = p;
141 tail->next = new_pad;
145 /** Close all pads and free allocated memory. */
146 void free_pads()
148 PAD *p, *next;
150 for (p = pads; p; p = next) {
151 free(p->name);
152 free(p->local_filename);
153 fclose(p->fp);
154 next = p->next;
155 free(p);
159 /** Unpackage a message packaged for transport. */
160 void unpackage(char *input)
162 MESSAGE msg;
163 unsigned int at;
164 char *s, *end;
166 /** Format <v0.7 (pad name is unspecified; use default):
167 * MARKER_BEGIN + offset + comma
168 * base64'd data
169 * MARKER_END
171 * Format >=0.7:
172 * MARKER_BEGIN + offset + comma + pad_name + comma
173 * base64'd data
174 * MARKER_END
177 at = 0;
179 /* Locate where the message begins. */
180 s = strstr(input, MARKER_BEGIN);
181 if (!s) {
182 fprintf(stderr, "unpackage: input |%s| lacks beginning marker %s\n",
183 input, MARKER_BEGIN);
184 exit(EX_DATAERR);
187 /* ...and ends. */
188 end = strstr(input, MARKER_END);
189 if (!end) {
190 fprintf(stderr, "unpackage: input |%s| lacks ending marker %s\n",
191 input, MARKER_END);
192 exit(EX_DATAERR);
195 s += strlen(MARKER_BEGIN);
196 msg.offset = strtoul(s, &s, 10);
198 printf("offset=%ld\n", msg.offset);
200 /* Move after mandatory comma. */
201 if (s[0] != ',') {
202 fprintf(stderr, "unpackage: missing comma after offset, at |%s|, input |%s|\n",
203 s, input);
204 exit(EX_DATAERR);
206 ++s;
208 memset(msg.pad_name, 0, PAD_NAME_LENGTH);
210 /* Includes pad name? */
211 if (s[0] != '\n' && s[0] != '\r') {
212 unsigned int i;
214 i = 0;
215 /* v0.7+ message, includes pad name */
216 while(s[0] != '\n' && s[0] != '\r' && s[0] != ',' && s < end) {
217 msg.pad_name[i] = s[0];
218 ++s;
219 ++i;
220 if (i > PAD_NAME_LENGTH) {
221 fprintf(stderr, "unpackage: pad name length > maximum %d, in input |%s|\n",
222 PAD_NAME_LENGTH, input);
223 exit(EX_DATAERR);
228 printf("Pad name: |%s|\n", msg.pad_name);
230 /* Go to next line */
231 while(s[0] == '\n' || s[0] == '\r' || s[0] == ',' && (s < end))
232 ++s;
234 printf("s=%s\n", s);
236 /* Extract base64 data from end of message. */
237 msg.cipher_text = strdup(s);
238 msg.cipher_text[end - s] = 0;
240 printf("b64_data=<%s>\n", msg.cipher_text);
242 /* TODO: free(msg.cipher_text); */