From e0d4139e19ceed9375cc7e7ba801a965d3376f7d Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 31 Jan 2011 18:19:14 +0100 Subject: [PATCH] Move OpenPGP OID helpers to common/. This is needed so that the agent will be able to export and import OpenPGP secret keys. Add test case. Removed unused function. --- common/ChangeLog | 7 ++ common/Makefile.am | 4 +- common/convert.c | 34 ------ common/openpgp-oid.c | 227 +++++++++++++++++++++++++++++++++++++++++ common/t-openpgp-oid.c | 148 +++++++++++++++++++++++++++ common/util.h | 6 +- g10/ChangeLog | 3 + g10/main.h | 4 - g10/misc.c | 201 ------------------------------------ g13/utils.h | 1 - 10 files changed, 393 insertions(+), 242 deletions(-) create mode 100644 common/openpgp-oid.c create mode 100644 common/t-openpgp-oid.c diff --git a/common/ChangeLog b/common/ChangeLog index 0150658e0..0214583b7 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,10 @@ +2011-01-31 Werner Koch + + * openpgp-oid.c: New. + * t-openpgp-oid.c: New. + + * convert.c (mpi2hex): Remove. + 2011-01-20 Werner Koch Fix bug#1313. diff --git a/common/Makefile.am b/common/Makefile.am index 110fc5f58..2539859d5 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -90,6 +90,7 @@ common_sources = \ localename.c \ session-env.c session-env.h \ userids.c userids.h \ + openpgp-oid.c \ helpfile.c # To make the code easier to read we have split home some code into @@ -160,7 +161,7 @@ if HAVE_W32_SYSTEM jnlib_tests += t-w32-reg endif module_tests = t-convert t-percent t-gettime t-sysutils t-sexputil \ - t-session-env + t-session-env t-openpgp-oid if !HAVE_W32CE_SYSTEM module_tests += t-exechelp endif @@ -192,6 +193,7 @@ t_sexputil_LDADD = $(t_common_ldadd) t_b64_LDADD = $(t_common_ldadd) t_exechelp_LDADD = $(t_common_ldadd) t_session_env_LDADD = $(t_common_ldadd) +t_openpgp_oid_LDADD = $(t_common_ldadd) diff --git a/common/convert.c b/common/convert.c index 5df6b335e..c314ec9a6 100644 --- a/common/convert.c +++ b/common/convert.c @@ -245,37 +245,3 @@ hex2str_alloc (const char *hexstring, size_t *r_count) BUG (); return result; } - -/* returns hex representation of the MPI; - * caller must free with xfree - * Returns NULL on error, never throws - */ -char * -mpi2hex( gcry_mpi_t m ) -{ -#warning we have code for this in libcrypt - size_t nbytes; - size_t nbytes2; - int rc; - byte *p; - - nbytes = (mpi_get_nbits ( m )+7)/8; - if( nbytes == 0 ) - return NULL; - p = xtrymalloc( nbytes*3+1 ); - if( p==NULL ) - return NULL; - rc = gcry_mpi_print (GCRYMPI_FMT_USG, p+2*nbytes+1, nbytes, &nbytes2, m); - if( rc ) { - xfree( p ); - return NULL; - } - - bin2hex( p+2*nbytes+1, nbytes2, p ); - p[nbytes2*2] = '\0'; - /*printf("%s:%d>>>> Created the string %s from %d bytes %02x %02x - ..., MPI was %d bytes\n", __FILE__, __LINE__, p, nbytes2, - p[2*nbytes+1], p[2*nbytes+2], nbytes); */ - return p; -} - diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c new file mode 100644 index 000000000..222c5a0b2 --- /dev/null +++ b/common/openpgp-oid.c @@ -0,0 +1,227 @@ +/* openpgp-oids.c - OID helper for OpenPGP + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +#include "util.h" + + +/* Helper for openpgp_oid_from_str. */ +static size_t +make_flagged_int (unsigned long value, char *buf, size_t buflen) +{ + int more = 0; + int shift; + + /* fixme: figure out the number of bits in an ulong and start with + that value as shift (after making it a multiple of 7) a more + straigtforward implementation is to do it in reverse order using + a temporary buffer - saves a lot of compares */ + for (more=0, shift=28; shift > 0; shift -= 7) + { + if (more || value >= (1<> shift); + value -= (value >> shift) << shift; + more = 1; + } + } + buf[buflen++] = value; + return buflen; +} + + +/* Convert the OID given in dotted decimal form in STRING to an DER + * encoding and store it as an opaque value at R_MPI. The format of + * the DER encoded is not a regular ASN.1 object but the modified + * format as used by OpenPGP for the ECC curve description. On error + * the function returns and error code an NULL is stored at R_BUG. + * Note that scanning STRING stops at the first white space + * character. */ +gpg_error_t +openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) +{ + unsigned char *buf; + size_t buflen; + unsigned long val1, val; + const char *endp; + int arcno; + + *r_mpi = NULL; + + if (!string || !*string) + return gpg_error (GPG_ERR_INV_VALUE); + + /* We can safely assume that the encoded OID is shorter than the string. */ + buf = xtrymalloc (1 + strlen (string) + 2); + if (!buf) + return gpg_error_from_syserror (); + /* Save the first byte for the length. */ + buflen = 1; + + val1 = 0; /* Avoid compiler warning. */ + arcno = 0; + do { + arcno++; + val = strtoul (string, (char**)&endp, 10); + if (!digitp (string) || !(*endp == '.' || !*endp)) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + if (*endp == '.') + string = endp+1; + + if (arcno == 1) + { + if (val > 2) + break; /* Not allowed, error catched below. */ + val1 = val; + } + else if (arcno == 2) + { /* Need to combine the first two arcs in one octet. */ + if (val1 < 2) + { + if (val > 39) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + buf[buflen++] = val1*40 + val; + } + else + { + val += 80; + buflen = make_flagged_int (val, buf, buflen); + } + } + else + { + buflen = make_flagged_int (val, buf, buflen); + } + } while (*endp == '.'); + + if (arcno == 1 || buflen < 2 || buflen > 254 ) + { /* It is not possible to encode only the first arc. */ + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + + *buf = buflen - 1; + *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); + if (!*r_mpi) + { + xfree (buf); + return gpg_error_from_syserror (); + } + return 0; +} + + +/* Return a malloced string represenation of the OID in the opaque MPI + A. In case of an error NULL is returned and ERRNO is set. */ +char * +openpgp_oid_to_str (gcry_mpi_t a) +{ + const unsigned char *buf; + size_t length; + char *string, *p; + int n = 0; + unsigned long val, valmask; + + valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + buf = gcry_mpi_get_opaque (a, &length); + length = (length+7)/8; + + /* The first bytes gives the length; check consistency. */ + if (!length || buf[0] != length -1) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + /* Skip length byte. */ + length--; + buf++; + + /* To calculate the length of the string we can safely assume an + upper limit of 3 decimal characters per byte. Two extra bytes + account for the special first octect */ + string = p = xtrymalloc (length*(1+3)+2+1); + if (!string) + return NULL; + if (!buf || !length) + { + *p = 0; + return string; + } + + if (buf[0] < 40) + p += sprintf (p, "0.%d", buf[n]); + else if (buf[0] < 80) + p += sprintf (p, "1.%d", buf[n]-40); + else { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < length ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + val -= 80; + sprintf (p, "2.%lu", val); + p += strlen (p); + } + for (n++; n < length; n++) + { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < length ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + sprintf (p, ".%lu", val); + p += strlen (p); + } + + *p = 0; + return string; + + badoid: + /* Return a special OID (gnu.gnupg.badoid) to indicate the error + case. The OID is broken and thus we return one which can't do + any harm. Formally this does not need to be a bad OID but an OID + with an arc that can't be represented in a 32 bit word is more + than likely corrupt. */ + xfree (string); + return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); +} diff --git a/common/t-openpgp-oid.c b/common/t-openpgp-oid.c new file mode 100644 index 000000000..80e576309 --- /dev/null +++ b/common/t-openpgp-oid.c @@ -0,0 +1,148 @@ +/* t-openpgp-oid.c - Module test for openpgp-oid.c + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a,e) \ + do { fprintf (stderr, "%s:%d: test %d failed (%s)\n", \ + __FILE__,__LINE__, (a), gpg_strerror (e)); \ + exit (1); \ + } while(0) + + +static void +test_openpgp_oid_from_str (void) +{ + static char *sample_oids[] = + { + "0.0", + "1.0", + "1.2.3", + "1.2.840.10045.3.1.7", + "1.3.132.0.34", + "1.3.132.0.35", + NULL + }; + gpg_error_t err; + gcry_mpi_t a; + int idx; + char *string; + unsigned char *p; + unsigned int nbits; + size_t length; + + err = openpgp_oid_from_str ("", &a); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (0, err); + gcry_mpi_release (a); + + err = openpgp_oid_from_str (".", &a); + if (gpg_err_code (err) != GPG_ERR_INV_OID_STRING) + fail (0, err); + gcry_mpi_release (a); + + err = openpgp_oid_from_str ("0", &a); + if (gpg_err_code (err) != GPG_ERR_INV_OID_STRING) + fail (0, err); + gcry_mpi_release (a); + + for (idx=0; sample_oids[idx]; idx++) + { + err = openpgp_oid_from_str (sample_oids[idx], &a); + if (err) + fail (idx, err); + + string = openpgp_oid_to_str (a); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, sample_oids[idx])) + fail (idx, 0); + xfree (string); + + p = gcry_mpi_get_opaque (a, &nbits); + length = (nbits+7)/8; + if (!p || !length || p[0] != length - 1) + fail (idx, 0); + + gcry_mpi_release (a); + } + +} + + +static void +test_openpgp_oid_to_str (void) +{ + static struct { + const char *string; + unsigned char der[10]; + } samples[] = { + { "1.2.840.10045.3.1.7", + {8, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }}, + + { "1.3.132.0.34", + {5, 0x2B, 0x81, 0x04, 0x00, 0x22 }}, + + { "1.3.132.0.35", + { 5, 0x2B, 0x81, 0x04, 0x00, 0x23 }}, + + { NULL }}; + gcry_mpi_t a; + int idx; + char *string; + unsigned char *p; + + for (idx=0; samples[idx].string; idx++) + { + p = xmalloc (samples[idx].der[0]+1); + memcpy (p, samples[idx].der, samples[idx].der[0]+1); + a = gcry_mpi_set_opaque (NULL, p, (samples[idx].der[0]+1)*8); + if (!a) + fail (idx, gpg_error_from_syserror ()); + + string = openpgp_oid_to_str (a); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, samples[idx].string)) + fail (idx, 0); + xfree (string); + gcry_mpi_release (a); + } + +} + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_openpgp_oid_from_str (); + test_openpgp_oid_to_str (); + + return 0; +} diff --git a/common/util.h b/common/util.h index 99d58e172..cf38ad4f8 100644 --- a/common/util.h +++ b/common/util.h @@ -196,7 +196,6 @@ gpg_error_t get_pk_algo_from_canon_sexp (const unsigned char *keydata, int hex2bin (const char *string, void *buffer, size_t length); int hexcolon2bin (const char *string, void *buffer, size_t length); char *bin2hex (const void *buffer, size_t length, char *stringbuf); -char *mpi2hex (gcry_mpi_t m); char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); const char *hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen); @@ -210,6 +209,11 @@ char *percent_unescape (const char *string, int nulrepl); size_t percent_plus_unescape_inplace (char *string, int nulrepl); size_t percent_unescape_inplace (char *string, int nulrepl); +/*-- openpgp-oid.c --*/ +gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); +char *openpgp_oid_to_str (gcry_mpi_t a); + + /*-- homedir.c --*/ const char *standard_homedir (void); diff --git a/g10/ChangeLog b/g10/ChangeLog index 587c9f158..2a284e765 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,8 @@ 2011-01-31 Werner Koch + * misc.c (make_flagged_int, openpgp_oid_from_str) + (openpgp_oid_to_str): Move to ../common/openpgp-oids.c. + * ecdh.c (pk_ecdh_encrypt_with_shared_point): Return an opaque MPI. * build-packet.c (mpi_write): Rename to gpg_mpi_write and make global. diff --git a/g10/main.h b/g10/main.h index d76d96bc4..d70c16901 100644 --- a/g10/main.h +++ b/g10/main.h @@ -165,10 +165,6 @@ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); -/* Other stuff */ -gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); -char *openpgp_oid_to_str (gcry_mpi_t a); - /*-- status.c --*/ void set_status_fd ( int fd ); diff --git a/g10/misc.c b/g10/misc.c index 2052e96c7..dcd2bd10b 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1552,204 +1552,3 @@ ecdsa_qbits_from_Q (unsigned int qbits) } - -/* Helper for openpgp_oid_from_str. */ -static size_t -make_flagged_int (unsigned long value, char *buf, size_t buflen) -{ - int more = 0; - int shift; - - /* fixme: figure out the number of bits in an ulong and start with - that value as shift (after making it a multiple of 7) a more - straigtforward implementation is to do it in reverse order using - a temporary buffer - saves a lot of compares */ - for (more=0, shift=28; shift > 0; shift -= 7) - { - if (more || value >= (1<> shift); - value -= (value >> shift) << shift; - more = 1; - } - } - buf[buflen++] = value; - return buflen; -} - - -/* Convert the OID given in dotted decimal form in STRING to an DER - * encoding and store it as an opaque value at R_MPI. The format of - * the DER encoded is not a regular ASN.1 object but the modified - * format as used by OpenPGP for the ECC curve description. On error - * the function returns and error code an NULL is stored at R_BUG. - * Note that scanning STRING stops at the first white space - * character. */ -gpg_error_t -openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) -{ - unsigned char *buf; - size_t buflen; - unsigned long val1, val; - const char *endp; - int arcno; - - *r_mpi = NULL; - - if (!string || !*string) - return gpg_error (GPG_ERR_INV_VALUE); - - /* We can safely assume that the encoded OID is shorter than the string. */ - buf = xtrymalloc (1 + strlen (string) + 2); - if (!buf) - return gpg_error_from_syserror (); - /* Save the first byte for the length. */ - buflen = 1; - - val1 = 0; /* Avoid compiler warning. */ - arcno = 0; - do { - arcno++; - val = strtoul (string, (char**)&endp, 10); - if (!digitp (string) || !(*endp == '.' || !*endp)) - { - xfree (buf); - return gpg_error (GPG_ERR_INV_OID_STRING); - } - if (*endp == '.') - string = endp+1; - - if (arcno == 1) - { - if (val > 2) - break; /* Not allowed, error catched below. */ - val1 = val; - } - else if (arcno == 2) - { /* Need to combine the first two arcs in one octet. */ - if (val1 < 2) - { - if (val > 39) - { - xfree (buf); - return gpg_error (GPG_ERR_INV_OID_STRING); - } - buf[buflen++] = val1*40 + val; - } - else - { - val += 80; - buflen = make_flagged_int (val, buf, buflen); - } - } - else - { - buflen = make_flagged_int (val, buf, buflen); - } - } while (*endp == '.'); - - if (arcno == 1 || buflen < 2 || buflen > 254 ) - { /* It is not possible to encode only the first arc. */ - xfree (buf); - return gpg_error (GPG_ERR_INV_OID_STRING); - } - - *buf = buflen - 1; - *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); - if (!*r_mpi) - { - xfree (buf); - return gpg_error_from_syserror (); - } - return 0; -} - - -/* Return a malloced string represenation of the OID in the opaque MPI - A. In case of an error NULL is returned and ERRNO is set. */ -char * -openpgp_oid_to_str (gcry_mpi_t a) -{ - const unsigned char *buf; - size_t length; - char *string, *p; - int n = 0; - unsigned long val, valmask; - - valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); - - if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) - { - gpg_err_set_errno (EINVAL); - return NULL; - } - - buf = gcry_mpi_get_opaque (a, &length); - length = (length+7)/8; - - /* The first bytes gives the length; check consistency. */ - if (!length || buf[0] != length -1) - { - gpg_err_set_errno (EINVAL); - return NULL; - } - /* Skip length byte. */ - length--; - buf++; - - /* To calculate the length of the string we can safely assume an - upper limit of 3 decimal characters per byte. Two extra bytes - account for the special first octect */ - string = p = xtrymalloc (length*(1+3)+2+1); - if (!string) - return NULL; - if (!buf || !length) - { - *p = 0; - return string; - } - - if (buf[0] < 40) - p += sprintf (p, "0.%d", buf[n]); - else if (buf[0] < 80) - p += sprintf (p, "1.%d", buf[n]-40); - else { - val = buf[n] & 0x7f; - while ( (buf[n]&0x80) && ++n < length ) - { - if ( (val & valmask) ) - goto badoid; /* Overflow. */ - val <<= 7; - val |= buf[n] & 0x7f; - } - val -= 80; - sprintf (p, "2.%lu", val); - p += strlen (p); - } - for (n++; n < length; n++) - { - val = buf[n] & 0x7f; - while ( (buf[n]&0x80) && ++n < length ) - { - if ( (val & valmask) ) - goto badoid; /* Overflow. */ - val <<= 7; - val |= buf[n] & 0x7f; - } - sprintf (p, ".%lu", val); - p += strlen (p); - } - - *p = 0; - return string; - - badoid: - /* Return a special OID (gnu.gnupg.badoid) to indicate the error - case. The OID is broken and thus we return one which can't do - any harm. Formally this does not need to be a bad OID but an OID - with an arc that can't be represented in a 32 bit word is more - than likely corrupt. */ - xfree (string); - return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); -} - diff --git a/g13/utils.h b/g13/utils.h index 528ce16ce..cebd0436d 100644 --- a/g13/utils.h +++ b/g13/utils.h @@ -38,7 +38,6 @@ const void *find_tuple (tupledesc_t tupledesc, unsigned int tag, size_t *r_length); const void *next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length); -char *mpi2hex( gcry_mpi_t m ); #endif /*G13_UTILS_H*/