diff options
Diffstat (limited to 'g10/misc.c')
-rw-r--r-- | g10/misc.c | 304 |
1 files changed, 278 insertions, 26 deletions
diff --git a/g10/misc.c b/g10/misc.c index dc2f73be4..2052e96c7 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -379,6 +379,19 @@ map_pk_openpgp_to_gcry (int algo) } } +/* Map Gcrypt public key algorithm numbers to those used by + OpenPGP. */ +int +map_pk_gcry_to_openpgp (enum gcry_pk_algos algo) +{ + switch (algo) + { + case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; + case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; + default: return algo < 110 ? algo : 0; + } +} + /* Return the block length of an OpenPGP cipher algorithm. */ int @@ -1347,35 +1360,44 @@ path_access(const char *file,int mode) -/* Temporary helper. */ +/* Return the number of public key parameters as used by OpenPGP. */ int -pubkey_get_npkey( int algo ) +pubkey_get_npkey (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 2; + else if (algo == PUBKEY_ALGO_ECDH) + return 3; + + /* All other algorithms match those of Libgcrypt. */ if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; - else if (algo == PUBKEY_ALGO_ECDSA) - algo = GCRY_PK_ECDSA; - else if (algo == PUBKEY_ALGO_ECDH) - algo = GCRY_PK_ECDH; - if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n)) + + if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &n)) n = 0; return n; } -/* Temporary helper. */ + +/* Return the number of secret key parameters as used by OpenPGP. */ int -pubkey_get_nskey( int algo ) +pubkey_get_nskey (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 3; + else if (algo == PUBKEY_ALGO_ECDH) + return 4; + + /* All other algorithms match those of Libgcrypt. */ if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; - else if (algo == PUBKEY_ALGO_ECDSA) - algo = GCRY_PK_ECDSA; - else if (algo == PUBKEY_ALGO_ECDH) - algo = GCRY_PK_ECDH; + if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &n )) n = 0; return n; @@ -1383,33 +1405,40 @@ pubkey_get_nskey( int algo ) /* Temporary helper. */ int -pubkey_get_nsig( int algo ) +pubkey_get_nsig (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 2; + else if (algo == PUBKEY_ALGO_ECDH) + return 0; + if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; - else if (algo == PUBKEY_ALGO_ECDSA) - algo = GCRY_PK_ECDSA; - else if (algo == PUBKEY_ALGO_ECDH) - algo = GCRY_PK_ECDH; + if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &n)) n = 0; return n; } + /* Temporary helper. */ int -pubkey_get_nenc( int algo ) +pubkey_get_nenc (int algo) { size_t n; + /* ECC is special. */ + if (algo == PUBKEY_ALGO_ECDSA) + return 0; + else if (algo == PUBKEY_ALGO_ECDH) + return 2; + if (algo == GCRY_PK_ELG_E) algo = GCRY_PK_ELG; - else if (algo == PUBKEY_ALGO_ECDSA) - algo = GCRY_PK_ECDSA; - else if (algo == PUBKEY_ALGO_ECDH) - algo = GCRY_PK_ECDH; + if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &n )) n = 0; return n; @@ -1442,9 +1471,16 @@ pubkey_nbits( int algo, gcry_mpi_t *key ) key[0], key[1] ); } else if( algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH ) { - rc = gcry_sexp_build ( &sexp, NULL, - "(public-key(ecc(c%m)(q%m)))", - key[0], key[1] /* not affecting the size calculation, so use 'ecc' == 'ecdsa' */ ); + char *curve = openpgp_oid_to_str (key[0]); + if (!curve) + rc = gpg_error_from_syserror (); + else + { + rc = gcry_sexp_build (&sexp, NULL, + "(public-key(ecc(curve%s)(q%m)))", + curve, key[1]); + xfree (curve); + } } else return 0; @@ -1472,6 +1508,19 @@ mpi_print (estream_t fp, gcry_mpi_t a, int mode) n1 = gcry_mpi_get_nbits(a); n += es_fprintf (fp, "[%u bits]", n1); } + else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits; + unsigned char *p = gcry_mpi_get_opaque (a, &nbits); + if (!p) + n += es_fprintf (fp, "[invalid opaque value]"); + else + { + nbits = (nbits + 7)/8; + for (; nbits; nbits--, p++) + n += es_fprintf (fp, "%02X", *p); + } + } else { unsigned char *buffer; @@ -1501,3 +1550,206 @@ ecdsa_qbits_from_Q (unsigned int qbits) qbits /= 2; return 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)) + { + buf[buflen++] = 0x80 | (value >> 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"); +} + |