From 52b7a60cf9f3cd2e5900396b0e3e65cbd335bc23 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 29 Sep 2023 11:34:06 +0200 Subject: [PATCH] common: Add new function b64decode. * common/b64dec.c (b64decode): New. * common/t-b64.c: Change license to LGPL. (oops): New macro. (hex2buffer): New. (test_b64decode): New. (main): Default to run the new test. * common/Makefile.am (module_maint_tests): Move t-b64 to ... (module_tests): here. -- Sometimes we have a short base64 encoded string we need todecode. This function makes it simpler. License change of the test module justified because I am the single author of the code. --- common/Makefile.am | 4 +- common/b64dec.c | 45 +++++++++++++++ common/b64enc.c | 1 + common/t-b64.c | 134 +++++++++++++++++++++++++++++++++++++++------ common/util.h | 2 + 5 files changed, 168 insertions(+), 18 deletions(-) diff --git a/common/Makefile.am b/common/Makefile.am index d5ab038bf..7c78708da 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -161,7 +161,7 @@ module_tests = t-stringhelp t-timestuff \ t-convert t-percent t-gettime t-sysutils t-sexputil \ t-session-env t-openpgp-oid t-ssh-utils \ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ - t-name-value t-ccparray t-recsel t-w32-cmdline + t-name-value t-ccparray t-recsel t-w32-cmdline t-b64 if HAVE_W32_SYSTEM module_tests += t-w32-reg else @@ -169,7 +169,7 @@ module_tests += t-exechelp t-exectool endif if MAINTAINER_MODE -module_maint_tests = t-helpfile t-b64 +module_maint_tests = t-helpfile else module_maint_tests = endif diff --git a/common/b64dec.c b/common/b64dec.c index 6af494b79..2904b0471 100644 --- a/common/b64dec.c +++ b/common/b64dec.c @@ -16,6 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . + * SPDX-License-Identifier: LGPL-2.1-or-later */ #include @@ -252,3 +253,47 @@ b64dec_finish (struct b64state *state) return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0; } + + +/* Convert STRING consisting of base64 characters into its binary + * representation and store the result in a newly allocated buffer at + * R_BUFFER with its length at R_BUFLEN. If TITLE is NULL a plain + * base64 decoding is done. If it is the empty string the decoder + * will skip everything until a "-----BEGIN " line has been seen, + * decoding then ends at a "----END " line. On failure the function + * returns an error code and sets R_BUFFER to NULL. If the decoded + * data has a length of 0 a dummy buffer will still be allocated and + * the length is set to 0. */ +gpg_error_t +b64decode (const char *string, const char *title, + void **r_buffer, size_t *r_buflen) +{ + gpg_error_t err; + struct b64state state; + size_t nbytes; + char *buffer; + + *r_buffer = NULL; + *r_buflen = 0; + + buffer = xtrystrdup (string); + if (!buffer) + return gpg_error_from_syserror(); + + err = b64dec_start (&state, title); + if (err) + { + xfree (buffer); + return err; + } + b64dec_proc (&state, buffer, strlen (buffer), &nbytes); + err = b64dec_finish (&state); + if (err) + xfree (buffer); + else + { + *r_buffer = buffer; + *r_buflen = nbytes; + } + return err; +} diff --git a/common/b64enc.c b/common/b64enc.c index d633048ea..7846dcb3e 100644 --- a/common/b64enc.c +++ b/common/b64enc.c @@ -18,6 +18,7 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . + * SPDX-License-Identifier: LGPL-2.1-or-later */ #include diff --git a/common/t-b64.c b/common/t-b64.c index 3b6387246..16c079d1d 100644 --- a/common/t-b64.c +++ b/common/t-b64.c @@ -1,30 +1,24 @@ /* t-b64.c - Module tests for b64enc.c and b64dec.c - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008, 2023 g10 Code GmbH * * This file is part of GnuPG. * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. + * This file 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. * - * GnuPG is distributed in the hope that it will be useful, + * This file 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 General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . + * SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - - As of now this is only a test program for manual tests. - - */ - - - #include #include #include @@ -36,10 +30,112 @@ __FILE__,__LINE__, (a)); \ errcount++; \ } while(0) +#define oops() do { fprintf (stderr, "%s:%d: ooops\n", \ + __FILE__,__LINE__); \ + exit (2); \ + } while(0) static int verbose; static int errcount; + +/* Convert STRING consisting of hex characters into its binary + * representation and return it as an allocated buffer. The valid + * length of the buffer is returned at R_LENGTH. The string is + * delimited by end of string. The function returns NULL on + * error. */ +static void * +hex2buffer (const char *string, size_t *r_length) +{ + const char *s; + unsigned char *buffer; + size_t length; + + buffer = xmalloc (strlen(string)/2+1); + length = 0; + for (s=string; *s; s +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return NULL; /* Invalid hex digits. */ + ((unsigned char*)buffer)[length++] = xtoi_2 (s); + } + *r_length = length; + return buffer; +} + + +static void +test_b64decode (void) +{ + static struct { + const char *string; /* String to test. */ + const char *title; /* title parameter. */ + gpg_error_t err; /* expected error. */ + const char *datastr; /* Expected data (hex encoded) */ + } tests[] = { + { "YQ==", NULL, 0, + "61" }, + { "YWE==", NULL, 0, + "6161" }, + { "YWFh", NULL, 0, + "616161" }, + { "YWFhYQ==", NULL, 0, + "61616161" }, + { "YWJjZA==", NULL, 0, + "61626364" }, + { "AA=", NULL, 0, + "00" }, + { "AAEA=", NULL, 0, + "000100" }, + { "/w==", NULL, 0, + "ff" }, + { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", NULL, 0, + "a1143012a0030a0103a10b06092a864882f712010202" }, + { "oRQwEqADCgEDoQsGCSqGSIL3EgECA-==", NULL, GPG_ERR_BAD_DATA, + "a1143012a0030a0103a10b06092a864882f712010202" }, + { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", "", 0, + "" }, + { "-----BEGIN PGP\n\n" + "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==\n" + "-----END PGP\n", "", 0, + "a1143012a0030a0103a10b06092a864882f712010202" }, + + { "", NULL, 0, + "" } + }; + int tidx; + gpg_error_t err; + void *data = NULL; + size_t datalen; + char *wantdata = NULL; + size_t wantdatalen; + + for (tidx = 0; tidx < DIM(tests); tidx++) + { + xfree (wantdata); + if (!(wantdata = hex2buffer (tests[tidx].datastr, &wantdatalen))) + oops (); + xfree (data); + err = b64decode (tests[tidx].string, tests[tidx].title, &data, &datalen); + if (verbose) + fprintf (stderr, "%s:%d: test %d, err=%d, datalen=%zu\n", + __FILE__, __LINE__, tidx, err, datalen); + if (gpg_err_code (err) != tests[tidx].err) + fail (tidx); + else if (err) + pass (); + else if (wantdatalen != datalen) + fail (tidx); + else if (memcmp (wantdata, data, datalen)) + fail (tidx); + else + pass (); + } + xfree (wantdata); + xfree (data); +} + + static void test_b64enc_pgp (const char *string) { @@ -101,6 +197,7 @@ test_b64enc_file (const char *fname) pass (); } + static void test_b64dec_file (const char *fname) { @@ -150,6 +247,7 @@ main (int argc, char **argv) { int do_encode = 0; int do_decode = 0; + int do_pgpdecode = 0; if (argc) { argc--; argv++; } @@ -169,13 +267,17 @@ main (int argc, char **argv) do_decode = 1; argc--; argv++; } + else if (argc) + do_pgpdecode = 1; if (do_encode) test_b64enc_file (argc? *argv: NULL); else if (do_decode) test_b64dec_file (argc? *argv: NULL); - else + else if (do_pgpdecode) test_b64enc_pgp (argc? *argv: NULL); + else + test_b64decode (); return !!errcount; } diff --git a/common/util.h b/common/util.h index aa24e39e6..83882caf2 100644 --- a/common/util.h +++ b/common/util.h @@ -171,6 +171,8 @@ gpg_error_t b64dec_start (struct b64state *state, const char *title); gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, size_t *r_nbytes); gpg_error_t b64dec_finish (struct b64state *state); +gpg_error_t b64decode (const char *string, const char *title, + void **r_buffer, size_t *r_buflen); /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen);