From 03aae15a5627bdf532f8c7db1cbf42c6d5210bf2 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 7 May 2009 15:01:47 +0000 Subject: [PATCH] New helper function factored out of ../scd and equipped with test code. --- common/ChangeLog | 10 +++ common/sexputil.c | 161 ++++++++++++++++++++++++++++++++++++++++++++ common/t-sexputil.c | 108 +++++++++++++++++++++++++++++ common/util.h | 9 +++ 4 files changed, 288 insertions(+) diff --git a/common/ChangeLog b/common/ChangeLog index 6ada73158..319b817a8 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,13 @@ +2009-05-07 Werner Koch + + * sexputil.c (get_rsa_pk_from_canon_sexp): New. + * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): Extend the test. + +2009-04-28 Werner Koch + + * sexputil.c (make_canon_sexp_from_rsa_pk): New. + * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): New. + 2009-04-01 Werner Koch * iobuf.c: Port David's changes from 1.4: diff --git a/common/sexputil.c b/common/sexputil.c index 4ff7b4955..7c6cb6af5 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -32,6 +32,7 @@ #endif #include "util.h" +#include "tlv.h" #include "sexp-parse.h" @@ -225,3 +226,163 @@ hash_algo_from_sigval (const unsigned char *sigval) return gcry_md_map_name (buffer); } + +/* Create a public key S-expression for an RSA public key from the + modulus M with length MLEN and the public exponent E with length + ELEN. Returns a newly allocated buffer of NULL in case of a memory + allocation problem. If R_LEN is not NULL, the length of the + canonical S-expression is stored there. */ +unsigned char * +make_canon_sexp_from_rsa_pk (const void *m_arg, size_t mlen, + const void *e_arg, size_t elen, + size_t *r_len) +{ + const unsigned char *m = m_arg; + const unsigned char *e = e_arg; + int m_extra = 0; + int e_extra = 0; + char mlen_str[35]; + char elen_str[35]; + unsigned char *keybuf, *p; + const char const part1[] = "(10:public-key(3:rsa(1:n"; + const char const part2[] = ")(1:e"; + const char const part3[] = ")))"; + + /* Remove leading zeroes. */ + for (; mlen && !*m; mlen--, m++) + ; + for (; elen && !*e; elen--, e++) + ; + + /* Insert a leading zero if the number would be zero or interpreted + as negative. */ + if (!mlen || (m[0] & 0x80)) + m_extra = 1; + if (!elen || (e[0] & 0x80)) + e_extra = 1; + + /* Build the S-expression. */ + snprintf (mlen_str, sizeof mlen_str, "%u:", (unsigned int)mlen+m_extra); + snprintf (elen_str, sizeof elen_str, "%u:", (unsigned int)elen+e_extra); + + keybuf = xtrymalloc (strlen (part1) + strlen (mlen_str) + mlen + m_extra + + strlen (part2) + strlen (elen_str) + elen + e_extra + + strlen (part3) + 1); + if (!keybuf) + return NULL; + + p = stpcpy (keybuf, part1); + p = stpcpy (p, mlen_str); + if (m_extra) + *p++ = 0; + memcpy (p, m, mlen); + p += mlen; + p = stpcpy (p, part2); + p = stpcpy (p, elen_str); + if (e_extra) + *p++ = 0; + memcpy (p, e, elen); + p += elen; + p = stpcpy (p, part3); + + if (r_len) + *r_len = p - keybuf; + + return keybuf; +} + + +/* Return the so called "keygrip" which is the SHA-1 hash of the + public key parameters expressed in a way depended on the algorithm. + + KEY is expected to be an canonical encoded S-expression with a + public or private key. KEYLEN is the length of that buffer. + + GRIP must be at least 20 bytes long. On success 0 is returned, on + error an error code. */ +gpg_error_t +get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, + unsigned char const **r_n, size_t *r_nlen, + unsigned char const **r_e, size_t *r_elen) +{ + gpg_error_t err; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth, last_depth1, last_depth2; + const unsigned char *rsa_n = NULL; + const unsigned char *rsa_e = NULL; + size_t rsa_n_len, rsa_e_len; + + *r_n = NULL; + *r_nlen = 0; + *r_e = NULL; + *r_elen = 0; + + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && toklen == 1) + { + const unsigned char **mpi; + size_t *mpi_len; + + switch (*tok) + { + case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; + case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; + default: mpi = NULL; mpi_len = NULL; break; + } + if (mpi && *mpi) + return gpg_error (GPG_ERR_DUP_VALUE); + + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && mpi) + { + /* Strip off leading zero bytes and save. */ + for (;toklen && !*tok; toklen--, tok++) + ; + *mpi = tok; + *mpi_len = toklen; + } + } + + /* Skip to the end of the list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + return err; + } + + if (!rsa_n || !rsa_n_len || !rsa_e || !rsa_e_len) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + *r_n = rsa_n; + *r_nlen = rsa_n_len; + *r_e = rsa_e; + *r_elen = rsa_e_len; + return 0; +} diff --git a/common/t-sexputil.c b/common/t-sexputil.c index 26a6ace4b..05da162bf 100644 --- a/common/t-sexputil.c +++ b/common/t-sexputil.c @@ -69,6 +69,113 @@ test_hash_algo_from_sigval (void) } +static void +test_make_canon_sexp_from_rsa_pk (void) +{ + struct { + unsigned char *m; + size_t mlen; + unsigned char *e; + size_t elen; + unsigned char *result; + size_t resultlen; + gpg_err_code_t reverr; /* Expected error from the reverse fucntion. */ + } tests[] = { + { + "\x82\xB4\x12\x48\x08\x48\xC0\x76\xAA\x8E\xF1\xF8\x7F\x5E\x9B\x89" + "\xA9\x62\x92\xA2\x16\x1B\xF5\x9F\xE1\x41\xF3\xF0\x42\xB5\x5C\x46" + "\xB8\x83\x9F\x39\x97\x73\xFF\xC5\xB2\xF4\x59\x5F\xBA\xC7\x0E\x03" + "\x9D\x27\xC0\x86\x37\x31\x46\xE0\xA1\xFE\xA1\x41\xD4\xE3\xE9\xB3" + "\x9B\xD5\x84\x65\xA5\x37\x35\x34\x07\x58\xB6\xBA\x21\xCA\x21\x72" + "\x4C\xF3\xFC\x91\x47\xD1\x3C\x1D\xA5\x9C\x38\x4D\x58\x39\x92\x16" + "\xB1\xE5\x43\xFE\xB5\x46\x4B\x43\xD1\x47\xB0\xE8\x2A\xDB\xF8\x34" + "\xB0\x5A\x22\x3D\x14\xBB\xEA\x63\x65\xA7\xF1\xF2\xF8\x97\x74\xA7", + 128, + "\x40\x00\x00\x81", + 4, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x32\x39\x3a\x00\x82\xb4\x12" + "\x48\x08\x48\xc0\x76\xaa\x8e\xf1\xf8\x7f\x5e\x9b\x89\xa9\x62\x92" + "\xa2\x16\x1b\xf5\x9f\xe1\x41\xf3\xf0\x42\xb5\x5c\x46\xb8\x83\x9f" + "\x39\x97\x73\xff\xc5\xb2\xf4\x59\x5f\xba\xc7\x0e\x03\x9d\x27\xc0" + "\x86\x37\x31\x46\xe0\xa1\xfe\xa1\x41\xd4\xe3\xe9\xb3\x9b\xd5\x84" + "\x65\xa5\x37\x35\x34\x07\x58\xb6\xba\x21\xca\x21\x72\x4c\xf3\xfc" + "\x91\x47\xd1\x3c\x1d\xa5\x9c\x38\x4d\x58\x39\x92\x16\xb1\xe5\x43" + "\xfe\xb5\x46\x4b\x43\xd1\x47\xb0\xe8\x2a\xdb\xf8\x34\xb0\x5a\x22" + "\x3d\x14\xbb\xea\x63\x65\xa7\xf1\xf2\xf8\x97\x74\xa7\x29\x28\x31" + "\x3a\x65\x34\x3a\x40\x00\x00\x81\x29\x29\x29", + 171 + }, + { + "\x63\xB4\x12\x48\x08\x48\xC0\x76\xAA\x8E\xF1\xF8\x7F\x5E\x9B\x89", + 16, + "\x03", + 1, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x36\x3a\x63\xb4\x12\x48\x08" + "\x48\xc0\x76\xaa\x8e\xf1\xf8\x7f\x5e\x9b\x89\x29\x28\x31\x3a\x65" + "\x31\x3a\x03\x29\x29\x29", + 54, + }, + { + "", + 0, + "", + 0, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x3a\x00\x29\x28\x31\x3a\x65" + "\x31\x3a\x00\x29\x29\x29", + 38, + GPG_ERR_BAD_PUBKEY + }, + { + NULL + } + }; + int idx; + gpg_error_t err; + unsigned char *sexp; + size_t length; + const unsigned char *rsa_n, *rsa_e; + size_t rsa_n_len, rsa_e_len; + + for (idx=0; tests[idx].m; idx++) + { + sexp = make_canon_sexp_from_rsa_pk (tests[idx].m, tests[idx].mlen, + tests[idx].e, tests[idx].elen, + &length); + if (!sexp) + { + fprintf (stderr, "%s:%d: out of core\n", __FILE__, __LINE__); + exit (1); + } + + if (length != tests[idx].resultlen) + fail (idx); + if (memcmp (sexp, tests[idx].result, tests[idx].resultlen)) + fail (idx); + + /* Test the reverse function. */ + err = get_rsa_pk_from_canon_sexp (sexp, length, + &rsa_n, &rsa_n_len, + &rsa_e, &rsa_e_len); + if (gpg_err_code (err) != tests[idx].reverr) + fail (idx); + if (!err) + { + if (tests[idx].mlen != rsa_n_len) + fail (idx); + if (memcmp (tests[idx].m, rsa_n, rsa_n_len)) + fail (idx); + if (tests[idx].elen != rsa_e_len) + fail (idx); + if (memcmp (tests[idx].e, rsa_e, rsa_e_len)) + fail (idx); + } + + xfree (sexp); + } +} int @@ -78,6 +185,7 @@ main (int argc, char **argv) (void)argv; test_hash_algo_from_sigval (); + test_make_canon_sexp_from_rsa_pk (); return 0; } diff --git a/common/util.h b/common/util.h index c64db9f6b..816afff0b 100644 --- a/common/util.h +++ b/common/util.h @@ -192,6 +192,15 @@ int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); unsigned char *make_simple_sexp_from_hexstr (const char *line, size_t *nscanned); int hash_algo_from_sigval (const unsigned char *sigval); +unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, + const void *e, size_t elen, + size_t *r_len); +gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + unsigned char const **r_n, + size_t *r_nlen, + unsigned char const **r_e, + size_t *r_elen); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length);