aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/sexputil.c90
-rw-r--r--common/t-sexputil.c102
-rw-r--r--common/tlv.c4
-rw-r--r--common/util.h6
4 files changed, 200 insertions, 2 deletions
diff --git a/common/sexputil.c b/common/sexputil.c
index 981a06664..9bb1d6bdc 100644
--- a/common/sexputil.c
+++ b/common/sexputil.c
@@ -263,6 +263,96 @@ cmp_simple_canon_sexp (const unsigned char *a_orig,
}
+
+/* Helper for cmp_canon_sexp. */
+static int
+cmp_canon_sexp_def_tcmp (void *ctx, int depth,
+ const unsigned char *aval, size_t alen,
+ const unsigned char *bval, size_t blen)
+{
+ (void)ctx;
+ (void)depth;
+
+ if (alen > blen)
+ return 1;
+ else if (alen < blen)
+ return -1;
+ else
+ return memcmp (aval, bval, alen);
+}
+
+
+/* Compare the two canonical encoded s-expressions A with maximum
+ * length ALEN and B with maximum length BLEN.
+ *
+ * Returns 0 if they match.
+ *
+ * If TCMP is NULL, this is not different really different from a
+ * memcmp but does not consider any garbage after the last closing
+ * parentheses.
+ *
+ * If TCMP is not NULL, it is expected to be a function to compare the
+ * values of each token. TCMP is called for each token while parsing
+ * the s-expressions until TCMP return a non-zero value. Here the CTX
+ * receives the provided value TCMPCTX, DEPTH is the number of
+ * currently open parentheses and (AVAL,ALEN) and (BVAL,BLEN) the
+ * values of the current token. TCMP needs to return zero to indicate
+ * that the tokens match. */
+int
+cmp_canon_sexp (const unsigned char *a, size_t alen,
+ const unsigned char *b, size_t blen,
+ int (*tcmp)(void *ctx, int depth,
+ const unsigned char *aval, size_t avallen,
+ const unsigned char *bval, size_t bvallen),
+ void *tcmpctx)
+{
+ const unsigned char *a_buf, *a_tok;
+ const unsigned char *b_buf, *b_tok;
+ size_t a_buflen, a_toklen;
+ size_t b_buflen, b_toklen;
+ int a_depth, b_depth, ret;
+
+ if ((!a && !b) || (!alen && !blen))
+ return 0; /* Both are NULL, they are identical. */
+ if (!a || !b)
+ return !!a - !!b; /* One is NULL, they are not identical. */
+ if (*a != '(' || *b != '(')
+ log_bug ("invalid S-exp in %s\n", __func__);
+
+ if (!tcmp)
+ tcmp = cmp_canon_sexp_def_tcmp;
+
+ a_depth = 0;
+ a_buf = a;
+ a_buflen = alen;
+ b_depth = 0;
+ b_buf = b;
+ b_buflen = blen;
+
+ for (;;)
+ {
+ if (parse_sexp (&a_buf, &a_buflen, &a_depth, &a_tok, &a_toklen))
+ return -1; /* A is invalid. */
+ if (parse_sexp (&b_buf, &b_buflen, &b_depth, &b_tok, &b_toklen))
+ return -1; /* B is invalid. */
+ if (!a_depth && !b_depth)
+ return 0; /* End of both expressions - they match. */
+ if (a_depth != b_depth)
+ return a_depth - b_depth; /* Not the same structure */
+ if (!a_tok && !b_tok)
+ ; /* parens */
+ else if (a_tok && b_tok)
+ {
+ ret = tcmp (tcmpctx, a_depth, a_tok, a_toklen, b_tok, b_toklen);
+ if (ret)
+ return ret; /* Mismatch */
+ }
+ else /* One has a paren other has not. */
+ return !!a_tok - !!b_tok;
+ }
+}
+
+
/* Create a simple S-expression from the hex string at LINE. Returns
a newly allocated buffer with that canonical encoded S-expression
or NULL in case of an error. On return the number of characters
diff --git a/common/t-sexputil.c b/common/t-sexputil.c
index ceb828076..659eadd3a 100644
--- a/common/t-sexputil.c
+++ b/common/t-sexputil.c
@@ -178,6 +178,107 @@ test_make_canon_sexp_from_rsa_pk (void)
}
+
+/* Communiacation object for tcmp. */
+struct tcmp_parm_s {
+ int curve_seen;
+};
+
+/* Helper for test_cmp_canon_sexp. */
+static int
+tcmp1 (void *opaque, int depth,
+ const unsigned char *aval, size_t alen,
+ const unsigned char *bval, size_t blen)
+{
+ struct tcmp_parm_s *parm = opaque;
+
+ (void)depth;
+
+ if (parm->curve_seen)
+ {
+ /* Last token was "curve", canonicalize its argument. */
+ parm->curve_seen = 0;
+
+ if (alen == 8 && !memcmp (aval, "nistp256", alen))
+ {
+ alen = 19;
+ aval = "1.2.840.10045.3.1.7";
+ }
+
+ if (blen == 8 && !memcmp (bval, "nistp256", blen))
+ {
+ blen = 19;
+ bval = "1.2.840.10045.3.1.7";
+ }
+ }
+ else if (alen == 5 && !memcmp (aval, "curve", 5))
+ parm->curve_seen = 1;
+ else
+ parm->curve_seen = 0;
+
+ if (alen > blen)
+ return 1;
+ else if (alen < blen)
+ return -1;
+ else
+ return memcmp (aval, bval, alen);
+}
+
+
+static void
+test_cmp_canon_sexp (void)
+{
+ struct {
+ unsigned char *a;
+ unsigned char *b;
+ int expected0; /* Expected result without compare function. */
+ int expected1; /* Expected result with compare function tcmp1. */
+ }
+ tests[] = {
+ {
+ "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))",
+ "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))",
+ 0, 0
+ },
+ {
+ "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))",
+ "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))",
+ 0, 0
+ },
+ {
+ "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))",
+ "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))",
+ -1, 0
+ },
+ {
+ "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))",
+ "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))",
+ 1, 0
+ },
+ {
+ NULL
+ }
+ };
+ struct tcmp_parm_s parm = {0};
+ int idx;
+ int res;
+
+ for (idx=0; tests[idx].a; idx++)
+ {
+ res = cmp_canon_sexp (tests[idx].a, strlen (tests[idx].a),
+ tests[idx].b, strlen (tests[idx].b),
+ NULL, NULL);
+ if (res != tests[idx].expected0)
+ fail (idx);
+ res = cmp_canon_sexp (tests[idx].a, strlen (tests[idx].a),
+ tests[idx].b, strlen (tests[idx].b),
+ tcmp1, &parm);
+ if (res != tests[idx].expected1)
+ fail (idx);
+ }
+}
+
+
int
main (int argc, char **argv)
{
@@ -186,6 +287,7 @@ main (int argc, char **argv)
test_hash_algo_from_sigval ();
test_make_canon_sexp_from_rsa_pk ();
+ test_cmp_canon_sexp ();
return 0;
}
diff --git a/common/tlv.c b/common/tlv.c
index 947464bf7..abef83a37 100644
--- a/common/tlv.c
+++ b/common/tlv.c
@@ -242,8 +242,8 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
returned as a pointer into the original buffer at TOK and TOKLEN.
If a parentheses is the next token, TOK will be set to NULL.
TOKLEN is checked to be within the bounds. On error an error code
- is returned and no pointer is not guaranteed to point to
- a meaningful value. DEPTH should be initialized to 0 and will
+ is returned and pointers are not guaranteed to point to
+ meaningful values. DEPTH should be initialized to 0 and will
reflect on return the actual depth of the tree. To detect the end
of the S-expression it is advisable to check DEPTH after a
successful return.
diff --git a/common/util.h b/common/util.h
index 490c443d8..f39093566 100644
--- a/common/util.h
+++ b/common/util.h
@@ -185,6 +185,12 @@ gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure,
gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
unsigned char *grip);
int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
+int cmp_canon_sexp (const unsigned char *a, size_t alen,
+ const unsigned char *b, size_t blen,
+ int (*tcmp)(void *ctx, int depth,
+ const unsigned char *aval, size_t avallen,
+ const unsigned char *bval, size_t bvallen),
+ void *tcmpctx);
unsigned char *make_simple_sexp_from_hexstr (const char *line,
size_t *nscanned);
int hash_algo_from_sigval (const unsigned char *sigval);