[libsecret/wip/dueno/local-file: 1/8] egg: Add egg-base64 module for base64url encoding



commit cf1751d511e3007a4c99b354d4af1c256718e42b
Author: Daiki Ueno <dueno src gnome org>
Date:   Thu Sep 20 13:57:40 2018 +0200

    egg: Add egg-base64 module for base64url encoding

 egg/Makefile.am   |   5 +
 egg/egg-base64.c  | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 egg/egg-base64.h  |  34 +++++++
 egg/test-base64.c |  71 ++++++++++++++
 4 files changed, 385 insertions(+)
---
diff --git a/egg/Makefile.am b/egg/Makefile.am
index dc136a8..2c647f7 100644
--- a/egg/Makefile.am
+++ b/egg/Makefile.am
@@ -12,6 +12,7 @@ ENCRYPTION_SRCS =
 endif
 
 libegg_la_SOURCES = \
+       egg/egg-base64.c egg/egg-base64.h \
        egg/egg-hex.c egg/egg-hex.h \
        egg/egg-secure-memory.c egg/egg-secure-memory.h \
        egg/egg-testing.c egg/egg-testing.h \
@@ -23,9 +24,13 @@ egg_LIBS =  \
        $(GLIB_LIBS)
 
 egg_TESTS = \
+       test-base64 \
        test-hex \
        test-secmem
 
+test_base64_SOURCES = egg/test-base64.c
+test_base64_LDADD = $(egg_LIBS)
+
 test_hex_SOURCES = egg/test-hex.c
 test_hex_LDADD = $(egg_LIBS)
 
diff --git a/egg/egg-base64.c b/egg/egg-base64.c
new file mode 100644
index 0000000..e0e8e22
--- /dev/null
+++ b/egg/egg-base64.c
@@ -0,0 +1,275 @@
+/* egg-base64.c - Base64 encoding/decoding
+ *
+ *  Copyright (C) 2006 Alexander Larsson <alexl redhat com>
+ *  Copyright (C) 2000-2003 Ximian Inc.
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ *
+ * This is based on code in camel, written by:
+ *    Michael Zucchi <notzed ximian com>
+ *    Jeffrey Stedfast <fejj ximian com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "egg-base64.h"
+
+/* This is a copy of glib/glib/gbase64.c.  The only difference is that
+   this version uses URL- and filename-safe alphabets as described in
+   RFC4848 and omits padding and line breaks, for the use with JWE.  */
+
+static const char base64_alphabet[] =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+static gsize
+egg_base64_encode_step (const guchar *in,
+                       gsize         len,
+                       gchar        *out,
+                       gint         *state,
+                       gint         *save)
+{
+  char *outptr;
+  const guchar *inptr;
+
+  g_return_val_if_fail (in != NULL, 0);
+  g_return_val_if_fail (out != NULL, 0);
+  g_return_val_if_fail (state != NULL, 0);
+  g_return_val_if_fail (save != NULL, 0);
+
+  if (len <= 0)
+    return 0;
+
+  inptr = in;
+  outptr = out;
+
+  if (len + ((char *) save) [0] > 2)
+    {
+      const guchar *inend = in+len-2;
+      int c1, c2, c3;
+      int already;
+
+      already = *state;
+
+      switch (((char *) save) [0])
+        {
+        case 1:
+          c1 = ((unsigned char *) save) [1];
+          goto skip1;
+        case 2:
+          c1 = ((unsigned char *) save) [1];
+          c2 = ((unsigned char *) save) [2];
+          goto skip2;
+        }
+
+      /*
+       * yes, we jump into the loop, no i'm not going to change it,
+       * it's beautiful!
+       */
+      while (inptr < inend)
+        {
+          c1 = *inptr++;
+        skip1:
+          c2 = *inptr++;
+        skip2:
+          c3 = *inptr++;
+          *outptr++ = base64_alphabet [ c1 >> 2 ];
+          *outptr++ = base64_alphabet [ c2 >> 4 |
+                                        ((c1&0x3) << 4) ];
+          *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
+                                        (c3 >> 6) ];
+          *outptr++ = base64_alphabet [ c3 & 0x3f ];
+        }
+
+      ((char *)save)[0] = 0;
+      len = 2 - (inptr - inend);
+      *state = already;
+    }
+
+  if (len>0)
+    {
+      char *saveout;
+
+      /* points to the slot for the next char to save */
+      saveout = & (((char *)save)[1]) + ((char *)save)[0];
+
+      /* len can only be 0 1 or 2 */
+      switch(len)
+        {
+        case 2: *saveout++ = *inptr++;
+        case 1: *saveout++ = *inptr++;
+        }
+      ((char *)save)[0] += len;
+    }
+
+  return outptr - out;
+}
+
+static gsize
+egg_base64_encode_close (gchar *out,
+                         gint  *state,
+                         gint  *save)
+{
+  int c1, c2;
+  char *outptr = out;
+
+  g_return_val_if_fail (out != NULL, 0);
+  g_return_val_if_fail (state != NULL, 0);
+  g_return_val_if_fail (save != NULL, 0);
+
+  c1 = ((unsigned char *) save) [1];
+  c2 = ((unsigned char *) save) [2];
+
+  switch (((char *) save) [0])
+    {
+    case 2:
+      outptr [0] = base64_alphabet [ c1 >> 2 ];
+      outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 ) ];
+      outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
+      g_assert (outptr [2] != 0);
+      outptr += 3;
+      break;
+    case 1:
+      outptr [0] = base64_alphabet [ c1 >> 2 ];
+      outptr [1] = base64_alphabet [ ( (c1&0x3) << 4 ) ];
+      outptr += 2;
+      break;
+    }
+
+  *save = 0;
+  *state = 0;
+
+  return outptr - out;
+}
+
+gchar *
+egg_base64_encode (const guchar *data,
+                  gsize         len)
+{
+  gchar *out;
+  gint state = 0, outlen;
+  gint save = 0;
+
+  g_return_val_if_fail (data != NULL || len == 0, NULL);
+
+  /* We can use a smaller limit here, since we know the saved state is 0,
+     +1 is needed for trailing \0, also check for unlikely integer overflow */
+  if (len >= ((G_MAXSIZE - 1) / 4 - 1) * 3)
+    g_error("%s: input too large for Base64 encoding (%"G_GSIZE_FORMAT" chars)",
+        G_STRLOC, len);
+
+  out = g_malloc ((len / 3 + 1) * 4 + 1);
+
+  outlen = egg_base64_encode_step (data, len, out, &state, &save);
+  outlen += egg_base64_encode_close (out + outlen, &state, &save);
+  out[outlen] = '\0';
+
+  return (gchar *) out;
+}
+
+static const unsigned char mime_base64_rank[256] = {
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255, 62,255,255,
+   52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255,
+  255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255, 63,
+  255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+  255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+};
+
+static gsize
+egg_base64_decode_step (const gchar  *in,
+                       gsize         len,
+                       guchar       *out)
+{
+  const guchar *inptr;
+  guchar *outptr;
+  const guchar *inend;
+  guchar c, rank;
+  guchar last[2];
+  unsigned int v;
+  int i;
+
+  g_return_val_if_fail (in != NULL, 0);
+  g_return_val_if_fail (out != NULL, 0);
+
+  if (len <= 0)
+    return 0;
+
+  inend = (const guchar *)in+len;
+  outptr = out;
+
+  /* convert 4 base64 bytes to 3 normal bytes */
+  v=0;
+  i=0;
+
+  last[0] = last[1] = 0;
+
+  inptr = (const guchar *)in;
+  while (inptr < inend)
+    {
+      c = *inptr++;
+      rank = mime_base64_rank [c];
+      if (rank != 0xff)
+        {
+          last[1] = last[0];
+          last[0] = c;
+          v = (v<<6) | rank;
+          i++;
+          if (i==4)
+            {
+              *outptr++ = v>>16;
+             *outptr++ = v>>8;
+             *outptr++ = v;
+              i=0;
+            }
+        }
+    }
+
+  if (i > 0)
+    {
+      v <<= 6*(4-i);
+      *outptr++ = v>>16;
+      if (i == 3)
+       *outptr++ = v>>8;
+    }
+
+  return outptr - out;
+}
+
+guchar *
+egg_base64_decode_inplace (gchar *text,
+                          gsize *out_len)
+{
+  gint input_length;
+
+  g_return_val_if_fail (text != NULL, NULL);
+  g_return_val_if_fail (out_len != NULL, NULL);
+
+  input_length = strlen (text);
+
+  *out_len = egg_base64_decode_step (text, input_length, (guchar *) text);
+
+  return (guchar *) text;
+}
diff --git a/egg/egg-base64.h b/egg/egg-base64.h
new file mode 100644
index 0000000..0267f5b
--- /dev/null
+++ b/egg/egg-base64.h
@@ -0,0 +1,34 @@
+/* egg-base64.h - Base64 coding functions
+ *
+ *  Copyright (C) 2005  Alexander Larsson <alexl redhat 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.1 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __EGG_BASE64_H__
+#define __EGG_BASE64_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar*  egg_base64_encode         (const guchar *data,
+                                   gsize         len) G_GNUC_MALLOC;
+guchar *egg_base64_decode_inplace (gchar        *text,
+                                   gsize        *out_len);
+
+
+G_END_DECLS
+
+#endif /* __EGG_BASE64_H__ */
diff --git a/egg/test-base64.c b/egg/test-base64.c
new file mode 100644
index 0000000..c9a7e58
--- /dev/null
+++ b/egg/test-base64.c
@@ -0,0 +1,71 @@
+/* test-base64.c: Test egg-base64.c
+
+   Copyright (C) 2018 Red Hat, Inc.
+
+   The Gnome Keyring 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.
+
+   The Gnome Keyring 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 the Gnome Library; see the file COPYING.LIB.  If not,
+   see <http://www.gnu.org/licenses/>.
+
+   Author: Daiki Ueno
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "egg/egg-base64.h"
+#include "egg/egg-testing.h"
+
+static void
+test_base64 (void)
+{
+       struct {
+               const guchar *input;
+               gsize length;
+               const gchar *output;
+       } tests[] = {
+               { (const guchar *) "", 0, "" },
+               { (const guchar *) "f", 1, "Zg" },
+               { (const guchar *) "fo", 2, "Zm8" },
+               { (const guchar *) "foo", 3, "Zm9v" },
+               { (const guchar *) "foob", 4, "Zm9vYg" },
+               { (const guchar *) "fooba", 5, "Zm9vYmE" },
+               { (const guchar *) "\xff\xee\xdd\xcc\xbb\xaa", 6, "_-7dzLuq" }
+       };
+       gsize i;
+
+       for (i = 0; i < G_N_ELEMENTS (tests); i++) {
+               gchar *output;
+               guchar *input;
+               gsize length;
+
+               output = egg_base64_encode (tests[i].input, tests[i].length);
+               g_assert_cmpstr (output, ==, tests[i].output);
+
+               input = egg_base64_decode_inplace (output, &length);
+               g_assert_cmpmem (input, length, tests[i].input, tests[i].length);
+               g_free (output);
+       }
+}
+
+int
+main (int argc, char **argv)
+{
+       g_test_init (&argc, &argv, NULL);
+
+       g_test_add_func ("/base64/test-base64", test_base64);
+
+       return g_test_run ();
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]