Actually increase buffer size of t-dns-cert.c.
[gnupg.git] / common / sexputil.c
index 4907a93..87f984f 100644 (file)
@@ -1,5 +1,5 @@
 /* sexputil.c - Utility functions for S-expressions.
- * Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #endif
 
 #include "util.h"
+#include "tlv.h"
 #include "sexp-parse.h"
 
+
+/* Helper function to create a canonical encoded S-expression from a
+   Libgcrypt S-expression object.  The function returns 0 on success
+   and the malloced canonical S-expression is stored at R_BUFFER and
+   the allocated length at R_BUFLEN.  On error an error code is
+   returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN.  If the
+   allocated buffer length is not required, NULL by be used for
+   R_BUFLEN.  */
+gpg_error_t
+make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen)
+{
+  size_t len;
+  unsigned char *buf;
+
+  *r_buffer = NULL;
+  if (r_buflen)
+    *r_buflen = 0;;
+
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
+  if (!len)
+    return gpg_error (GPG_ERR_BUG);
+  buf = xtrymalloc (len);
+  if (!buf)
+    return gpg_error_from_syserror ();
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len);
+  if (!len)
+    return gpg_error (GPG_ERR_BUG);
+
+  *r_buffer = buf;
+  if (r_buflen)
+    *r_buflen = len;
+
+  return 0;
+}
+
+
+/* Same as make_canon_sexp but pad the buffer to multiple of 64
+   bits.  If SECURE is set, secure memory will be allocated.  */
+gpg_error_t
+make_canon_sexp_pad (gcry_sexp_t sexp, int secure,
+                     unsigned char **r_buffer, size_t *r_buflen)
+{
+  size_t len;
+  unsigned char *buf;
+
+  *r_buffer = NULL;
+  if (r_buflen)
+    *r_buflen = 0;;
+
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
+  if (!len)
+    return gpg_error (GPG_ERR_BUG);
+  len += (8 - len % 8) % 8;
+  buf = secure? xtrycalloc_secure (1, len) : xtrycalloc (1, len);
+  if (!buf)
+    return gpg_error_from_syserror ();
+  if (!gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len))
+    return gpg_error (GPG_ERR_BUG);
+
+  *r_buffer = buf;
+  if (r_buflen)
+    *r_buflen = len;
+
+  return 0;
+}
+
 /* Return the so called "keygrip" which is the SHA-1 hash of the
    public key parameters expressed in a way depended on the algorithm.
 
@@ -62,7 +129,7 @@ keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
 
 
 /* Compare two simple S-expressions like "(3:foo)".  Returns 0 if they
-   are identical or !0 if they are not.  Not that this function can't
+   are identical or !0 if they are not.  Note that this function can't
    be used for sorting. */
 int
 cmp_simple_canon_sexp (const unsigned char *a_orig,
@@ -99,7 +166,7 @@ cmp_simple_canon_sexp (const unsigned char *a_orig,
 }
 
 
-/* Create a simple S-expression from the hex string at LIBNE.  Returns
+/* Create a simple S-expression from the hex string at LINE.  Returns
    a newly allocated buffer with that canonical encoded S-expression
    or NULL in case of an error.  On return the number of characters
    scanned in LINE will be stored at NSCANNED.  This fucntions stops
@@ -122,7 +189,7 @@ make_simple_sexp_from_hexstr (const char *line, size_t *nscanned)
     *nscanned = n;
   if (!n)
     return NULL;
-  len = ((n+1) & ~0x01)/2; 
+  len = ((n+1) & ~0x01)/2;
   numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen);
   buf = xtrymalloc (1 + numbuflen + len + 1 + 1);
   if (!buf)
@@ -185,7 +252,208 @@ hash_algo_from_sigval (const unsigned char *sigval)
     return 0; /* Algorithm string is missing or too long.  */
   memcpy (buffer, s, n);
   buffer[n] = 0;
-  
+
   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 parameters of a public RSA key expressed as an
+   canonical encoded S-expression.  */
+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 (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;
+}
+
+
+/* Return the algo of a public RSA expressed as an canonical encoded
+   S-expression.  On error the algo is set to 0. */
+gpg_error_t
+get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen,
+                             int *r_algo)
+{
+  gpg_error_t err;
+  const unsigned char *buf, *tok;
+  size_t buflen, toklen;
+  int depth;
+
+  *r_algo = 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)
+    return gpg_error (GPG_ERR_BAD_PUBKEY);
+
+  if (toklen == 3 && !memcmp ("rsa", tok, toklen))
+    *r_algo = GCRY_PK_RSA;
+  else if (toklen == 3 && !memcmp ("dsa", tok, toklen))
+    *r_algo = GCRY_PK_DSA;
+  else if (toklen == 3 && !memcmp ("elg", tok, toklen))
+    *r_algo = GCRY_PK_ELG;
+  else if (toklen == 5 && !memcmp ("ecdsa", tok, toklen))
+    *r_algo = GCRY_PK_ECDSA;
+  else
+    return  gpg_error (GPG_ERR_PUBKEY_ALGO);
+
+  return 0;
+}