1 /* $OpenBSD: mft.c,v 1.121 2024/12/24 10:03:59 tb Exp $ */
2 /*
3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <assert.h>
20 #include <err.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <openssl/bn.h>
28 #include <openssl/asn1.h>
29 #include <openssl/asn1t.h>
30 #include <openssl/safestack.h>
31 #include <openssl/sha.h>
32 #include <openssl/stack.h>
33 #include <openssl/x509.h>
34
35 #include "extern.h"
36
37 extern ASN1_OBJECT *mft_oid;
38 BN_CTX *bn_ctx;
39
40 /*
41 * Types and templates for the Manifest eContent, RFC 6486, section 4.2.
42 */
43
44 ASN1_ITEM_EXP FileAndHash_it;
45 ASN1_ITEM_EXP Manifest_it;
46
47 typedef struct {
48 ASN1_IA5STRING *file;
49 ASN1_BIT_STRING *hash;
50 } FileAndHash;
51
52 DECLARE_STACK_OF(FileAndHash);
53
54 #ifndef DEFINE_STACK_OF
55 #define sk_FileAndHash_dup(sk) SKM_sk_dup(FileAndHash, (sk))
56 #define sk_FileAndHash_free(sk) SKM_sk_free(FileAndHash, (sk))
57 #define sk_FileAndHash_num(sk) SKM_sk_num(FileAndHash, (sk))
58 #define sk_FileAndHash_value(sk, i) SKM_sk_value(FileAndHash, (sk), (i))
59 #define sk_FileAndHash_sort(sk) SKM_sk_sort(FileAndHash, (sk))
60 #define sk_FileAndHash_set_cmp_func(sk, cmp) \
61 SKM_sk_set_cmp_func(FileAndHash, (sk), (cmp))
62 #endif
63
64 typedef struct {
65 ASN1_INTEGER *version;
66 ASN1_INTEGER *manifestNumber;
67 ASN1_GENERALIZEDTIME *thisUpdate;
68 ASN1_GENERALIZEDTIME *nextUpdate;
69 ASN1_OBJECT *fileHashAlg;
70 STACK_OF(FileAndHash) *fileList;
71 } Manifest;
72
73 ASN1_SEQUENCE(FileAndHash) = {
74 ASN1_SIMPLE(FileAndHash, file, ASN1_IA5STRING),
75 ASN1_SIMPLE(FileAndHash, hash, ASN1_BIT_STRING),
76 } ASN1_SEQUENCE_END(FileAndHash);
77
78 ASN1_SEQUENCE(Manifest) = {
79 ASN1_EXP_OPT(Manifest, version, ASN1_INTEGER, 0),
80 ASN1_SIMPLE(Manifest, manifestNumber, ASN1_INTEGER),
81 ASN1_SIMPLE(Manifest, thisUpdate, ASN1_GENERALIZEDTIME),
82 ASN1_SIMPLE(Manifest, nextUpdate, ASN1_GENERALIZEDTIME),
83 ASN1_SIMPLE(Manifest, fileHashAlg, ASN1_OBJECT),
84 ASN1_SEQUENCE_OF(Manifest, fileList, FileAndHash),
85 } ASN1_SEQUENCE_END(Manifest);
86
87 DECLARE_ASN1_FUNCTIONS(Manifest);
88 IMPLEMENT_ASN1_FUNCTIONS(Manifest);
89
90 #define GENTIME_LENGTH 15
91
92 /*
93 * Determine rtype corresponding to file extension. Returns RTYPE_INVALID
94 * on error or unknown extension.
95 */
96 enum rtype
rtype_from_file_extension(const char * fn)97 rtype_from_file_extension(const char *fn)
98 {
99 size_t sz;
100
101 sz = strlen(fn);
102 if (sz < 5)
103 return RTYPE_INVALID;
104
105 if (strcasecmp(fn + sz - 4, ".tal") == 0)
106 return RTYPE_TAL;
107 if (strcasecmp(fn + sz - 4, ".cer") == 0)
108 return RTYPE_CER;
109 if (strcasecmp(fn + sz - 4, ".crl") == 0)
110 return RTYPE_CRL;
111 if (strcasecmp(fn + sz - 4, ".mft") == 0)
112 return RTYPE_MFT;
113 if (strcasecmp(fn + sz - 4, ".roa") == 0)
114 return RTYPE_ROA;
115 if (strcasecmp(fn + sz - 4, ".gbr") == 0)
116 return RTYPE_GBR;
117 if (strcasecmp(fn + sz - 4, ".sig") == 0)
118 return RTYPE_RSC;
119 if (strcasecmp(fn + sz - 4, ".asa") == 0)
120 return RTYPE_ASPA;
121 if (strcasecmp(fn + sz - 4, ".tak") == 0)
122 return RTYPE_TAK;
123 if (strcasecmp(fn + sz - 4, ".csv") == 0)
124 return RTYPE_GEOFEED;
125 if (strcasecmp(fn + sz - 4, ".spl") == 0)
126 return RTYPE_SPL;
127
128 return RTYPE_INVALID;
129 }
130
131 /*
132 * Validate that a filename listed on a Manifest only contains characters
133 * permitted in RFC 9286 section 4.2.2.
134 * Also ensure that there is exactly one '.'.
135 */
136 static int
valid_mft_filename(const char * fn,size_t len)137 valid_mft_filename(const char *fn, size_t len)
138 {
139 const unsigned char *c;
140
141 if (!valid_filename(fn, len))
142 return 0;
143
144 c = memchr(fn, '.', len);
145 if (c == NULL || c != memrchr(fn, '.', len))
146 return 0;
147
148 return 1;
149 }
150
151 /*
152 * Check that the file is allowed to be part of a manifest and the parser
153 * for this type is implemented in rpki-client.
154 * Returns corresponding rtype or RTYPE_INVALID to mark the file as unknown.
155 */
156 static enum rtype
rtype_from_mftfile(const char * fn)157 rtype_from_mftfile(const char *fn)
158 {
159 enum rtype type;
160
161 type = rtype_from_file_extension(fn);
162 switch (type) {
163 case RTYPE_CER:
164 case RTYPE_CRL:
165 case RTYPE_GBR:
166 case RTYPE_ROA:
167 case RTYPE_ASPA:
168 case RTYPE_SPL:
169 case RTYPE_TAK:
170 return type;
171 default:
172 return RTYPE_INVALID;
173 }
174 }
175
176 /*
177 * Parse an individual "FileAndHash", RFC 6486, sec. 4.2.
178 * Return zero on failure, non-zero on success.
179 */
180 static int
mft_parse_filehash(const char * fn,struct mft * mft,const FileAndHash * fh,int * found_crl)181 mft_parse_filehash(const char *fn, struct mft *mft, const FileAndHash *fh,
182 int *found_crl)
183 {
184 char *file = NULL;
185 int rc = 0;
186 struct mftfile *fent;
187 enum rtype type;
188 size_t new_idx = 0;
189
190 if (!valid_mft_filename(fh->file->data, fh->file->length)) {
191 warnx("%s: RFC 6486 section 4.2.2: bad filename", fn);
192 goto out;
193 }
194 file = strndup(fh->file->data, fh->file->length);
195 if (file == NULL)
196 err(1, NULL);
197
198 if (fh->hash->length != SHA256_DIGEST_LENGTH) {
199 warnx("%s: RFC 6486 section 4.2.1: hash: "
200 "invalid SHA256 length, have %d", fn, fh->hash->length);
201 goto out;
202 }
203
204 type = rtype_from_mftfile(file);
205 if (type == RTYPE_CRL) {
206 if (*found_crl == 1) {
207 warnx("%s: RFC 6487: too many CRLs listed on MFT", fn);
208 goto out;
209 }
210 if (strcmp(file, mft->crl) != 0) {
211 warnx("%s: RFC 6487: name (%s) doesn't match CRLDP "
212 "(%s)", fn, file, mft->crl);
213 goto out;
214 }
215 /* remember the filehash for the CRL in struct mft */
216 memcpy(mft->crlhash, fh->hash->data, SHA256_DIGEST_LENGTH);
217 *found_crl = 1;
218 }
219
220 if (filemode)
221 fent = &mft->files[mft->filesz++];
222 else {
223 /* Fisher-Yates shuffle */
224 new_idx = arc4random_uniform(mft->filesz + 1);
225 mft->files[mft->filesz++] = mft->files[new_idx];
226 fent = &mft->files[new_idx];
227 }
228
229 fent->type = type;
230 fent->file = file;
231 file = NULL;
232 memcpy(fent->hash, fh->hash->data, SHA256_DIGEST_LENGTH);
233
234 rc = 1;
235 out:
236 free(file);
237 return rc;
238 }
239
240 static int
mft_fh_cmp_name(const FileAndHash * const * a,const FileAndHash * const * b)241 mft_fh_cmp_name(const FileAndHash *const *a, const FileAndHash *const *b)
242 {
243 if ((*a)->file->length < (*b)->file->length)
244 return -1;
245 if ((*a)->file->length > (*b)->file->length)
246 return 1;
247
248 return memcmp((*a)->file->data, (*b)->file->data, (*b)->file->length);
249 }
250
251 static int
mft_fh_cmp_hash(const FileAndHash * const * a,const FileAndHash * const * b)252 mft_fh_cmp_hash(const FileAndHash *const *a, const FileAndHash *const *b)
253 {
254 assert((*a)->hash->length == SHA256_DIGEST_LENGTH);
255 assert((*b)->hash->length == SHA256_DIGEST_LENGTH);
256
257 return memcmp((*a)->hash->data, (*b)->hash->data, (*b)->hash->length);
258 }
259
260 /*
261 * Assuming that the hash lengths are validated, this checks that all file names
262 * and hashes in a manifest are unique. Returns 1 on success, 0 on failure.
263 */
264 static int
mft_has_unique_names_and_hashes(const char * fn,const Manifest * mft)265 mft_has_unique_names_and_hashes(const char *fn, const Manifest *mft)
266 {
267 STACK_OF(FileAndHash) *fhs;
268 int i, ret = 0;
269
270 if ((fhs = sk_FileAndHash_dup(mft->fileList)) == NULL)
271 err(1, NULL);
272
273 (void)sk_FileAndHash_set_cmp_func(fhs, mft_fh_cmp_name);
274 sk_FileAndHash_sort(fhs);
275
276 for (i = 0; i < sk_FileAndHash_num(fhs) - 1; i++) {
277 const FileAndHash *curr = sk_FileAndHash_value(fhs, i);
278 const FileAndHash *next = sk_FileAndHash_value(fhs, i + 1);
279
280 if (mft_fh_cmp_name(&curr, &next) == 0) {
281 warnx("%s: duplicate name: %.*s", fn,
282 curr->file->length, curr->file->data);
283 goto err;
284 }
285 }
286
287 (void)sk_FileAndHash_set_cmp_func(fhs, mft_fh_cmp_hash);
288 sk_FileAndHash_sort(fhs);
289
290 for (i = 0; i < sk_FileAndHash_num(fhs) - 1; i++) {
291 const FileAndHash *curr = sk_FileAndHash_value(fhs, i);
292 const FileAndHash *next = sk_FileAndHash_value(fhs, i + 1);
293
294 if (mft_fh_cmp_hash(&curr, &next) == 0) {
295 warnx("%s: duplicate hash for %.*s and %.*s", fn,
296 curr->file->length, curr->file->data,
297 next->file->length, next->file->data);
298 goto err;
299 }
300 }
301
302 ret = 1;
303
304 err:
305 sk_FileAndHash_free(fhs);
306
307 return ret;
308 }
309
310 /*
311 * Handle the eContent of the manifest object, RFC 6486 sec. 4.2.
312 * Returns 0 on failure and 1 on success.
313 */
314 static int
mft_parse_econtent(const char * fn,struct mft * mft,const unsigned char * d,size_t dsz)315 mft_parse_econtent(const char *fn, struct mft *mft, const unsigned char *d,
316 size_t dsz)
317 {
318 const unsigned char *oder;
319 Manifest *mft_asn1;
320 FileAndHash *fh;
321 int found_crl, i, rc = 0;
322
323 oder = d;
324 if ((mft_asn1 = d2i_Manifest(NULL, &d, dsz)) == NULL) {
325 warnx("%s: RFC 6486 section 4: failed to parse Manifest", fn);
326 goto out;
327 }
328 if (d != oder + dsz) {
329 warnx("%s: %td bytes trailing garbage in eContent", fn,
330 oder + dsz - d);
331 goto out;
332 }
333
334 if (!valid_econtent_version(fn, mft_asn1->version, 0))
335 goto out;
336
337 mft->seqnum = x509_convert_seqnum(fn, "manifest number",
338 mft_asn1->manifestNumber);
339 if (mft->seqnum == NULL)
340 goto out;
341
342 /*
343 * OpenSSL's DER decoder implementation will accept a GeneralizedTime
344 * which doesn't conform to RFC 5280. So, double check.
345 */
346 if (ASN1_STRING_length(mft_asn1->thisUpdate) != GENTIME_LENGTH) {
347 warnx("%s: embedded from time format invalid", fn);
348 goto out;
349 }
350 if (ASN1_STRING_length(mft_asn1->nextUpdate) != GENTIME_LENGTH) {
351 warnx("%s: embedded until time format invalid", fn);
352 goto out;
353 }
354
355 if (!x509_get_time(mft_asn1->thisUpdate, &mft->thisupdate)) {
356 warn("%s: parsing manifest thisUpdate failed", fn);
357 goto out;
358 }
359 if (!x509_get_time(mft_asn1->nextUpdate, &mft->nextupdate)) {
360 warn("%s: parsing manifest nextUpdate failed", fn);
361 goto out;
362 }
363
364 if (mft->thisupdate > mft->nextupdate) {
365 warnx("%s: bad update interval", fn);
366 goto out;
367 }
368
369 if (OBJ_obj2nid(mft_asn1->fileHashAlg) != NID_sha256) {
370 warnx("%s: RFC 6486 section 4.2.1: fileHashAlg: "
371 "want SHA256 object, have %s", fn,
372 nid2str(OBJ_obj2nid(mft_asn1->fileHashAlg)));
373 goto out;
374 }
375
376 if (sk_FileAndHash_num(mft_asn1->fileList) <= 0) {
377 warnx("%s: no files in manifest fileList", fn);
378 goto out;
379 }
380 if (sk_FileAndHash_num(mft_asn1->fileList) >= MAX_MANIFEST_ENTRIES) {
381 warnx("%s: %d exceeds manifest entry limit (%d)", fn,
382 sk_FileAndHash_num(mft_asn1->fileList),
383 MAX_MANIFEST_ENTRIES);
384 goto out;
385 }
386
387 mft->files = calloc(sk_FileAndHash_num(mft_asn1->fileList),
388 sizeof(struct mftfile));
389 if (mft->files == NULL)
390 err(1, NULL);
391
392 found_crl = 0;
393 for (i = 0; i < sk_FileAndHash_num(mft_asn1->fileList); i++) {
394 fh = sk_FileAndHash_value(mft_asn1->fileList, i);
395 if (!mft_parse_filehash(fn, mft, fh, &found_crl))
396 goto out;
397 }
398
399 if (!found_crl) {
400 warnx("%s: CRL not part of MFT fileList", fn);
401 goto out;
402 }
403
404 if (!mft_has_unique_names_and_hashes(fn, mft_asn1))
405 goto out;
406
407 rc = 1;
408 out:
409 Manifest_free(mft_asn1);
410 return rc;
411 }
412
413 /*
414 * Parse the objects that have been published in the manifest.
415 * Return mft if it conforms to RFC 6486, otherwise NULL.
416 */
417 struct mft *
mft_parse(X509 ** x509,const char * fn,int talid,const unsigned char * der,size_t len)418 mft_parse(X509 **x509, const char *fn, int talid, const unsigned char *der,
419 size_t len)
420 {
421 struct mft *mft;
422 struct cert *cert = NULL;
423 int rc = 0;
424 size_t cmsz;
425 unsigned char *cms;
426 char *crldp = NULL, *crlfile;
427 time_t signtime = 0;
428
429 cms = cms_parse_validate(x509, fn, der, len, mft_oid, &cmsz, &signtime);
430 if (cms == NULL)
431 return NULL;
432 assert(*x509 != NULL);
433
434 if ((mft = calloc(1, sizeof(*mft))) == NULL)
435 err(1, NULL);
436 mft->signtime = signtime;
437
438 if (!x509_get_aia(*x509, fn, &mft->aia))
439 goto out;
440 if (!x509_get_aki(*x509, fn, &mft->aki))
441 goto out;
442 if (!x509_get_sia(*x509, fn, &mft->sia))
443 goto out;
444 if (!x509_get_ski(*x509, fn, &mft->ski))
445 goto out;
446 if (mft->aia == NULL || mft->aki == NULL || mft->sia == NULL ||
447 mft->ski == NULL) {
448 warnx("%s: RFC 6487 section 4.8: "
449 "missing AIA, AKI, SIA, or SKI X509 extension", fn);
450 goto out;
451 }
452
453 if (!x509_inherits(*x509)) {
454 warnx("%s: RFC 3779 extension not set to inherit", fn);
455 goto out;
456 }
457
458 /* get CRL info for later */
459 if (!x509_get_crl(*x509, fn, &crldp))
460 goto out;
461 if (crldp == NULL) {
462 warnx("%s: RFC 6487 section 4.8.6: CRL: "
463 "missing CRL distribution point extension", fn);
464 goto out;
465 }
466 crlfile = strrchr(crldp, '/');
467 if (crlfile == NULL) {
468 warnx("%s: RFC 6487 section 4.8.6: "
469 "invalid CRL distribution point", fn);
470 goto out;
471 }
472 crlfile++;
473 if (!valid_mft_filename(crlfile, strlen(crlfile)) ||
474 rtype_from_file_extension(crlfile) != RTYPE_CRL) {
475 warnx("%s: RFC 6487 section 4.8.6: CRL: "
476 "bad CRL distribution point extension", fn);
477 goto out;
478 }
479 if ((mft->crl = strdup(crlfile)) == NULL)
480 err(1, NULL);
481
482 if (mft_parse_econtent(fn, mft, cms, cmsz) == 0)
483 goto out;
484
485 if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL)
486 goto out;
487
488 if (mft->signtime > mft->nextupdate) {
489 warnx("%s: dating issue: CMS signing-time after MFT nextUpdate",
490 fn);
491 goto out;
492 }
493
494 rc = 1;
495 out:
496 if (rc == 0) {
497 mft_free(mft);
498 mft = NULL;
499 X509_free(*x509);
500 *x509 = NULL;
501 }
502 free(crldp);
503 cert_free(cert);
504 free(cms);
505 return mft;
506 }
507
508 /*
509 * Free an MFT pointer.
510 * Safe to call with NULL.
511 */
512 void
mft_free(struct mft * p)513 mft_free(struct mft *p)
514 {
515 size_t i;
516
517 if (p == NULL)
518 return;
519
520 for (i = 0; i < p->filesz; i++)
521 free(p->files[i].file);
522
523 free(p->path);
524 free(p->files);
525 free(p->seqnum);
526 free(p->aia);
527 free(p->aki);
528 free(p->sia);
529 free(p->ski);
530 free(p->crl);
531 free(p);
532 }
533
534 /*
535 * Serialise MFT parsed content into the given buffer.
536 * See mft_read() for the other side of the pipe.
537 */
538 void
mft_buffer(struct ibuf * b,const struct mft * p)539 mft_buffer(struct ibuf *b, const struct mft *p)
540 {
541 size_t i;
542
543 io_simple_buffer(b, &p->repoid, sizeof(p->repoid));
544 io_simple_buffer(b, &p->talid, sizeof(p->talid));
545 io_simple_buffer(b, &p->certid, sizeof(p->certid));
546 io_simple_buffer(b, &p->seqnum_gap, sizeof(p->seqnum_gap));
547 io_str_buffer(b, p->path);
548
549 io_str_buffer(b, p->aia);
550 io_str_buffer(b, p->aki);
551 io_str_buffer(b, p->ski);
552
553 io_simple_buffer(b, &p->filesz, sizeof(size_t));
554 for (i = 0; i < p->filesz; i++) {
555 io_str_buffer(b, p->files[i].file);
556 io_simple_buffer(b, &p->files[i].type,
557 sizeof(p->files[i].type));
558 io_simple_buffer(b, &p->files[i].location,
559 sizeof(p->files[i].location));
560 io_simple_buffer(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
561 }
562 }
563
564 /*
565 * Read an MFT structure from the file descriptor.
566 * Result must be passed to mft_free().
567 */
568 struct mft *
mft_read(struct ibuf * b)569 mft_read(struct ibuf *b)
570 {
571 struct mft *p = NULL;
572 size_t i;
573
574 if ((p = calloc(1, sizeof(struct mft))) == NULL)
575 err(1, NULL);
576
577 io_read_buf(b, &p->repoid, sizeof(p->repoid));
578 io_read_buf(b, &p->talid, sizeof(p->talid));
579 io_read_buf(b, &p->certid, sizeof(p->certid));
580 io_read_buf(b, &p->seqnum_gap, sizeof(p->seqnum_gap));
581 io_read_str(b, &p->path);
582
583 io_read_str(b, &p->aia);
584 io_read_str(b, &p->aki);
585 io_read_str(b, &p->ski);
586 assert(p->aia && p->aki && p->ski);
587
588 io_read_buf(b, &p->filesz, sizeof(size_t));
589 if ((p->files = calloc(p->filesz, sizeof(struct mftfile))) == NULL)
590 err(1, NULL);
591
592 for (i = 0; i < p->filesz; i++) {
593 io_read_str(b, &p->files[i].file);
594 io_read_buf(b, &p->files[i].type, sizeof(p->files[i].type));
595 io_read_buf(b, &p->files[i].location,
596 sizeof(p->files[i].location));
597 io_read_buf(b, p->files[i].hash, SHA256_DIGEST_LENGTH);
598 }
599
600 return p;
601 }
602
603 /*
604 * Compare the thisupdate time of two mft files.
605 */
606 int
mft_compare_issued(const struct mft * a,const struct mft * b)607 mft_compare_issued(const struct mft *a, const struct mft *b)
608 {
609 if (a->thisupdate > b->thisupdate)
610 return 1;
611 if (a->thisupdate < b->thisupdate)
612 return -1;
613 return 0;
614 }
615
616 /*
617 * Compare the manifestNumber of two mft files.
618 */
619 int
mft_compare_seqnum(const struct mft * a,const struct mft * b)620 mft_compare_seqnum(const struct mft *a, const struct mft *b)
621 {
622 int r;
623
624 r = strlen(a->seqnum) - strlen(b->seqnum);
625 if (r > 0) /* seqnum in a is longer -> higher */
626 return 1;
627 if (r < 0) /* seqnum in a is shorter -> smaller */
628 return -1;
629
630 r = strcmp(a->seqnum, b->seqnum);
631 if (r > 0) /* a is greater, prefer a */
632 return 1;
633 if (r < 0) /* b is greater, prefer b */
634 return -1;
635
636 return 0;
637 }
638
639 /*
640 * Test if there is a gap in the sequence numbers of two MFTs.
641 * Return 1 if a gap is detected.
642 */
643 int
mft_seqnum_gap_present(const struct mft * a,const struct mft * b)644 mft_seqnum_gap_present(const struct mft *a, const struct mft *b)
645 {
646 BIGNUM *diff, *seqnum_a, *seqnum_b;
647 int ret = 0;
648
649 BN_CTX_start(bn_ctx);
650 if ((diff = BN_CTX_get(bn_ctx)) == NULL ||
651 (seqnum_a = BN_CTX_get(bn_ctx)) == NULL ||
652 (seqnum_b = BN_CTX_get(bn_ctx)) == NULL)
653 errx(1, "BN_CTX_get");
654
655 if (!BN_hex2bn(&seqnum_a, a->seqnum))
656 errx(1, "BN_hex2bn");
657
658 if (!BN_hex2bn(&seqnum_b, b->seqnum))
659 errx(1, "BN_hex2bn");
660
661 if (!BN_sub(diff, seqnum_a, seqnum_b))
662 errx(1, "BN_sub");
663
664 ret = !BN_is_one(diff);
665
666 BN_CTX_end(bn_ctx);
667
668 return ret;
669 }
670