Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

gdp / gdp / gdp_datum.c @ master

History | View | Annotate | Download (17.6 KB)

1
/* vim: set ai sw=4 sts=4 ts=4 :*/
2

    
3
/*
4
**  Message management
5
**
6
**                Messages contain the header and data information for a single
7
**                message.
8
**
9
**        ----- BEGIN LICENSE BLOCK -----
10
**        GDP: Global Data Plane Support Library
11
**        From the Ubiquitous Swarm Lab, 490 Cory Hall, U.C. Berkeley.
12
**
13
**        Copyright (c) 2015-2019, Regents of the University of California.
14
**        All rights reserved.
15
**
16
**        Permission is hereby granted, without written agreement and without
17
**        license or royalty fees, to use, copy, modify, and distribute this
18
**        software and its documentation for any purpose, provided that the above
19
**        copyright notice and the following two paragraphs appear in all copies
20
**        of this software.
21
**
22
**        IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
23
**        SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
24
**        PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
25
**        EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
**
27
**        REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
28
**        LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29
**        FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION,
30
**        IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO
31
**        OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
32
**        OR MODIFICATIONS.
33
**        ----- END LICENSE BLOCK -----
34
*/
35

    
36
#include <ep/ep.h>
37
#include <ep/ep_dbg.h>
38
#include <ep/ep_hexdump.h>
39
#include <ep/ep_prflags.h>
40
#include <ep/ep_string.h>
41
#include <ep/ep_thr.h>
42

    
43
#include "gdp.h"
44
#include "gdp_md.h"
45
#include "gdp_priv.h"
46

    
47
#include <string.h>
48

    
49
static EP_DBG        Dbg = EP_DBG_INIT("gdp.datum", "GDP datum processing");
50

    
51
static gdp_datum_t                *DatumFreeList;
52
static EP_THR_MUTEX                DatumFreeListMutex EP_THR_MUTEX_INITIALIZER2(GDP_MUTEX_LORDER_LEAF);
53

    
54

    
55
/*
56
**  Create a new datum.
57
**                The datum is returned unlocked with a data buffer.
58
*/
59

    
60
gdp_datum_t *
61
gdp_datum_new(void)
62
{
63
        gdp_datum_t *datum;
64

    
65
        // get a message off the free list, if any
66
        ep_thr_mutex_lock(&DatumFreeListMutex);
67
        if ((datum = DatumFreeList) != NULL)
68
        {
69
                DatumFreeList = datum->next;
70
        }
71
        ep_thr_mutex_unlock(&DatumFreeListMutex);
72

    
73
        if (datum == NULL)
74
        {
75
                // nothing on the free list; allocate anew
76
                datum = (gdp_datum_t *) ep_mem_zalloc(sizeof *datum);
77
                ep_thr_mutex_init(&datum->mutex, EP_THR_MUTEX_DEFAULT);
78
                ep_thr_mutex_setorder(&datum->mutex, GDP_MUTEX_LORDER_DATUM);
79
        }
80
        datum->next = NULL;
81

    
82
        EP_ASSERT(!EP_UT_BITSET(GDP_DF_INUSE, datum->flags));
83

    
84
        // initialize metadata
85
        gdp_datum_reset(datum);
86
        ep_dbg_cprintf(Dbg, 48, "gdp_datum_new => %p\n", datum);
87
        VALGRIND_HG_CLEAN_MEMORY(datum, sizeof *datum);
88
        return datum;
89
}
90

    
91

    
92
void
93
gdp_datum_free(gdp_datum_t *datum)
94
{
95
        ep_dbg_cprintf(Dbg, 48, "gdp_datum_free(%p)\n", datum);
96

    
97
        // sanity
98
        if (datum == NULL)
99
                return;
100
        EP_ASSERT_ELSE(EP_UT_BITSET(GDP_DF_INUSE, datum->flags), return);
101
        datum->flags = 0;
102

    
103
        if (datum->dbuf != NULL)
104
        {
105
                size_t ndrain = gdp_buf_getlength(datum->dbuf);
106
                ep_dbg_cprintf(Dbg, 50, "  ... draining %zd bytes\n", ndrain);
107
                if (ndrain > 0)
108
                        gdp_buf_drain(datum->dbuf, ndrain);
109
        }
110
        if (datum->sig != NULL)
111
        {
112
                //XXX retain this buffer?
113
                gdp_sig_free(datum->sig);
114
                datum->sig = NULL;
115
        }
116

    
117
        // make sure the datum is unlocked before putting on the free list
118
        if (ep_thr_mutex_trylock(&datum->mutex) != 0)
119
        {
120
                // shouldn't happen
121
                ep_dbg_cprintf(Dbg, 1, "gdp_datum_free(%p): was locked\n", datum);
122
        }
123
        ep_thr_mutex_unlock(&datum->mutex);
124
#if GDP_DEBUG_NO_FREE_LISTS                // avoid helgrind complaints
125
        ep_thr_mutex_destroy(&datum->mutex);
126
        if (datum->dbuf == NULL)
127
                gdp_buf_free(datum->dbuf);
128
        ep_mem_free(datum);
129
#else
130
        ep_thr_mutex_lock(&DatumFreeListMutex);
131
        datum->next = DatumFreeList;
132
        DatumFreeList = datum;
133
        ep_thr_mutex_unlock(&DatumFreeListMutex);
134
#endif
135
}
136

    
137

    
138
/*
139
**  Reset a datum
140
**
141
**                This resets the data and signature buffers, but leaves the
142
**                timestamp and recno since the caller might like to see the
143
**                updated values.
144
*/
145

    
146
void
147
gdp_datum_reset(gdp_datum_t *datum)
148
{
149
        if (datum->dbuf == NULL)
150
                datum->dbuf = gdp_buf_new();
151
        else
152
                gdp_buf_reset(datum->dbuf);
153
        if (datum->sig != NULL)
154
                gdp_sig_reset(datum->sig);
155
        datum->recno = GDP_PDU_NO_RECNO;
156
        datum->flags = GDP_DF_INUSE;
157
        EP_TIME_INVALIDATE(&datum->ts);
158
}
159

    
160

    
161
gdp_recno_t
162
gdp_datum_getrecno(const gdp_datum_t *datum)
163
{
164
        return datum->recno;
165
}
166

    
167
void
168
gdp_datum_getts(const gdp_datum_t *datum, EP_TIME_SPEC *ts)
169
{
170
        memcpy(ts, &datum->ts, sizeof *ts);
171
}
172

    
173
size_t
174
gdp_datum_getdlen(const gdp_datum_t *datum)
175
{
176
        return gdp_buf_getlength(datum->dbuf);
177
}
178

    
179
gdp_buf_t *
180
gdp_datum_getbuf(const gdp_datum_t *datum)
181
{
182
        return datum->dbuf;
183
}
184

    
185
gdp_sig_t *
186
gdp_datum_getsig(const gdp_datum_t *datum)
187
{
188
        return datum->sig;
189
}
190

    
191

    
192
/*
193
**        GDP_DATUM_PRINT --- print a datum (for debugging)
194
*/
195

    
196
static EP_PRFLAGS_DESC        DatumFlagsDesc[] =
197
{
198
        { GDP_DF_INUSE,                GDP_DF_INUSE,                "INUSE"                        },
199
        { GDP_DF_GOODSIG,        GDP_DF_GOODSIG,                "GOODSIG"                },
200
        { 0,                                0,                                        NULL                        }
201
};
202

    
203
void
204
gdp_datum_print(const gdp_datum_t *datum, FILE *fp, uint32_t flags)
205
{
206
        unsigned char *d;
207
        int l;
208
        bool quiet = EP_UT_BITSET(GDP_DATUM_PRQUIET, flags);
209
        bool debug = EP_UT_BITSET(GDP_DATUM_PRDEBUG, flags);
210

    
211
        if (quiet && (debug || EP_UT_BITSET(GDP_DATUM_PRMETAONLY, flags)))
212
                quiet = false;
213

    
214
        if (!EP_ASSERT(fp != NULL))
215
                return;
216

    
217
        flockfile(fp);
218
        if (debug)
219
                fprintf(fp, "datum @ %p: ", datum);
220
        if (datum == NULL)
221
        {
222
                if (!quiet)
223
                        fprintf(fp, "null datum\n");
224
                goto done;
225
        }
226

    
227
        if (!quiet)
228
                fprintf(fp, "recno %" PRIgdp_recno ", ", datum->recno);
229

    
230
        if (datum->dbuf == NULL)
231
        {
232
                if (!quiet)
233
                        fprintf(fp, "no data");
234
                d = NULL;
235
                l = -1;
236
        }
237
        else
238
        {
239
                l = gdp_buf_getlength(datum->dbuf);
240
                if (!quiet)
241
                        fprintf(fp, "len %d", l);
242
                if (l > 0)
243
                        d = gdp_buf_getptr(datum->dbuf, l);
244
                else
245
                        d = (unsigned char *) "";
246
        }
247

    
248
        if (!quiet)
249
        {
250
                if (EP_TIME_IS_VALID(&datum->ts))
251
                {
252
                        fprintf(fp, ", ts ");
253
                        ep_time_print(&datum->ts, fp, EP_TIME_FMT_HUMAN);
254
                }
255
                else
256
                {
257
                        fprintf(fp, ", no timestamp");
258
                }
259

    
260
                if (debug)
261
                {
262
                        fprintf(fp, ", flags ");
263
                        ep_prflags(datum->flags, DatumFlagsDesc, fp);
264
                }
265
                fprintf(fp, "\n");
266
        }
267

    
268
        if (!EP_UT_BITSET(GDP_DATUM_PRMETAONLY, flags))
269
        {
270
                if (EP_UT_BITSET(GDP_DATUM_PRTEXT, flags))
271
                        fprintf(fp, "%.*s\n", l, d);
272
                else if (EP_UT_BITSET(GDP_DATUM_PRBINARY, flags))
273
                        fwrite(fp, l, 1, fp);
274
                else
275
                        ep_hexdump(d, l, fp, EP_HEXDUMP_ASCII, 0);
276
        }
277

    
278
        if (EP_UT_BITSET(GDP_DATUM_PRSIG, flags))
279
        {
280
                if (datum->sig != NULL)
281
                {
282
                        size_t siglen;
283
                        d = gdp_sig_getptr(datum->sig, &siglen);
284
                        if (siglen > 0)
285
                        {
286
                                fprintf(fp, "  sig\n");
287
                                ep_hexdump(d, siglen, fp, EP_HEXDUMP_HEX, 0);
288
                        }
289
                        else
290
                                fprintf(fp, "  empty sig\n");
291
                }
292
                else
293
                        fprintf(fp, "  no sig\n");
294
        }
295
done:
296
        funlockfile(fp);
297
}
298

    
299

    
300
/*
301
**  Copy contents of one datum into another
302
*/
303

    
304
void
305
gdp_datum_copy(gdp_datum_t *to, const gdp_datum_t *from)
306
{
307
        to->recno = from->recno;
308
        to->ts = from->ts;
309
        to->flags = from->flags;
310
        if (from->dbuf != NULL)
311
        {
312
                if (EP_ASSERT(to->dbuf != NULL))
313
                        gdp_buf_copy(to->dbuf, from->dbuf);
314
        }
315
        if (from->sig != NULL)
316
        {
317
                if (EP_ASSERT(to->sig != NULL))
318
                        gdp_sig_copy(to->sig, from->sig);
319
        }
320
}
321

    
322

    
323
/*
324
**  Duplicate a datum (internal use)
325
*/
326

    
327
gdp_datum_t *
328
gdp_datum_dup(const gdp_datum_t *datum)
329
{
330
        gdp_datum_t *ndatum;
331

    
332
        ndatum = gdp_datum_new();
333
        ndatum->recno = datum->recno;
334
        ndatum->ts = datum->ts;
335
        gdp_buf_copy(ndatum->dbuf, datum->dbuf);
336
        if (datum->sig != NULL)
337
                ndatum->sig = gdp_sig_dup(datum->sig);
338

    
339
        return ndatum;
340
}
341

    
342

    
343
/*
344
**  Print a datum (for debugging)
345
*/
346

    
347
void
348
_gdp_datum_dump(const gdp_datum_t *datum,
349
                        FILE *fp)
350
{
351
        if (fp == NULL)
352
                fp = ep_dbg_getfile();
353
        gdp_datum_print(datum, fp, GDP_DATUM_PRDEBUG);
354
}
355

    
356
/*
357
**  Incorporate a datum into a pre-existing message digest.
358
**  This can also be used when signing and verifying.
359
**
360
**  The message digest must already be initialized, and can already
361
**  have data included in it.  Generally speaking, this will include at
362
**  a minimum the name of the log in which this data is stored, which
363
**  itself will be the hash of the metadata for that log.  This allows
364
**  this routine to be usable for both computing a basic hash and for
365
**  signing.
366
**
367
**  This routine adds to the existing digest:
368
**                * record number
369
**                * timestamp
370
**                * hash of previous datum
371
**                * the "proof" (hash back pointers and offsets)
372
**                * hash of data payload
373
**
374
**        If the datum does not yet include the hash of the data payload,
375
**        that will be computed and cached.
376
*/
377

    
378
EP_STAT
379
_gdp_datum_digest(gdp_datum_t *datum, EP_CRYPTO_MD *md)
380
{
381
        if (ep_dbg_test(Dbg, 50))
382
        {
383
                ep_dbg_printf("_gdp_datum_digest: ");
384
                _gdp_datum_dump(datum, NULL);
385
        }
386

    
387
        // check for upstream problems and avoid core dumps
388
        if (md == NULL)
389
                return GDP_STAT_CRYPTO_ERROR;
390

    
391
        // now compute H(recno || timestamp || prevHash || proof || H(data))
392
        // recno
393
        {
394
                uint8_t recnobuf[8];                // 64 bits
395
                uint8_t *pbp = recnobuf;
396
                PUT64(datum->recno);
397
                ep_crypto_md_update(md, &recnobuf, sizeof recnobuf);
398
        }
399
        // timestamp
400
        {
401
                uint8_t tsbuf[16];                        // 64 + 32 + 32 bits
402
                uint8_t *pbp = tsbuf;
403
                PUT64(datum->ts.tv_sec);
404
                PUT32(datum->ts.tv_nsec);
405
                PUT32(*(uint32_t *) &datum->ts.tv_accuracy);
406
                ep_crypto_md_update(md, tsbuf, sizeof tsbuf);
407
        }
408
        // prevhash
409
        if (datum->prevhash != NULL)
410
        {
411
                size_t prevhashlen;
412
                void *hashbytes = gdp_hash_getptr(datum->prevhash, &prevhashlen);
413
                ep_crypto_md_update(md, hashbytes, prevhashlen);
414
        }
415
        // proof
416
        //TODO: include proof
417
        // data hash
418
        {
419
                int hashalg = ep_crypto_md_type(md);
420
                EP_CRYPTO_MD *dmd;
421
                EP_STAT estat = ep_crypto_md_new(hashalg, &dmd);
422

    
423
                if (!EP_STAT_ISOK(estat))
424
                {
425
                        char ebuf[100];
426
                        ep_dbg_cprintf(Dbg, 9,
427
                                        "_gdp_datum_digest: ep_crypto_md_new(%d) => %s\n",
428
                                        hashalg, ep_stat_tostr(estat, ebuf, sizeof ebuf));
429
                        return estat;
430
                }
431
                if (datum->dbuf != NULL)
432
                {
433
                        size_t dlen = gdp_buf_getlength(datum->dbuf);
434
                        ep_crypto_md_update(dmd, gdp_buf_getptr(datum->dbuf, dlen), dlen);
435
                }
436

    
437
                uint8_t dhash[EP_CRYPTO_MAX_DIGEST];
438
                size_t dhlen = sizeof dhash;
439
                ep_crypto_md_final(dmd, &dhash, &dhlen);
440
                ep_crypto_md_free(dmd);
441
                ep_crypto_md_update(md, dhash, dhlen);
442
        }
443
        return EP_STAT_OK;
444
}
445

    
446

    
447
/*
448
**  Compute hash of a datum.
449
**
450
**                Two versions, one using a GIN, other using a GOB.
451
**                This is not usable for signing or verification.
452
*/
453

    
454
gdp_hash_t *
455
gdp_datum_hash(gdp_datum_t *datum, gdp_gin_t *gin)
456
{
457
        return _gdp_datum_hash(datum, gin->gob);
458
}
459

    
460

    
461
gdp_hash_t *
462
_gdp_datum_hash(gdp_datum_t *datum, gdp_gob_t *gob)
463
{
464
        if (gob->vrfy_ctx == NULL)
465
        {
466
                EP_STAT estat = _gdp_gob_init_vrfy_ctx(gob);
467
                if (gob->vrfy_ctx == NULL)
468
                {
469
                        char ebuf[100];
470
                        ep_dbg_cprintf(Dbg, 3, "_gdp_datum_hash(init): %s\n",
471
                                        ep_stat_tostr(estat, ebuf, sizeof ebuf));
472
                        return NULL;
473
                }
474
        }
475

    
476
        EP_CRYPTO_MD *md = ep_crypto_md_clone(gob->vrfy_ctx);
477
        _gdp_datum_digest(datum, md);
478
        uint8_t mdbuf[EP_CRYPTO_MAX_DIGEST];
479
        size_t mdlen = sizeof mdbuf;
480
        ep_crypto_md_final(md, &mdbuf, &mdlen);
481
        ep_crypto_md_free(md);
482
        gdp_hash_t *hash = gdp_hash_new(gob->hashalg, mdbuf, mdlen);
483
        return hash;
484
}
485

    
486

    
487
/*
488
**  Check a datum hash for equality
489
*/
490

    
491
bool
492
gdp_datum_hash_equal(gdp_datum_t *datum,
493
                                        const gdp_gin_t *gin,
494
                                        const gdp_hash_t *hash)
495
{
496
        return _gdp_datum_hash_equal(datum, gin->gob, hash);
497
}
498

    
499
bool
500
_gdp_datum_hash_equal(gdp_datum_t *datum,
501
                                        gdp_gob_t *gob,
502
                                        const gdp_hash_t *hash)
503
{
504
        bool r = false;
505
        gdp_hash_t *newhash = _gdp_datum_hash(datum, gob);
506

    
507
        if (gdp_hash_equal(hash, newhash))
508
                r = true;
509
        gdp_hash_free(newhash);
510
        return r;
511
}
512

    
513

    
514
/*
515
**  Convert internal datum structure to protobuf-encoded datum.
516
**  Assumes the protobuf version is empty on entry.
517
*/
518

    
519
void
520
_gdp_datum_to_pb(const gdp_datum_t *datum,
521
                                GdpMessage *msg,
522
                                GdpDatum *pbd)
523
{
524
        // recno
525
        pbd->recno = datum->recno;
526

    
527
        // timestamp
528
        if (EP_TIME_IS_VALID(&datum->ts))
529
        {
530
                if (pbd->ts == NULL)
531
                {
532
                        pbd->ts = (GdpTimestamp *) ep_mem_zalloc(sizeof *pbd->ts);
533
                        gdp_timestamp__init(pbd->ts);
534
                }
535
                pbd->ts->sec = datum->ts.tv_sec;
536
                pbd->ts->has_sec = true;
537
                pbd->ts->nsec = datum->ts.tv_nsec;
538
                pbd->ts->has_nsec = pbd->ts->nsec != 0;
539
                pbd->ts->accuracy = datum->ts.tv_accuracy;
540
                pbd->ts->has_accuracy = pbd->ts->accuracy != 0.0;
541
        }
542
        else if (pbd->ts != NULL)
543
        {
544
                ep_dbg_cprintf(Dbg, 3, "_gdp_datum_to_pb: freeing ts\n");
545
                gdp_timestamp__free_unpacked(pbd->ts, NULL);
546
                pbd->ts = NULL;
547
        }
548

    
549
        // data payload
550
        if (datum->dbuf != NULL && gdp_buf_getlength(datum->dbuf) > 0)
551
        {
552
                size_t l = gdp_buf_getlength(datum->dbuf);
553
                pbd->data.len = l;
554
                pbd->data.data = (uint8_t *) ep_mem_malloc(l);
555
                memcpy(pbd->data.data, gdp_buf_getptr(datum->dbuf, l), l);
556
        }
557

    
558
        // hash of previous record
559
        if (datum->prevhash == NULL)
560
                pbd->has_prevhash = false;
561
        else
562
        {
563
                size_t l = gdp_hash_getlength(datum->prevhash);
564
                pbd->has_prevhash = true;
565
                pbd->prevhash.len = l;
566
                if (pbd->prevhash.data != NULL)
567
                        ep_mem_free(pbd->prevhash.data);
568
                pbd->prevhash.data = (uint8_t *) ep_mem_malloc(l);
569
                memcpy(pbd->prevhash.data, gdp_hash_getptr(datum->prevhash, NULL), l);
570
        }
571

    
572
        // signature
573
        if (datum->sig != NULL)
574
        {
575
                if (pbd->sig == NULL)
576
                {
577
                        pbd->sig = (GdpSignature *) ep_mem_malloc(sizeof *pbd->sig);
578
                        gdp_signature__init(pbd->sig);
579
                }
580
                size_t l;
581
                void *sigdata = gdp_sig_getptr(datum->sig, &l);
582
                pbd->sig->sig.len = l;
583
                if (pbd->sig->sig.data != NULL)
584
                        ep_mem_free(pbd->sig->sig.data);
585
                pbd->sig->sig.data = ep_mem_malloc(l);
586
                memcpy(pbd->sig->sig.data, sigdata, l);
587
        }
588
        else if (pbd->sig != NULL)
589
        {
590
                ep_dbg_cprintf(Dbg, 3, "_gdp_datum_to_pb: freeing sig\n");
591
                gdp_signature__free_unpacked(pbd->sig, NULL);
592
                pbd->sig = NULL;
593
        }
594
}
595

    
596

    
597
/*
598
**  Convert protobuf-encoded datum to internal datum structure.
599
*/
600

    
601
void
602
_gdp_timestamp_from_pb(EP_TIME_SPEC *ts,
603
                                const GdpTimestamp *pbd)
604
{
605
        if (pbd != NULL)
606
        {
607
                ts->tv_sec = pbd->sec;
608
                ts->tv_nsec = pbd->nsec;
609
                ts->tv_accuracy = pbd->accuracy;
610
        }
611
        else
612
        {
613
                EP_TIME_INVALIDATE(ts);
614
        }
615
}
616

    
617
void
618
_gdp_datum_from_pb(gdp_datum_t *datum,
619
                                const GdpDatum *pbd,
620
                                const GdpSignature *sig)
621
{
622
        // recno
623
        datum->recno = pbd->recno;
624

    
625
        // timestamp
626
        _gdp_timestamp_from_pb(&datum->ts, pbd->ts);
627

    
628
        // data
629
        if (datum->dbuf == NULL)
630
                datum->dbuf = gdp_buf_new();
631
        gdp_buf_write(datum->dbuf, pbd->data.data, pbd->data.len);
632

    
633
        // signature
634
        if (sig != NULL)
635
        {
636
                if (datum->sig == NULL)
637
                        datum->sig = gdp_sig_new(0, NULL, 0);
638
                gdp_sig_set(datum->sig, sig->sig.data, sig->sig.len);
639
        }
640
}
641

    
642

    
643
/*
644
**  Sign a datum
645
**
646
**                gob->digest must already be set up to contain the header part
647
**                of the signature before this is called.  Since this also
648
**                includes the secret key, it isn't passed in here.
649
**                The signature is left in the datum.
650
*/
651

    
652
EP_STAT
653
_gdp_datum_sign(gdp_datum_t *datum, gdp_gob_t *gob)
654
{
655
        EP_STAT estat;
656

    
657
        if (gob->sign_ctx == NULL)
658
        {
659
                ep_dbg_cprintf(Dbg, 1, "_gdp_datum_sign: null GOB signature digest\n");
660
                return GDP_STAT_CRYPTO_SKEY_REQUIRED;
661
        }
662

    
663
        EP_CRYPTO_MD *md = ep_crypto_md_clone(gob->sign_ctx);
664
        _gdp_datum_digest(datum, md);
665

    
666
        // now get the final signature
667
        uint8_t sigbuf[EP_CRYPTO_MAX_SIG];
668
        size_t siglen = sizeof sigbuf;
669
        estat = ep_crypto_sign_final(md, sigbuf, &siglen);
670

    
671
        // transfer the signature into the datum
672
        if (EP_STAT_ISOK(estat))
673
        {
674
                if (datum->sig == NULL)
675
                        datum->sig = gdp_sig_new(gob->hashalg, NULL, 0);
676
                gdp_sig_set(datum->sig, sigbuf, siglen);
677
        }
678
        else if (ep_dbg_test(Dbg, 1))
679
        {
680
                char ebuf[100];
681
                ep_dbg_printf("_gdp_datum_sign: ep_crypto_sign_final => %s\n",
682
                                        ep_stat_tostr(estat, ebuf, sizeof ebuf));
683
        }
684

    
685
        // zero out sigbuf (good hygiene)
686
        memset(sigbuf, 0, sizeof sigbuf);
687
        return estat;
688
}
689

    
690

    
691
/*
692
**  Verify datum signature
693
**
694
**                This also initializes the message digest (hash) field, even if
695
**                we don't have a public key, so it's important that this be
696
**                called even if we don't care about the signature.
697
*/
698

    
699
EP_STAT
700
gdp_datum_vrfy(gdp_datum_t *datum, gdp_gin_t *gin)
701
{
702
        // if signature already verified, don't do it again
703
        if (EP_UT_BITSET(GDP_DF_GOODSIG, datum->flags))
704
        {
705
                ep_dbg_cprintf(Dbg, 32, "gdp_datum_vrfy: already OK\n");
706
                return EP_STAT_OK;
707
        }
708

    
709
        return _gdp_datum_vrfy_gob(datum, gin->gob);
710
}
711

    
712
EP_STAT
713
_gdp_datum_vrfy_gob(gdp_datum_t *datum, gdp_gob_t *gob)
714
{
715
        EP_STAT estat = EP_STAT_OK;
716

    
717
        if (gob->vrfy_ctx == NULL)
718
        {
719
                // sets up message digest hash even if no public key
720
                estat = _gdp_gob_init_vrfy_ctx(gob);
721
                if (!EP_STAT_ISOK(estat))
722
                {
723
                        char ebuf[100];
724
                        ep_dbg_cprintf(Dbg, 30,
725
                                        "gdp_datum_vrfy(init): %s\n",
726
                                        ep_stat_tostr(estat, ebuf, sizeof ebuf));
727
                        estat = GDP_STAT_CRYPTO_NO_PUB_KEY;
728
                        goto nopubkey;
729
                }
730
        }
731

    
732
        // if we aren't verifying we can quit now
733
        if (!EP_UT_BITSET(GOBF_VERIFYING, gob->flags))
734
        {
735
                // not verifying: no public key
736
                ep_dbg_cprintf(Dbg, 21, "gdp_datum_vrfy: no pub key\n");
737
                return GDP_STAT_CRYPTO_NO_PUB_KEY;
738
        }
739

    
740
        // if there's no signature we can give up now
741
        size_t siglen;
742
        if (datum->sig == NULL || (siglen = gdp_sig_getlength(datum->sig)) <= 0)
743
        {
744
                ep_dbg_cprintf(Dbg, 31, "gdp_datum_vrfy: no signature\n");
745
                return GDP_STAT_CRYPTO_SIG_MISSING;
746
        }
747

    
748
        EP_CRYPTO_MD *md = ep_crypto_md_clone(gob->vrfy_ctx);
749

    
750
        _gdp_datum_digest(datum, md);
751
        size_t len = gdp_sig_getlength(datum->sig);
752
        estat = ep_crypto_vrfy_final(md, gdp_sig_getptr(datum->sig, NULL), len);
753
        ep_crypto_md_free(md);
754
        if (!EP_STAT_ISOK(estat))
755
        {
756
                // error: signature failure
757
                char ebuf[100];
758
                ep_dbg_cprintf(Dbg, 31, "gdp_datum_vrfy(final): %s\n",
759
                                        ep_stat_tostr(estat, ebuf, sizeof ebuf));
760
                if (EP_UT_BITSET(GOBF_VRFY_WARN, gob->flags))
761
                {
762
                        ep_dbg_cprintf(Dbg, 31,
763
                                                "gdp_datum_vrfy: signature failure (warn)\n");
764
                        estat = EP_STAT_OK;                //XXX: perhaps a warning status?
765

    
766
                        // pretend the verification succeeded
767
                        datum->flags |= GDP_DF_GOODSIG;
768
                }
769
                else
770
                {
771
                        ep_dbg_cprintf(Dbg, 1,
772
                                                "gdp_datum_vrfy: signature failure (fail)\n");
773
                        estat = GDP_STAT_CRYPTO_VRFY_FAIL;
774
                }
775
        }
776
        else
777
        {
778
                // successful verification
779
                datum->flags |= GDP_DF_GOODSIG;
780

    
781
nopubkey:
782
                {
783
                        int lev = EP_STAT_ISOK(estat) ? 51 : 31;
784
                        char ebuf[100];
785
                        ep_dbg_cprintf(Dbg, lev, "gdp_datum_vrfy: %s\n",
786
                                                ep_stat_tostr(estat, ebuf, sizeof ebuf));
787
                }
788
        }
789
        return estat;
790
}