diff --git a/agent/command.c b/agent/command.c
index 92e12e2..c7853a9 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -271,7 +271,6 @@ parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf)
 {
   int rc;
   size_t n;
-  const unsigned char *p;
 
   rc = parse_hexstring (ctx, string, &n);
   if (rc)
@@ -280,8 +279,8 @@ parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf)
   if (n != 20)
     return set_error (GPG_ERR_ASS_PARAMETER, "invalid length of keygrip");
 
-  for (p=(const unsigned char*)string, n=0; n < 20; p += 2, n++)
-    buf[n] = xtoi_2 (p);
+  if (hex2bin (string, buf, 20) < 0)
+    return set_error (GPG_ERR_BUG, "hex2bin");
 
   return 0;
 }
@@ -1100,7 +1099,7 @@ cmd_preset_passphrase (assuan_context_t ctx, char *line)
   size_t len;
 
   if (!opt.allow_preset_passphrase)
-    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    return set_error (GPG_ERR_NOT_SUPPORTED, "no --allow-preset-passphrase");
 
   rc = parse_keygrip (ctx, line, grip);
   if (rc)
@@ -1135,11 +1134,17 @@ cmd_preset_passphrase (assuan_context_t ctx, char *line)
   /* If there is a passphrase, use it.  Currently, a passphrase is
      required.  */
   if (*line)
-    passphrase = line;
+    {
+      /* Do in-place conversion.  */
+      passphrase = line;
+      if (!hex2str (passphrase, passphrase, strlen (passphrase)+1, NULL))
+        rc = set_error (GPG_ERR_ASS_PARAMETER, "invalid hexstring");
+    }
   else
-    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    rc = set_error (GPG_ERR_NOT_IMPLEMENTED, "passphrase is required");
 
-  rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl);
+  if (!rc)
+    rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl);
 
   if (rc)
     log_error ("command preset_passwd failed: %s\n", gpg_strerror (rc));
diff --git a/agent/preset-passphrase.c b/agent/preset-passphrase.c
index 42c7d6c..cb906ad 100644
--- a/agent/preset-passphrase.c
+++ b/agent/preset-passphrase.c
@@ -113,37 +113,6 @@ my_strusage (int level)
 /* Include the implementation of map_spwq_error.  */
 MAP_SPWQ_ERROR_IMPL
       
-/* Convert the string SRC into HEX encoding.  Caller needs to xfree
-   the returned string.  */
-static char *
-make_hexstring (const char *src)
-{
-  int len = 2 * strlen (src) + 1;
-  char *dst;
-  char *res;
-
-  res = dst = xtrymalloc (len);
-  if (!dst)
-    {
-      log_error ("can not escape string: %s\n",
-		 gpg_strerror (gpg_error_from_syserror ()));
-      return NULL;
-    }
-
-#define _tohex(nr)	((nr) < 10 ? ((nr) + '0') : (((nr) - 10) + 'A'))
-#define tohex1(p)  _tohex (*((unsigned char *) p) & 15)
-#define tohex2(p)  _tohex ((*((unsigned char *) p) >> 4) & 15)
-
-  while (*src)
-    {
-      *(dst++) = tohex2 (src);
-      *(dst++) = tohex1 (src);
-      src++;
-    }
-  *dst = '\0';
-  return res;
-}
-
 
 static void
 preset_passphrase (const char *keygrip)
@@ -175,11 +144,14 @@ preset_passphrase (const char *keygrip)
       /* FIXME: How to handle empty passwords?  */
     }
 
-  passphrase_esc = make_hexstring (opt_passphrase
-				   ? opt_passphrase : passphrase);
+  {
+    const char *s = opt_passphrase ? opt_passphrase : passphrase;
+    passphrase_esc = bin2hex (s, strlen (s), NULL);
+  }
   if (!passphrase_esc)
     {
-      /* Error message printed by callee.  */
+      log_error ("can not escape string: %s\n",
+		 gpg_strerror (gpg_error_from_syserror ()));
       return;
     }
 
diff --git a/common/convert.c b/common/convert.c
index 7aa6354..c946b81 100644
--- a/common/convert.c
+++ b/common/convert.c
@@ -160,3 +161,87 @@ bin2hexcolon (const void *buffer, size_t length, char *stringbuf)
 }
 
 
+
+/* Convert HEXSTRING consisting of hex characters into string and
+   store that at BUFFER.  HEXSTRING is either delimited by end of
+   string or a white space character.  The function makes sure that
+   the resulting string in BUFFER is terminated by a Nul character.
+   BUFSIZE is the availabe length of BUFFER; if the converted result
+   plus a possible required Nul character does not fit into this
+   buffer, the function returns NULL and won't change the existing
+   conent of buffer.  In-place conversion is possible as long as
+   BUFFER points to HEXSTRING.
+   
+   If BUFFER is NULL and bufsize is 0 the function scans HEXSTRING but
+   does not store anything.  This may be used to find the end of
+   hexstring.
+
+   On sucess the function returns a pointer to the next character
+   after HEXSTRING (which is either end-of-string or a the next white
+   space).  If BUFLEN is not NULL the strlen of buffer is stored
+   there; this will even be done if BUFFER has been passed as NULL. */
+const char *
+hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen)
+{
+  const char *s = hexstring;
+  int idx, count;
+  int need_nul = 0;
+
+  if (buflen)
+    *buflen = 0;
+
+  for (s=hexstring, count=0; hexdigitp (s) && hexdigitp (s+1); s += 2, count++)
+    ;
+  if (*s && (!isascii (*s) || !isspace (*s)) )
+    return NULL;   /* Not followed by Nul or white space.  */
+  need_nul = !(s[-2] == '0' && s[-1] == '0');
+  if (need_nul)
+    count++;
+
+  if (buffer)
+    {
+      if (count > bufsize)
+        return NULL; /* Too long.  */
+      
+      for (s=hexstring, idx=0; hexdigitp (s) && hexdigitp (s+1); s += 2)
+        ((unsigned char*)buffer)[idx++] = xtoi_2 (s);
+      if (need_nul)
+        buffer[idx] = 0;
+    }
+
+  if (buflen)
+    *buflen = count - 1;
+  return s;
+}
+
+
+/* Same as hex2str but this function allocated a new string.  Returns
+   NULL on error.  If R_COUNT is not NULL, the number of scanned bytes
+   will be stored there.  ERRNO is set on error. */
+char *
+hex2str_alloc (const char *hexstring, size_t *r_count)
+{
+  const char *tail;
+  size_t nbytes;
+  char *result;
+
+  tail = hex2str (hexstring, NULL, 0, &nbytes);
+  if (!tail)
+    {
+      if (r_count)
+        *r_count = 0;
+      errno = EINVAL;
+      return NULL;
+    }
+  if (r_count)
+    *r_count = tail - hexstring;
+  result = xtrymalloc (nbytes+1);
+  if (!result)
+    return NULL;
+  if (!hex2str (hexstring, result, nbytes+1, NULL))
+    BUG ();
+  return result;
+}
+
+
+
diff --git a/common/t-convert.c b/common/t-convert.c
index ede1e46..caf743f 100644
--- a/common/t-convert.c
+++ b/common/t-convert.c
@@ -20,6 +20,7 @@
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <assert.h>
 
 #include "util.h"
 
@@ -275,6 +276,173 @@ test_bin2hexcolon (void)
 
 
 
+static void
+test_hex2str (void)
+{
+  static struct {
+    const char *hex;
+    const char *str;
+    int off;
+    int no_alloc_test;
+  } tests[] = {
+    /* Simple tests.  */
+    { "112233445566778899aabbccddeeff1122",
+      "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22",
+      34 },
+    { "112233445566778899aabbccddeeff1122 blah",
+      "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22",
+      34 },
+    { "112233445566778899aabbccddeeff1122\tblah",
+      "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22",
+      34 },
+    { "112233445566778899aabbccddeeff1122\nblah",
+      "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22",
+      34 },
+    /* Valid tests yielding an empty string.  */
+    { "00",
+      "",
+      2 },
+    { "00 x",
+      "",
+      2 },
+    { "",
+      "",
+      0 },
+    { " ",
+      "",
+      0 },
+    /* Test trailing Nul feature.  */
+    { "112233445566778899aabbccddeeff112200",
+      "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22",
+      36 },
+    { "112233445566778899aabbccddeeff112200 ",
+      "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22",
+      36 },
+    /* Test buffer size. (buffer is of length 20)  */
+    { "6162636465666768696A6b6c6D6e6f70717273",
+      "abcdefghijklmnopqrs",
+      38 },
+    { "6162636465666768696A6b6c6D6e6f7071727300",
+      "abcdefghijklmnopqrs",
+      40 },
+    { "6162636465666768696A6b6c6D6e6f7071727374",
+      NULL,
+      0, 1 },
+    { "6162636465666768696A6b6c6D6e6f707172737400",
+      NULL,
+      0, 1 },
+    { "6162636465666768696A6b6c6D6e6f707172737475",
+      NULL,
+      0, 1 },
+
+    /* Invalid tests. */
+    { "112233445566778899aabbccddeeff1122334",      NULL, 0 },
+    { "112233445566778899AABBCCDDEEFF1122334",      NULL, 0 },
+    { "112233445566778899AABBCCDDEEFG11223344",     NULL, 0 },
+    { "0:0112233445566778899aabbccddeeff11223344",  NULL, 0 },
+    { "112233445566778899aabbccddeeff11223344:",    NULL, 0 },
+    { "112233445566778899aabbccddeeff112233445",    NULL, 0 },
+    { "112233445566778899aabbccddeeff1122334455",   NULL, 0, 1 },
+    { "112233445566778899aabbccddeeff11223344blah", NULL, 0 },
+    { "0",    NULL, 0 },
+    { "00:",  NULL, 0 },
+    { "00x",  NULL, 0 },
+
+    { NULL, NULL, 0 }
+  };
+
+  int idx;
+  char buffer[20];
+  const char *tail;
+  size_t count;
+  char *result;
+
+  for (idx=0; tests[idx].hex; idx++)
+    {
+      tail = hex2str (tests[idx].hex, buffer, sizeof buffer, &count);
+      if (tests[idx].str)
+        {
+          /* Good case test.  */
+          if (!tail)
+            fail (idx);
+          else if (strcmp (tests[idx].str, buffer))
+            fail (idx);
+          else if (tail - tests[idx].hex != tests[idx].off)
+            fail (idx);
+          else if (strlen (buffer) != count)
+            fail (idx);
+        }
+      else
+        {
+          /* Bad case test.  */
+          if (tail)
+            fail (idx);
+        }
+    }
+
+  /* Same tests again using in-place conversion.  */
+  for (idx=0; tests[idx].hex; idx++)
+    {
+      char tmpbuf[100];
+      
+      assert (strlen (tests[idx].hex)+1 < sizeof tmpbuf);
+      strcpy (tmpbuf, tests[idx].hex);
+      
+      /* Note: we still need to use 20 as buffer length because our
+         tests assume that. */
+      tail = hex2str (tmpbuf, tmpbuf, 20, &count);
+      if (tests[idx].str)
+        {
+          /* Good case test.  */
+          if (!tail)
+            fail (idx);
+          else if (strcmp (tests[idx].str, tmpbuf))
+            fail (idx);
+          else if (tail - tmpbuf != tests[idx].off)
+            fail (idx);
+          else if (strlen (tmpbuf) != count)
+            fail (idx);
+        }
+      else
+        {
+          /* Bad case test.  */
+          if (tail)
+            fail (idx);
+          if (strcmp (tmpbuf, tests[idx].hex))
+            fail (idx); /* Buffer was modified.  */
+        }
+    }
+
+  /* Test the allocation variant.  */
+  for (idx=0; tests[idx].hex; idx++)
+    {
+      if (tests[idx].no_alloc_test)
+        continue;
+
+      result = hex2str_alloc (tests[idx].hex, &count);
+      if (tests[idx].str)
+        {
+          /* Good case test.  */
+          if (!result)
+            fail (idx);
+          else if (strcmp (tests[idx].str, result))
+            fail (idx);
+          else if (count != tests[idx].off)
+            fail (idx);
+        }
+      else
+        {
+          /* Bad case test.  */
+          if (result)
+            fail (idx);
+        }
+      xfree (result);
+    }
+}
+
+
+
+
 
 int
 main (int argc, char **argv)
@@ -284,6 +452,7 @@ main (int argc, char **argv)
   test_hexcolon2bin ();
   test_bin2hex ();
   test_bin2hexcolon ();
+  test_hex2str ();
 
   return 0;
 }
diff --git a/common/util.h b/common/util.h
index 5b564a2..572fa1a 100644
--- a/common/util.h
+++ b/common/util.h
@@ -189,6 +189,9 @@ 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 *bin2hexcolon (const void *buffer, size_t length, char *stringbuf);
+const char *hex2str (const char *hexstring,
+                     char *buffer, size_t bufsize, size_t *buflen);
+char *hex2str_alloc (const char *hexstring, size_t *r_count);
 
 
 /*-- homedir.c --*/

