diff --git a/glib/Makefile.am b/glib/Makefile.am
index 65142f2..fb03733 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -143,6 +143,7 @@ libglib_2_0_la_SOURCES = 	\
 	gfileutils.c		\
 	ggettext.c		\
 	ghash.c			\
+	ghex.c			\
 	ghmac.c			\
 	ghook.c			\
 	ghostutils.c		\
@@ -273,6 +274,7 @@ glibsubinclude_HEADERS = \
 	gfileutils.h	\
 	ggettext.h	\
 	ghash.h		\
+	ghex.h		\
 	ghmac.h		\
 	ghook.h		\
 	ghostutils.h	\
diff --git a/glib/ghex.c b/glib/ghex.c
new file mode 100644
index 0000000..8de6449
--- /dev/null
+++ b/glib/ghex.c
@@ -0,0 +1,103 @@
+/* ghex.h - hex encoding functions
+ *
+ * Copyright (C) 2012  Christian Hergert  <chris@dronelabs.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gbytes.h"
+#include "ghex.h"
+#include "gmem.h"
+#include "gmessages.h"
+#include "gstring.h"
+
+/**
+ * g_hex_encode:
+ * @bytes: (in): The bytes to encode.
+ *
+ * Generates a string containing @bytes encoded as hex.
+ *
+ * Returns: (transfer full): A string containing the hex encoded bytes.
+ *    The resulting string should be freed with g_free().
+ */
+gchar *
+g_hex_encode (GBytes *bytes)
+{
+  const guint8 *data;
+  GString *str;
+  gsize size;
+  guint i;
+
+  g_return_val_if_fail(bytes, NULL);
+
+  data = g_bytes_get_data(bytes, &size);
+  str = g_string_new(NULL);
+  for (i = 0; i < size; i++)
+    g_string_append_printf(str, "%02x", data[i]);
+  return g_string_free(str, FALSE);
+}
+
+/**
+ * g_hex_decode:
+ * @encoded: (in): A hex encoded string.
+ * @encoded_len: (in): Length of @encoded or -1 for a %NULL-terminated string.
+ *
+ * Decodes @encoded, a hex-encoded string into the raw bytes. If successful,
+ * the decoded buffer is returned and @decoded_len is set to the number of
+ * bytes in the resulting buffer.
+ *
+ * Returns: (transfer full): The decoded hex string if successful,
+ *    otherwise %NULL. The resulting bytes should be freed with
+ *    g_bytes_unref().
+ */
+GBytes *
+g_hex_decode (const gchar *encoded,
+              gssize       encoded_len)
+{
+  GByteArray *bytes;
+  guint8 b;
+  guint i;
+  gint v1;
+  gint v2;
+
+  g_return_val_if_fail(encoded, NULL);
+
+  if (encoded_len < 0)
+    encoded_len = strlen(encoded);
+
+  if ((encoded_len % 2) != 0)
+    return NULL;
+
+  bytes = g_byte_array_sized_new(encoded_len / 2);
+
+  for (i = 0; i < encoded_len; i += 2)
+    {
+      if ((-1 == (v1 = g_ascii_xdigit_value(encoded[i]))) ||
+          (-1 == (v2 = g_ascii_xdigit_value(encoded[i+1]))))
+        {
+          g_byte_array_free(bytes, TRUE);
+          return NULL;
+        }
+      b = (v1 << 4) | v2;
+      g_byte_array_append(bytes, &b, 1);
+    }
+
+  return g_byte_array_free_to_bytes(bytes);
+}
diff --git a/glib/ghex.h b/glib/ghex.h
new file mode 100644
index 0000000..1952919
--- /dev/null
+++ b/glib/ghex.h
@@ -0,0 +1,40 @@
+/* ghex.h - hex encoding functions
+ *
+ * Copyright (C) 2012  Christian Hergert  <chris@dronelabs.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only <glib.h> can be included directly."
+#endif
+
+#ifndef __G_HEX_H__
+#define __G_HEX_H__
+
+#include <glib/gtypes.h>
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_2_34
+gchar  *g_hex_encode (GBytes       *bytes);
+GLIB_AVAILABLE_IN_2_34
+GBytes *g_hex_decode (const gchar  *encoded,
+                      gssize        encoded_len);
+
+G_END_DECLS
+
+#endif /* __G_HEX_H__ */
diff --git a/glib/glib.h b/glib/glib.h
index 77abf23..58ffab2 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -50,6 +50,7 @@
 #include <glib/gfileutils.h>
 #include <glib/ggettext.h>
 #include <glib/ghash.h>
+#include <glib/ghex.h>
 #include <glib/ghmac.h>
 #include <glib/ghook.h>
 #include <glib/ghostutils.h>
diff --git a/glib/tests/.gitignore b/glib/tests/.gitignore
index 1bc83c5..f86218e 100644
--- a/glib/tests/.gitignore
+++ b/glib/tests/.gitignore
@@ -25,6 +25,7 @@ gvariant
 gwakeup
 gwakeup-fallback
 hash
+hex
 hmac
 hook
 hostutils
diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am
index 26ae31c..dc05b44 100644
--- a/glib/tests/Makefile.am
+++ b/glib/tests/Makefile.am
@@ -283,6 +283,9 @@ unix_nothreads_LDADD  = $(progs_ldadd)
 TEST_PROGS      += include
 include_LDADD    = $(progs_ldadd)
 
+TEST_PROGS += hex
+hex_LDADD = $(progs_ldadd)
+
 # some testing of gtester funcitonality
 XMLLINT=xmllint
 gtester-xmllint-check: # check testreport xml with xmllint if present
diff --git a/glib/tests/hex.c b/glib/tests/hex.c
new file mode 100644
index 0000000..2225ab8
--- /dev/null
+++ b/glib/tests/hex.c
@@ -0,0 +1,88 @@
+/* GLIB - Library of useful routines for C programming
+ *
+ * Copyright (C) 2012  Christian Hergert  <chris@dronelabs.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef G_DISABLE_ASSERT
+#undef G_DISABLE_ASSERT
+#endif
+
+#include <glib.h>
+#include <string.h>
+
+static const guint8 test1[] = { 0x00, 0x01, 0x02, 0x03 };
+static const guint8 test2[] = { 0x07, 0xab, 0xff, 0x00 };
+static const guint8 test3[] = { 0xfa };
+
+static void
+test_encode (void)
+{
+#define TEST_ENCODE(d, s) \
+   G_STMT_START { \
+      GBytes *b = g_bytes_new_static((d), G_N_ELEMENTS(d)); \
+      gchar *str = g_hex_encode(b); \
+      g_assert_cmpstr(str, ==, s); \
+      g_free(str); \
+      g_bytes_unref(b); \
+   } G_STMT_END
+
+   TEST_ENCODE(test1, "00010203");
+   TEST_ENCODE(test2, "07abff00");
+   TEST_ENCODE(test3, "fa");
+
+#undef TEST_ENCODE
+}
+
+static void
+test_decode (void)
+{
+#define TEST_DECODE(d, s) \
+   G_STMT_START { \
+      GBytes *b = g_bytes_new_static((d), G_N_ELEMENTS(d)); \
+      GBytes *b2 = g_hex_decode((s), -1); \
+      g_assert_cmpint(0, ==, g_bytes_compare(b, b2)); \
+      g_bytes_unref(b); \
+      g_bytes_unref(b2); \
+   } G_STMT_END
+
+#define TEST_DECODE_FAILURE(s) \
+   G_STMT_START { \
+      GBytes *b = g_hex_decode((s), -1); \
+      g_assert(!b); \
+   } G_STMT_END
+
+   TEST_DECODE(test1, "00010203");
+   TEST_DECODE(test2, "07abff00");
+   TEST_DECODE(test3, "fa");
+   TEST_DECODE_FAILURE("f");
+   TEST_DECODE_FAILURE("fbacd");
+   TEST_DECODE_FAILURE("123zzz");
+
+#undef TEST_DECODE
+#undef TEST_DECODE_FAILURE
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+   g_test_init(&argc, &argv, NULL);
+   g_test_add_func("/Hex/encode", test_encode);
+   g_test_add_func("/Hex/decode", test_decode);
+   return g_test_run();
+}
