aboutsummaryrefslogtreecommitdiffstats
path: root/g10/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/misc.c')
-rw-r--r--g10/misc.c304
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");
+}
+