diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 010c23f60..d3d1f2aa7 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -1,347 +1,349 @@ /* openpgp-oids.c - OID helper for OpenPGP * Copyright (C) 2011 Free Software Foundation, Inc. * Copyright (C) 2013 Werner Koch * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file 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" /* A table with all our supported OpenPGP curves. */ static struct { const char *name; /* Standard name. */ const char *oidstr; /* IETF formatted OID. */ unsigned int nbits; /* Nominla bit length of the curve. */ const char *alias; /* NULL or alternative name of the curve. */ } oidtable[] = { { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519" }, { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, { "NIST P-521", "1.3.132.0.35", 521, "nistp521" }, { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256 }, { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384 }, { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512 }, { "secp256k1", "1.3.132.0.10", 256 }, { NULL, NULL, 0} }; /* The OID for Curve Ed25519 in OpenPGP format. */ static const char oid_ed25519[] = { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; /* 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; unsigned int lengthi; 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, &lengthi); length = (lengthi+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; } + if (val < 80) + goto badoid; 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"); } /* Return true if A represents the OID for Ed25519. */ int openpgp_oid_is_ed25519 (gcry_mpi_t a) { const unsigned char *buf; unsigned int nbits; size_t n; if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) return 0; buf = gcry_mpi_get_opaque (a, &nbits); n = (nbits+7)/8; return (n == DIM (oid_ed25519) && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); } /* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL store the bit size of the curve there. Returns NULL for unknown curve names. */ const char * openpgp_curve_to_oid (const char *name, unsigned int *r_nbits) { int i; unsigned int nbits = 0; const char *oidstr = NULL; if (name) { for (i=0; oidtable[i].name; i++) if (!strcmp (oidtable[i].name, name) || (oidtable[i].alias && !strcmp (oidtable[i].alias, name))) { oidstr = oidtable[i].oidstr; nbits = oidtable[i].nbits; break; } if (!oidtable[i].name) { /* If not found assume the input is already an OID and check whether we support it. */ for (i=0; oidtable[i].name; i++) if (!strcmp (name, oidtable[i].oidstr)) { oidstr = oidtable[i].oidstr; nbits = oidtable[i].nbits; break; } } } if (r_nbits) *r_nbits = nbits; return oidstr; } /* Map an OpenPGP OID to the Libgcrypt curve NAME. Returns "?" for unknown curve names. We prefer an alias name here which is more suitable for printing. */ const char * openpgp_oid_to_curve (const char *oidstr) { int i; if (!oidstr) return ""; for (i=0; oidtable[i].name; i++) if (!strcmp (oidtable[i].oidstr, oidstr)) return oidtable[i].alias? oidtable[i].alias : oidtable[i].name; return "?"; } diff --git a/common/t-openpgp-oid.c b/common/t-openpgp-oid.c index 79e5a7095..5cd778d72 100644 --- a/common/t-openpgp-oid.c +++ b/common/t-openpgp-oid.c @@ -1,189 +1,198 @@ /* 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) +#define BADOID "1.3.6.1.4.1.11591.2.12242973" + + 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 }}, + { BADOID, + { 9, 0x80, 0x02, 0x70, 0x50, 0x25, 0x46, 0xfd, 0x0c, 0xc0 }}, + + { BADOID, + { 1, 0x80 }}, + { 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); } } static void test_openpgp_oid_is_ed25519 (void) { static struct { int yes; const char *oidstr; } samples[] = { { 0, "0.0" }, { 0, "1.3.132.0.35" }, { 0, "1.3.6.1.4.1.3029.1.5.0" }, { 0, "1.3.6.1.4.1.3029.1.5.1" }, /* Used during Libgcrypt development. */ { 0, "1.3.6.1.4.1.3029.1.5.2" }, { 0, "1.3.6.1.4.1.3029.1.5.1.0" }, { 0, "1.3.6.1.4.1.3029.1.5" }, { 0, "1.3.6.1.4.1.11591.15.0" }, { 1, "1.3.6.1.4.1.11591.15.1" }, /* Your the one we want. */ { 0, "1.3.6.1.4.1.11591.15.2" }, { 0, "1.3.6.1.4.1.11591.15.1.0" }, { 0, "1.3.6.1.4.1.11591.15" }, { 0, NULL }, }; gpg_error_t err; gcry_mpi_t a; int idx; for (idx=0; samples[idx].oidstr; idx++) { err = openpgp_oid_from_str (samples[idx].oidstr, &a); if (err) fail (idx, err); if (openpgp_oid_is_ed25519 (a) != samples[idx].yes) fail (idx, 0); gcry_mpi_release (a); } } int main (int argc, char **argv) { (void)argc; (void)argv; test_openpgp_oid_from_str (); test_openpgp_oid_to_str (); test_openpgp_oid_is_ed25519 (); return 0; }