common: New function cmp_canon_sexp.

* common/sexputil.c (cmp_canon_sexp): New.
(cmp_canon_sexp_def_tcmp): New.
* common/t-sexputil.c (test_cmp_canon_sexp): Add a simple test.
--

To be used to fix
GnuPG-bug-id: 5061

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-09-11 15:23:22 +02:00
parent 9a94db1f66
commit b6ba6a79ce
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 200 additions and 2 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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.

View File

@ -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);