~ubuntu-branches/ubuntu/oneiric/libotr/oneiric-security

« back to all changes in this revision

Viewing changes to src/proto.c

  • Committer: Bazaar Package Importer
  • Author(s): Thibaut VARENE
  • Date: 2006-01-02 19:52:18 UTC
  • mfrom: (2.1.1 dapper)
  • Revision ID: james.westby@ubuntu.com-20060102195218-wb8803196y9mycx6
Tags: 3.0.0-2
Fix typo: "malformed messahes" (Closes: #345400)

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
#include "mem.h"
36
36
#include "version.h"
37
37
#include "tlv.h"
38
 
 
39
 
#undef DEBUG
40
 
 
41
 
#ifdef DEBUG
42
 
static void debug_data(const char *title, const unsigned char *data,
43
 
        size_t len)
44
 
{
45
 
    size_t i;
46
 
    fprintf(stderr, "%s: ", title);
47
 
    for(i=0;i<len;++i) {
48
 
        fprintf(stderr, "%02x", data[i]);
49
 
    }
50
 
    fprintf(stderr, "\n");
51
 
}
52
 
 
53
 
static void debug_int(const char *title, const unsigned char *data)
54
 
{
55
 
    unsigned int v =
56
 
        (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
57
 
    fprintf(stderr, "%s: %u (0x%x)\n", title, v, v);
58
 
}
59
 
#else
60
 
#define debug_data(t,b,l)
61
 
#define debug_int(t,b)
62
 
#endif
63
 
 
64
 
#define write_int(x) do { \
65
 
        bufp[0] = ((x) >> 24) & 0xff; \
66
 
        bufp[1] = ((x) >> 16) & 0xff; \
67
 
        bufp[2] = ((x) >> 8) & 0xff; \
68
 
        bufp[3] = (x) & 0xff; \
69
 
        bufp += 4; lenp -= 4; \
70
 
    } while(0)
71
 
 
72
 
#define write_mpi(x,nx,dx) do { \
73
 
        write_int(nx); \
74
 
        gcry_mpi_print(format, bufp, lenp, NULL, (x)); \
75
 
        debug_data((dx), bufp, (nx)); \
76
 
        bufp += (nx); lenp -= (nx); \
77
 
    } while(0)
78
 
 
79
 
#define require_len(l) do { \
80
 
        if (lenp < (l)) goto invval; \
81
 
    } while(0)
82
 
 
83
 
#define read_int(x) do { \
84
 
        require_len(4); \
85
 
        (x) = (bufp[0] << 24) | (bufp[1] << 16) | (bufp[2] << 8) | bufp[3]; \
86
 
        bufp += 4; lenp -= 4; \
87
 
    } while(0)
88
 
 
89
 
#define read_mpi(x) do { \
90
 
        size_t mpilen; \
91
 
        read_int(mpilen); \
92
 
        require_len(mpilen); \
93
 
        gcry_mpi_scan(&(x), GCRYMPI_FMT_USG, bufp, mpilen, NULL); \
94
 
        bufp += mpilen; lenp -= mpilen; \
95
 
    } while(0)
 
38
#include "serial.h"
96
39
 
97
40
/* Initialize the OTR library.  Pass the version of the API you are
98
41
 * using. */
123
66
    return OTRL_VERSION;
124
67
}
125
68
 
126
 
/* Create a public key block from a private key */
127
 
gcry_error_t otrl_proto_make_pubkey(unsigned char **pubbufp, size_t *publenp,
128
 
        gcry_sexp_t privkey)
129
 
{
130
 
    gcry_mpi_t p,q,g,y;
131
 
    gcry_sexp_t dsas,ps,qs,gs,ys;
132
 
    size_t np,nq,ng,ny;
133
 
    enum gcry_mpi_format format = GCRYMPI_FMT_USG;
134
 
    unsigned char *bufp;
135
 
    size_t lenp;
136
 
 
137
 
    *pubbufp = NULL;
138
 
    *publenp = 0;
139
 
 
140
 
    /* Extract the public parameters */
141
 
    dsas = gcry_sexp_find_token(privkey, "dsa", 0);
142
 
    if (dsas == NULL) {
143
 
        return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
144
 
    }
145
 
    ps = gcry_sexp_find_token(dsas, "p", 0);
146
 
    qs = gcry_sexp_find_token(dsas, "q", 0);
147
 
    gs = gcry_sexp_find_token(dsas, "g", 0);
148
 
    ys = gcry_sexp_find_token(dsas, "y", 0);
149
 
    gcry_sexp_release(dsas);
150
 
    if (!ps || !qs || !gs || !ys) {
151
 
        gcry_sexp_release(ps);
152
 
        gcry_sexp_release(qs);
153
 
        gcry_sexp_release(gs);
154
 
        gcry_sexp_release(ys);
155
 
        return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
156
 
    }
157
 
    p = gcry_sexp_nth_mpi(ps, 1, GCRYMPI_FMT_USG);
158
 
    gcry_sexp_release(ps);
159
 
    q = gcry_sexp_nth_mpi(qs, 1, GCRYMPI_FMT_USG);
160
 
    gcry_sexp_release(qs);
161
 
    g = gcry_sexp_nth_mpi(gs, 1, GCRYMPI_FMT_USG);
162
 
    gcry_sexp_release(gs);
163
 
    y = gcry_sexp_nth_mpi(ys, 1, GCRYMPI_FMT_USG);
164
 
    gcry_sexp_release(ys);
165
 
    if (!p || !q || !g || !y) {
166
 
        gcry_mpi_release(p);
167
 
        gcry_mpi_release(q);
168
 
        gcry_mpi_release(g);
169
 
        gcry_mpi_release(y);
170
 
        return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
171
 
    }
172
 
 
173
 
    *publenp = 0;
174
 
    gcry_mpi_print(format, NULL, 0, &np, p);
175
 
    *publenp += np + 4;
176
 
    gcry_mpi_print(format, NULL, 0, &nq, q);
177
 
    *publenp += nq + 4;
178
 
    gcry_mpi_print(format, NULL, 0, &ng, g);
179
 
    *publenp += ng + 4;
180
 
    gcry_mpi_print(format, NULL, 0, &ny, y);
181
 
    *publenp += ny + 4;
182
 
 
183
 
    *pubbufp = malloc(*publenp);
184
 
    if (*pubbufp == NULL) {
185
 
        gcry_mpi_release(p);
186
 
        gcry_mpi_release(q);
187
 
        gcry_mpi_release(g);
188
 
        gcry_mpi_release(y);
189
 
        return gcry_error(GPG_ERR_ENOMEM);
190
 
    }
191
 
    bufp = *pubbufp;
192
 
    lenp = *publenp;
193
 
 
194
 
    write_mpi(p,np,"P");
195
 
    write_mpi(q,nq,"Q");
196
 
    write_mpi(g,ng,"G");
197
 
    write_mpi(y,ny,"Y");
198
 
 
199
 
    gcry_mpi_release(p);
200
 
    gcry_mpi_release(q);
201
 
    gcry_mpi_release(g);
202
 
    gcry_mpi_release(y);
203
 
 
204
 
    return gcry_error(GPG_ERR_NO_ERROR);
205
 
}
206
 
 
207
69
/* Store some MAC keys to be revealed later */
208
70
static gcry_error_t reveal_macs(ConnContext *context,
209
71
        DH_sesskeys *sess1, DH_sesskeys *sess2)
274
136
        err = otrl_dh_session(&(context->sesskeys[0][0]),
275
137
                &(context->our_dh_key), context->their_y);
276
138
        if (err) return err;
 
139
    } else {
 
140
        otrl_dh_session_blank(&(context->sesskeys[0][0]));
277
141
    }
278
142
    if (context->their_old_y) {
279
143
        err = otrl_dh_session(&(context->sesskeys[0][1]),
280
144
                &(context->our_dh_key), context->their_old_y);
281
145
        if (err) return err;
 
146
    } else {
 
147
        otrl_dh_session_blank(&(context->sesskeys[0][1]));
282
148
    }
283
149
    return gcry_error(GPG_ERR_NO_ERROR);
284
150
}
322
188
/* Return a pointer to a newly-allocated OTR query message, customized
323
189
 * with our name.  The caller should free() the result when he's done
324
190
 * with it. */
325
 
char *otrl_proto_default_query_msg(const char *ourname)
 
191
char *otrl_proto_default_query_msg(const char *ourname, OtrlPolicy policy)
326
192
{
327
193
    char *msg;
 
194
    int v1_supported, v2_supported;
 
195
    const char *version_tag;
328
196
    /* Don't use g_strdup_printf here, because someone (not us) is going
329
197
     * to free() the *message pointer, not g_free() it.  We can't
330
198
     * require that they g_free() it, because this pointer will probably
331
199
     * get passed to the main IM application for processing (and
332
200
     * free()ing). */
333
 
    const char *format = "?OTR?\n<b>%s</b> has requested an "
 
201
    const char *format = "?OTR%s\n<b>%s</b> has requested an "
334
202
            "<a href=\"http://www.cypherpunks.ca/otr/\">Off-the-Record "
335
203
            "private conversation</a>.  However, you do not have a plugin "
336
204
            "to support that.\nSee <a href=\"http://www.cypherpunks.ca/otr/\">"
337
205
            "http://www.cypherpunks.ca/otr/</a> for more information.";
338
206
 
339
 
    /* Remove "%s", add '\0' */
340
 
    msg = malloc(strlen(format) + strlen(ourname) - 1);
 
207
    /* Figure out the version tag */
 
208
    v1_supported = (policy & OTRL_POLICY_ALLOW_V1);
 
209
    v2_supported = (policy & OTRL_POLICY_ALLOW_V2);
 
210
    if (v1_supported) {
 
211
        if (v2_supported) {
 
212
            version_tag = "?v2?";
 
213
        } else {
 
214
            version_tag = "?";
 
215
        }
 
216
    } else {
 
217
        if (v2_supported) {
 
218
            version_tag = "v2?";
 
219
        } else {
 
220
            version_tag = "v?";
 
221
        }
 
222
    }
 
223
 
 
224
    /* Remove two "%s", add '\0' */
 
225
    msg = malloc(strlen(format) + strlen(version_tag) + strlen(ourname) - 3);
341
226
    if (!msg) return NULL;
342
 
    sprintf(msg, format, ourname);
 
227
    sprintf(msg, format, version_tag, ourname);
343
228
    return msg;
344
229
}
345
230
 
 
231
/* Return the best version of OTR support by both sides, given an OTR
 
232
 * Query Message and the local policy. */
 
233
unsigned int otrl_proto_query_bestversion(const char *querymsg,
 
234
        OtrlPolicy policy)
 
235
{
 
236
    char *otrtag;
 
237
    unsigned int query_versions = 0;
 
238
 
 
239
    otrtag = strstr(querymsg, "?OTR");
 
240
    otrtag += 4;
 
241
    if (*otrtag == '?') {
 
242
        query_versions = (1<<0);
 
243
        ++otrtag;
 
244
    }
 
245
    if (*otrtag == 'v') {
 
246
        for(++otrtag; *otrtag && *otrtag != '?'; ++otrtag) {
 
247
            switch(*otrtag) {
 
248
                case '2':
 
249
                    query_versions |= (1<<1);
 
250
                    break;
 
251
            }
 
252
        }
 
253
    }
 
254
 
 
255
    if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) {
 
256
        return 2;
 
257
    }
 
258
    if ((policy & OTRL_POLICY_ALLOW_V1) && (query_versions & (1<<0))) {
 
259
        return 1;
 
260
    }
 
261
    return 0;
 
262
}
 
263
 
 
264
/* Locate any whitespace tag in this message, and return the best
 
265
 * version of OTR support on both sides.  Set *starttagp and *endtagp to
 
266
 * the start and end of the located tag, so that it can be snipped out. */
 
267
unsigned int otrl_proto_whitespace_bestversion(const char *msg,
 
268
        const char **starttagp, const char **endtagp, OtrlPolicy policy)
 
269
{
 
270
    const char *starttag, *endtag;
 
271
    unsigned int query_versions = 0;
 
272
 
 
273
    *starttagp = NULL;
 
274
    *endtagp = NULL;
 
275
 
 
276
    starttag = strstr(msg, OTRL_MESSAGE_TAG_BASE);
 
277
    if (!starttag) return 0;
 
278
 
 
279
    endtag = starttag + strlen(OTRL_MESSAGE_TAG_BASE);
 
280
 
 
281
    /* Look for groups of 8 spaces and/or tabs */
 
282
    while(1) {
 
283
        int i;
 
284
        int allwhite = 1;
 
285
        for(i=0;i<8;++i) {
 
286
            if (endtag[i] != ' ' && endtag[i] != '\t') {
 
287
                allwhite = 0;
 
288
                break;
 
289
            }
 
290
        }
 
291
        if (allwhite) {
 
292
            if (!strncmp(endtag, OTRL_MESSAGE_TAG_V1, 8)) {
 
293
                query_versions |= (1<<0);
 
294
            }
 
295
            if (!strncmp(endtag, OTRL_MESSAGE_TAG_V2, 8)) {
 
296
                query_versions |= (1<<1);
 
297
            }
 
298
            endtag += 8;
 
299
        } else {
 
300
            break;
 
301
        }
 
302
    }
 
303
 
 
304
    *starttagp = starttag;
 
305
    *endtagp = endtag;
 
306
 
 
307
    if ((policy & OTRL_POLICY_ALLOW_V2) && (query_versions & (1<<1))) {
 
308
        return 2;
 
309
    }
 
310
    if ((policy & OTRL_POLICY_ALLOW_V1) && (query_versions & (1<<0))) {
 
311
        return 1;
 
312
    }
 
313
    return 0;
 
314
}
 
315
 
346
316
/* Return the Message type of the given message. */
347
 
OTRMessageType otrl_proto_message_type(const char *message)
 
317
OtrlMessageType otrl_proto_message_type(const char *message)
348
318
{
349
319
    char *otrtag;
350
320
 
351
321
    otrtag = strstr(message, "?OTR");
352
322
 
353
323
    if (!otrtag) {
354
 
        if (strstr(message, OTR_MESSAGE_TAG)) {
355
 
            return OTR_TAGGEDPLAINTEXT;
 
324
        if (strstr(message, OTRL_MESSAGE_TAG_BASE)) {
 
325
            return OTRL_MSGTYPE_TAGGEDPLAINTEXT;
356
326
        } else {
357
 
            return OTR_NOTOTR;
358
 
        }
359
 
    }
360
 
 
361
 
    if (!strncmp(otrtag, "?OTR?", 5)) return OTR_QUERY;
362
 
    if (!strncmp(otrtag, "?OTR:AAEK", 9)) return OTR_KEYEXCH;
363
 
    if (!strncmp(otrtag, "?OTR:AAED", 9)) return OTR_DATA;
364
 
    if (!strncmp(otrtag, "?OTR Error:", 11)) return OTR_ERROR;
365
 
 
366
 
    return OTR_UNKNOWN;
367
 
}
368
 
 
369
 
/* Create a Key Exchange message for our correspondent.  If we need a
370
 
 * private key and don't have one, create_privkey will be called.  Use
371
 
 * the privkeys from the given OtrlUserState. */
372
 
gcry_error_t otrl_proto_create_key_exchange(OtrlUserState us,
373
 
        char **messagep, ConnContext *context, unsigned char is_reply,
374
 
        void (*create_privkey)(void *create_privkey_data,
375
 
            const char *accountname, const char *protocol),
376
 
        void *create_privkey_data)
377
 
{
378
 
    gcry_mpi_t r, s;
379
 
    gcry_sexp_t dsas, rs, ss;
380
 
    gcry_sexp_t sigs, hashs;
381
 
    size_t nr, ns, buflen, lenp;
382
 
    unsigned char *buf, *bufp;
383
 
    enum gcry_mpi_format format = GCRYMPI_FMT_USG;
384
 
    unsigned char digest[20];
385
 
    gcry_mpi_t digestmpi;
386
 
    char *base64buf;
387
 
    size_t base64len;
388
 
    size_t pubkeylen;
389
 
    PrivKey *privkey =
390
 
        otrl_privkey_find(us, context->accountname, context->protocol);
391
 
 
392
 
    *messagep = NULL;
393
 
 
394
 
    if (privkey == NULL) {
395
 
        /* We've got no private key! */
396
 
        if (create_privkey) {
397
 
            create_privkey(create_privkey_data, context->accountname,
398
 
                    context->protocol);
399
 
            privkey =
400
 
                otrl_privkey_find(us, context->accountname, context->protocol);
401
 
        }
402
 
    }
403
 
    if (privkey == NULL) {
404
 
        /* We've still got no private key! */
405
 
        return gcry_error(GPG_ERR_UNUSABLE_SECKEY);
406
 
    }
407
 
    
408
 
    /* Make sure we have two keys */
409
 
    while (context->our_keyid < 2) {
410
 
        rotate_dh_keys(context);
411
 
    }
412
 
 
413
 
    buflen = 3 + 1 + privkey->pubkey_datalen + 4 + 40;
414
 
        /* header, is_reply, pubkey, keyid, sig */
415
 
    gcry_mpi_print(format, NULL, 0, &pubkeylen, context->our_old_dh_key.pub);
416
 
    buflen += pubkeylen + 4;
417
 
    buf = malloc(buflen);
418
 
    if (buf == NULL) {
419
 
        return gcry_error(GPG_ERR_ENOMEM);
420
 
    }
421
 
    bufp = buf;
422
 
    lenp = buflen;
423
 
    memmove(bufp, "\x00\x01\x0a", 3);  /* header */
424
 
    debug_data("Header", bufp, 3);
425
 
    bufp += 3; lenp -= 3;
426
 
 
427
 
    *bufp = is_reply;                  /* is_reply */
428
 
    debug_data("Reply", bufp, 1);
429
 
    bufp += 1; lenp -= 1;
430
 
 
431
 
                                       /* DSA pubkey */
432
 
    memmove(bufp, privkey->pubkey_data, privkey->pubkey_datalen);
433
 
    debug_data("DSA key", bufp, privkey->pubkey_datalen);
434
 
    bufp += privkey->pubkey_datalen; lenp -= privkey->pubkey_datalen;
435
 
 
436
 
                                       /* keyid */
437
 
    write_int(context->our_keyid - 1);
438
 
    debug_int("Keyid", bufp - 4);
439
 
 
440
 
                                       /* DH pubkey */
441
 
    write_mpi(context->our_old_dh_key.pub, pubkeylen, "Pubkey");
442
 
 
443
 
    /* Get a hash of the data to be signed */
444
 
    gcry_md_hash_buffer(GCRY_MD_SHA1, digest, buf, bufp-buf);
445
 
    gcry_mpi_scan(&digestmpi, GCRYMPI_FMT_USG, digest, 20, NULL);
446
 
 
447
 
    /* Calculate the sig */
448
 
    gcry_sexp_build(&hashs, NULL, "(%m)", digestmpi);
449
 
    gcry_mpi_release(digestmpi);
450
 
    gcry_pk_sign(&sigs, hashs, privkey->privkey);
451
 
    gcry_sexp_release(hashs);
452
 
    dsas = gcry_sexp_find_token(sigs, "dsa", 0);
453
 
    gcry_sexp_release(sigs);
454
 
    rs = gcry_sexp_find_token(dsas, "r", 0);
455
 
    ss = gcry_sexp_find_token(dsas, "s", 0);
456
 
    gcry_sexp_release(dsas);
457
 
    r = gcry_sexp_nth_mpi(rs, 1, GCRYMPI_FMT_USG);
458
 
    gcry_sexp_release(rs);
459
 
    s = gcry_sexp_nth_mpi(ss, 1, GCRYMPI_FMT_USG);
460
 
    gcry_sexp_release(ss);
461
 
    gcry_mpi_print(format, NULL, 0, &nr, r);
462
 
    gcry_mpi_print(format, NULL, 0, &ns, s);
463
 
    memset(bufp, 0, 40);
464
 
    gcry_mpi_print(format, bufp+(20-nr), lenp, NULL, r);
465
 
    debug_data("R", bufp, 20);
466
 
    bufp += 20; lenp -= 20;
467
 
    gcry_mpi_print(format, bufp+(20-ns), lenp, NULL, s);
468
 
    debug_data("S", bufp, 20);
469
 
    bufp += 20; lenp -= 20;
470
 
 
471
 
    assert(lenp == 0);
472
 
 
473
 
    gcry_mpi_release(r);
474
 
    gcry_mpi_release(s);
475
 
 
476
 
    /* Make the base64-encoding. */
477
 
    base64len = ((buflen + 2) / 3) * 4;
478
 
    base64buf = malloc(5 + base64len + 1 + 1);
479
 
    assert(base64buf != NULL);
480
 
    memmove(base64buf, "?OTR:", 5);
481
 
    otrl_base64_encode(base64buf+5, buf, buflen);
482
 
    base64buf[5 + base64len] = '.';
483
 
    base64buf[5 + base64len + 1] = '\0';
484
 
 
485
 
    free(buf);
486
 
 
487
 
    *messagep = base64buf;
488
 
 
489
 
    return gcry_error(GPG_ERR_NO_ERROR);
490
 
}
491
 
 
492
 
/* Deallocate an OTRKeyExchangeMsg returned from proto_parse_key_exchange */
493
 
void otrl_proto_free_key_exchange(OTRKeyExchangeMsg kem)
494
 
{
495
 
    if (!kem) return;
496
 
    gcry_sexp_release(kem->digest_sexp);
497
 
    gcry_sexp_release(kem->dsa_pubkey);
498
 
    gcry_mpi_release(kem->dh_pubkey);
499
 
    gcry_sexp_release(kem->dsa_sig);
500
 
    free(kem);
501
 
}
502
 
 
503
 
/* Parse a purported Key Exchange message.  Possible error code portions
504
 
 * of the return value:
505
 
 *   GPG_ERR_NO_ERROR:      Success
506
 
 *   GPG_ERR_ENOMEM:        Out of memory condition
507
 
 *   GPG_ERR_INV_VALUE:     The message was not a well-formed Key Exchange
508
 
 *                          message
509
 
 *   GPG_ERR_BAD_SIGNATURE: The signature on the message didn't verify
510
 
 */
511
 
gcry_error_t otrl_proto_parse_key_exchange(OTRKeyExchangeMsg *kemp,
512
 
        const char *msg)
513
 
{
514
 
    char *otrtag, *endtag;
515
 
    unsigned char *rawmsg, *bufp;
516
 
    size_t msglen, rawlen, lenp;
517
 
    gcry_mpi_t p,q,g,e,r,s;
518
 
    unsigned char hash_of_msg[20];
519
 
    gcry_mpi_t hashmpi;
520
 
    const unsigned char *fingerprintstart, *fingerprintend;
521
 
    const unsigned char *signaturestart, *signatureend;
522
 
    OTRKeyExchangeMsg kem = calloc(1, sizeof(struct s_OTRKeyExchangeMsg));
523
 
 
524
 
    if (!kem) {
525
 
        *kemp = NULL;
526
 
        return gcry_error(GPG_ERR_ENOMEM);
527
 
    }
528
 
 
529
 
    otrtag = strstr(msg, "?OTR:");
530
 
    if (!otrtag) {
531
 
        *kemp = NULL;
532
 
        otrl_proto_free_key_exchange(kem);
533
 
        return gcry_error(GPG_ERR_INV_VALUE);
534
 
    }
535
 
    endtag = strchr(otrtag, '.');
536
 
    if (endtag) {
537
 
        msglen = endtag-otrtag;
538
 
    } else {
539
 
        msglen = strlen(otrtag);
540
 
    }
541
 
 
542
 
    /* Base64-decode the message */
543
 
    rawlen = ((msglen-5) / 4) * 3;   /* maximum possible */
544
 
    rawmsg = malloc(rawlen);
545
 
    if (!rawmsg && rawlen > 0) {
546
 
        *kemp = NULL;
547
 
        otrl_proto_free_key_exchange(kem);
548
 
        return gcry_error(GPG_ERR_ENOMEM);
549
 
    }
550
 
    rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5);  /* actual size */
551
 
 
552
 
    bufp = rawmsg;
553
 
    lenp = rawlen;
554
 
 
555
 
    signaturestart = bufp;
556
 
 
557
 
    require_len(3);
558
 
    if (memcmp(bufp, "\x00\x01\x0a", 3)) {
559
 
        /* Invalid header */
560
 
        goto invval;
561
 
    }
562
 
    bufp += 3; lenp -= 3;
563
 
 
564
 
    require_len(1);
565
 
    kem->is_reply = *bufp;
566
 
    if (kem->is_reply != 0 && kem->is_reply != 1) {
567
 
        /* Malformed is_reply field */
568
 
        goto invval;
569
 
    }
570
 
    bufp += 1; lenp -= 1;
571
 
 
572
 
    fingerprintstart = bufp;
573
 
    /* Read the DSA public key and calculate its fingerprint. */
574
 
    read_mpi(p);
575
 
    read_mpi(q);
576
 
    read_mpi(g);
577
 
    read_mpi(e);
578
 
    fingerprintend = bufp;
579
 
    gcry_md_hash_buffer(GCRY_MD_SHA1, kem->key_fingerprint,
580
 
            fingerprintstart, fingerprintend-fingerprintstart);
581
 
 
582
 
    /* Create the pubkey S-expression. */
583
 
    gcry_sexp_build(&(kem->dsa_pubkey), NULL,
584
 
            "(public-key (dsa (p %m)(q %m)(g %m)(y %m)))",
585
 
            p, q, g, e);
586
 
    gcry_mpi_release(p);
587
 
    gcry_mpi_release(q);
588
 
    gcry_mpi_release(g);
589
 
    gcry_mpi_release(e);
590
 
 
591
 
    /* Read the key id */
592
 
    read_int(kem->keyid);
593
 
    if (kem->keyid == 0) {
594
 
        /* Not a legal value */
595
 
        goto invval;
596
 
    }
597
 
 
598
 
    /* Read the DH public key */
599
 
    read_mpi(kem->dh_pubkey);
600
 
 
601
 
    /* Hash the message so we can check the signature */
602
 
    signatureend = bufp;
603
 
    gcry_md_hash_buffer(GCRY_MD_SHA1, hash_of_msg,
604
 
            signaturestart, signatureend-signaturestart);
605
 
    /* Turn the hash into an mpi, then into a sexp */
606
 
    gcry_mpi_scan(&hashmpi, GCRYMPI_FMT_USG, hash_of_msg, 20, NULL);
607
 
    gcry_sexp_build(&(kem->digest_sexp), NULL, "(%m)", hashmpi);
608
 
    gcry_mpi_release(hashmpi);
609
 
 
610
 
    /* Read the signature */
611
 
    require_len(40);
612
 
    gcry_mpi_scan(&r, GCRYMPI_FMT_USG, bufp, 20, NULL);
613
 
    gcry_mpi_scan(&s, GCRYMPI_FMT_USG, bufp+20, 20, NULL);
614
 
    lenp -= 40;
615
 
    gcry_sexp_build(&(kem->dsa_sig), NULL, "(sig-val (dsa (r %m)(s %m)))",
616
 
            r, s);
617
 
    gcry_mpi_release(r);
618
 
    gcry_mpi_release(s);
619
 
 
620
 
    /* That should be everything */
621
 
    if (lenp != 0) goto invval;
622
 
 
623
 
    /* Verify the signature */
624
 
    if (gcry_pk_verify(kem->dsa_sig, kem->digest_sexp, kem->dsa_pubkey)) {
625
 
        /* It failed! */
626
 
        otrl_proto_free_key_exchange(kem);
627
 
        free(rawmsg);
628
 
        *kemp = NULL;
629
 
        return gcry_error(GPG_ERR_BAD_SIGNATURE);
630
 
    }
631
 
 
632
 
    free(rawmsg);
633
 
    *kemp = kem;
634
 
    return gcry_error(GPG_ERR_NO_ERROR);
635
 
invval:
636
 
    otrl_proto_free_key_exchange(kem);
637
 
    free(rawmsg);
638
 
    *kemp = NULL;
639
 
    return gcry_error(GPG_ERR_INV_VALUE);
640
 
}
641
 
 
642
 
/* Deal with a Key Exchange Message once it's been received and passed
643
 
 * all the validity and UI ("accept this fingerprint?") tests.
644
 
 * context/fprint is the ConnContext and Fingerprint to which it
645
 
 * belongs.  Use the given OtrlUserState to look up any necessary
646
 
 * private keys.  It is the caller's responsibility to
647
 
 * otrl_proto_free_key_exchange(kem) when we're done.  If *messagep gets
648
 
 * set to non-NULL by this function, then it's a message that needs to
649
 
 * get sent to the correspondent.  If we need a private key and don't
650
 
 * have one, create_privkey will be called. */
651
 
gcry_error_t otrl_proto_accept_key_exchange(OtrlUserState us,
652
 
        ConnContext *context, Fingerprint *fprint, OTRKeyExchangeMsg kem,
653
 
        char **messagep,
654
 
        void (*create_privkey)(void *create_privkey_data,
655
 
            const char *accountname, const char *protocol),
656
 
        void *create_privkey_data)
657
 
{
658
 
    gcry_error_t err;
659
 
    char *savedmessage = context->lastmessage;
660
 
    int savedmay_retransmit = context->may_retransmit;
661
 
    time_t savedtime = context->lastsent;
662
 
    ConnectionState state = context->state;
663
 
    *messagep = NULL;
664
 
    context->lastmessage = NULL;
665
 
 
666
 
    switch(state) {
667
 
        case CONN_CONNECTED:
668
 
            if (kem->is_reply == 0) {
669
 
                /* Send a Key Exchange message to the correspondent */
670
 
                err = otrl_proto_create_key_exchange(us, messagep, context, 1,
671
 
                        create_privkey, create_privkey_data);
672
 
                if (err) return err;
673
 
            }
674
 
            if (context->their_keyid > 0 &&
675
 
                    (kem->keyid == context->their_keyid &&
676
 
                        !gcry_mpi_cmp(kem->dh_pubkey, context->their_y))
677
 
                    || (kem->keyid == (context->their_keyid - 1) &&
678
 
                        !gcry_mpi_cmp(kem->dh_pubkey, context->their_old_y))) {
679
 
                /* We've already seen this key of theirs, so all is
680
 
                 * good. */
681
 
                break;
682
 
            }
683
 
            /* It's an entirely different session; our correspondent has
684
 
             * gone away and come back. */
685
 
            otrl_context_force_setup(context);
686
 
 
687
 
            /* FALLTHROUGH */
688
 
        case CONN_UNCONNECTED:
689
 
        case CONN_SETUP:
690
 
            if (state == CONN_UNCONNECTED ||
691
 
                    (state == CONN_SETUP && kem->is_reply == 0)) {
692
 
                /* Send a Key Exchange message to the correspondent */
693
 
                err = otrl_proto_create_key_exchange(us, messagep, context, 1,
694
 
                        create_privkey, create_privkey_data);
695
 
                if (err) return err;
696
 
            }
697
 
            context->their_keyid = kem->keyid;
698
 
            gcry_mpi_release(context->their_y);
699
 
            context->their_y = gcry_mpi_copy(kem->dh_pubkey);
700
 
            err = otrl_dh_session(&(context->sesskeys[0][0]),
701
 
                    &(context->our_dh_key), context->their_y);
702
 
            if (err) return err;
703
 
            err = otrl_dh_session(&(context->sesskeys[1][0]),
704
 
                    &(context->our_old_dh_key), context->their_y);
705
 
            if (err) return err;
706
 
            context->state = CONN_CONNECTED;
707
 
            context->active_fingerprint = fprint;
708
 
            context->generation++;
709
 
            context->lastmessage = savedmessage;
710
 
            context->may_retransmit = savedmay_retransmit;
711
 
            context->lastsent = savedtime;
712
 
            break;
713
 
    }
714
 
 
715
 
    return gcry_error(GPG_ERR_NO_ERROR);
 
327
            return OTRL_MSGTYPE_NOTOTR;
 
328
        }
 
329
    }
 
330
 
 
331
    if (!strncmp(otrtag, "?OTR?", 5)) return OTRL_MSGTYPE_QUERY;
 
332
    if (!strncmp(otrtag, "?OTRv", 5)) return OTRL_MSGTYPE_QUERY;
 
333
    if (!strncmp(otrtag, "?OTR:AAIC", 9)) return OTRL_MSGTYPE_DH_COMMIT;
 
334
    if (!strncmp(otrtag, "?OTR:AAIK", 9)) return OTRL_MSGTYPE_DH_KEY;
 
335
    if (!strncmp(otrtag, "?OTR:AAIR", 9)) return OTRL_MSGTYPE_REVEALSIG;
 
336
    if (!strncmp(otrtag, "?OTR:AAIS", 9)) return OTRL_MSGTYPE_SIGNATURE;
 
337
    if (!strncmp(otrtag, "?OTR:AAEK", 9)) return OTRL_MSGTYPE_V1_KEYEXCH;
 
338
    if (!strncmp(otrtag, "?OTR:AAED", 9)) return OTRL_MSGTYPE_DATA;
 
339
    if (!strncmp(otrtag, "?OTR:AAID", 9)) return OTRL_MSGTYPE_DATA;
 
340
    if (!strncmp(otrtag, "?OTR Error:", 11)) return OTRL_MSGTYPE_ERROR;
 
341
 
 
342
    return OTRL_MSGTYPE_UNKNOWN;
716
343
}
717
344
 
718
345
/* Create an OTR Data message.  Pass the plaintext as msg, and an
719
346
 * optional chain of TLVs.  A newly-allocated string will be returned in
720
347
 * *encmessagep. */
721
348
gcry_error_t otrl_proto_create_data(char **encmessagep, ConnContext *context,
722
 
        const char *msg, OtrlTLV *tlvs)
 
349
        const char *msg, const OtrlTLV *tlvs, unsigned char flags)
723
350
{
724
351
    size_t justmsglen = strlen(msg);
725
352
    size_t msglen = justmsglen + 1 + otrl_tlv_seriallen(tlvs);
736
363
    char *msgbuf = NULL;
737
364
    enum gcry_mpi_format format = GCRYMPI_FMT_USG;
738
365
    char *msgdup;
 
366
    int version = context->protocol_version;
739
367
 
740
368
    /* Make sure we're actually supposed to be able to encrypt */
741
 
    if (context->state != CONN_CONNECTED || context->their_keyid == 0) {
 
369
    if (context->msgstate != OTRL_MSGSTATE_ENCRYPTED ||
 
370
            context->their_keyid == 0) {
742
371
        return gcry_error(GPG_ERR_CONFLICT);
743
372
    }
744
373
 
754
383
 
755
384
    /* Header, send keyid, recv keyid, counter, msg len, msg
756
385
     * len of revealed mac keys, revealed mac keys, MAC */
757
 
    buflen = 3 + 4 + 4 + 8 + 4 + msglen + 4 + reveallen + 20;
 
386
    buflen = 3 + (version == 2 ? 1 : 0) + 4 + 4 + 8 + 4 + msglen +
 
387
        4 + reveallen + 20;
758
388
    gcry_mpi_print(format, NULL, 0, &pubkeylen, context->our_dh_key.pub);
759
389
    buflen += pubkeylen + 4;
760
390
    buf = malloc(buflen);
770
400
    otrl_tlv_serialize(msgbuf + justmsglen + 1, tlvs);
771
401
    bufp = buf;
772
402
    lenp = buflen;
773
 
    memmove(bufp, "\x00\x01\x03", 3);  /* header */
 
403
    if (version == 1) {
 
404
        memmove(bufp, "\x00\x01\x03", 3);  /* header */
 
405
    } else {
 
406
        memmove(bufp, "\x00\x02\x03", 3);  /* header */
 
407
    }
774
408
    debug_data("Header", bufp, 3);
775
409
    bufp += 3; lenp -= 3;
 
410
    if (version == 2) {
 
411
        bufp[0] = flags;
 
412
        bufp += 1; lenp -= 1;
 
413
    }
776
414
    write_int(context->our_keyid-1);                    /* sender keyid */
777
415
    debug_int("Sender keyid", bufp-4);
778
416
    write_int(context->their_keyid);                    /* recipient keyid */
861
499
    return err;
862
500
}
863
501
 
 
502
/* Extract the flags from an otherwise unreadable Data Message. */
 
503
gcry_error_t otrl_proto_data_read_flags(const char *datamsg,
 
504
        unsigned char *flagsp)
 
505
{
 
506
    char *otrtag, *endtag;
 
507
    unsigned char *rawmsg = NULL;
 
508
    unsigned char *bufp;
 
509
    size_t msglen, rawlen, lenp;
 
510
    unsigned char version;
 
511
 
 
512
    if (flagsp) *flagsp = 0;
 
513
    otrtag = strstr(datamsg, "?OTR:");
 
514
    if (!otrtag) {
 
515
        goto invval;
 
516
    }
 
517
    endtag = strchr(otrtag, '.');
 
518
    if (endtag) {
 
519
        msglen = endtag-otrtag;
 
520
    } else {
 
521
        msglen = strlen(otrtag);
 
522
    }
 
523
 
 
524
    /* Base64-decode the message */
 
525
    rawlen = ((msglen-5) / 4) * 3;   /* maximum possible */
 
526
    rawmsg = malloc(rawlen);
 
527
    if (!rawmsg && rawlen > 0) {
 
528
        return gcry_error(GPG_ERR_ENOMEM);
 
529
    }
 
530
    rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5);  /* actual size */
 
531
 
 
532
    bufp = rawmsg;
 
533
    lenp = rawlen;
 
534
 
 
535
    require_len(3);
 
536
    if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) {
 
537
        /* Invalid header */
 
538
        goto invval;
 
539
    }
 
540
    version = bufp[1];
 
541
    bufp += 3; lenp -= 3;
 
542
 
 
543
    if (version == 2) {
 
544
        require_len(1);
 
545
        if (flagsp) *flagsp = bufp[0];
 
546
        bufp += 1; lenp -= 1;
 
547
    }
 
548
 
 
549
    free(rawmsg);
 
550
    return gcry_error(GPG_ERR_NO_ERROR);
 
551
 
 
552
invval:
 
553
    free(rawmsg);
 
554
    return gcry_error(GPG_ERR_INV_VALUE);
 
555
}
 
556
 
864
557
/* Accept an OTR Data Message in datamsg.  Decrypt it and put the
865
 
 * plaintext into *plaintextp, and any TLVs into tlvsp. */
 
558
 * plaintext into *plaintextp, and any TLVs into tlvsp.  Put any
 
559
 * received flags into *flagsp (if non-NULL). */
866
560
gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp,
867
 
        ConnContext *context, const char *datamsg)
 
561
        ConnContext *context, const char *datamsg, unsigned char *flagsp)
868
562
{
869
563
    char *otrtag, *endtag;
870
564
    gcry_error_t err;
880
574
    unsigned char *nul = NULL;
881
575
    unsigned char givenmac[20];
882
576
    DH_sesskeys *sess;
 
577
    unsigned char version;
883
578
 
884
579
    *plaintextp = NULL;
885
580
    *tlvsp = NULL;
 
581
    if (flagsp) *flagsp = 0;
886
582
    otrtag = strstr(datamsg, "?OTR:");
887
583
    if (!otrtag) {
888
584
        goto invval;
908
604
 
909
605
    macstart = bufp;
910
606
    require_len(3);
911
 
    if (memcmp(bufp, "\x00\x01\x03", 3)) {
 
607
    if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) {
912
608
        /* Invalid header */
913
609
        goto invval;
914
610
    }
 
611
    version = bufp[1];
915
612
    bufp += 3; lenp -= 3;
916
613
 
 
614
    if (version == 2) {
 
615
        require_len(1);
 
616
        if (flagsp) *flagsp = bufp[0];
 
617
        bufp += 1; lenp -= 1;
 
618
    }
917
619
    read_int(sender_keyid);
918
620
    read_int(recipient_keyid);
919
621
    read_mpi(sender_next_y);
1027
729
    free(rawmsg);
1028
730
    return err;
1029
731
}
 
732
 
 
733
/* Accumulate a potential fragment into the current context. */
 
734
OtrlFragmentResult otrl_proto_fragment_accumulate(char **unfragmessagep,
 
735
        ConnContext *context, const char *msg)
 
736
{
 
737
    OtrlFragmentResult res = OTRL_FRAGMENT_INCOMPLETE;
 
738
    const char *tag;
 
739
 
 
740
    tag = strstr(msg, "?OTR,");
 
741
    if (tag) {
 
742
        unsigned short n = 0, k = 0;
 
743
        int start = 0, end = 0;
 
744
 
 
745
        sscanf(tag, "?OTR,%hu,%hu,%n%*[^,],%n", &k, &n, &start, &end);
 
746
        if (k > 0 && n > 0 && k <= n && start > 0 && end > 0 && start < end) {
 
747
            if (k == 1) {
 
748
                int fraglen = end - start - 1;
 
749
                free(context->fragment);
 
750
                context->fragment = malloc(fraglen + 1);
 
751
                if (fraglen + 1 > fraglen && context->fragment) {
 
752
                    memmove(context->fragment, tag + start, fraglen);
 
753
                    context->fragment_len = fraglen;
 
754
                    context->fragment[context->fragment_len] = '\0';
 
755
                    context->fragment_n = n;
 
756
                    context->fragment_k = k;
 
757
                } else {
 
758
                    free(context->fragment);
 
759
                    context->fragment = NULL;
 
760
                    context->fragment_len = 0;
 
761
                    context->fragment_n = 0;
 
762
                    context->fragment_k = 0;
 
763
                }
 
764
            } else if (n == context->fragment_n &&
 
765
                    k == context->fragment_k + 1) {
 
766
                int fraglen = end - start - 1;
 
767
                char *newfrag = realloc(context->fragment,
 
768
                        context->fragment_len + fraglen + 1);
 
769
                if (context->fragment_len + fraglen + 1 > fraglen && newfrag) {
 
770
                    context->fragment = newfrag;
 
771
                    memmove(context->fragment + context->fragment_len,
 
772
                            tag + start, fraglen);
 
773
                    context->fragment_len += fraglen;
 
774
                    context->fragment[context->fragment_len] = '\0';
 
775
                    context->fragment_k = k;
 
776
                } else {
 
777
                    free(context->fragment);
 
778
                    context->fragment = NULL;
 
779
                    context->fragment_len = 0;
 
780
                    context->fragment_n = 0;
 
781
                    context->fragment_k = 0;
 
782
                }
 
783
            } else {
 
784
                free(context->fragment);
 
785
                context->fragment = NULL;
 
786
                context->fragment_len = 0;
 
787
                context->fragment_n = 0;
 
788
                context->fragment_k = 0;
 
789
            }
 
790
        }
 
791
 
 
792
        if (context->fragment_n > 0 &&
 
793
                context->fragment_n == context->fragment_k) {
 
794
            /* We've got a complete message */
 
795
            *unfragmessagep = context->fragment;
 
796
            context->fragment = NULL;
 
797
            context->fragment_len = 0;
 
798
            context->fragment_n = 0;
 
799
            context->fragment_k = 0;
 
800
            res = OTRL_FRAGMENT_COMPLETE;
 
801
        }
 
802
    } else {
 
803
        /* Unfragmented message, so discard any fragment we may have */
 
804
        free(context->fragment);
 
805
        context->fragment = NULL;
 
806
        context->fragment_len = 0;
 
807
        context->fragment_n = 0;
 
808
        context->fragment_k = 0;
 
809
        res = OTRL_FRAGMENT_UNFRAGMENTED;
 
810
    }
 
811
 
 
812
    return res;
 
813
}