base64: support binary data

Thu, 26 Mar 2020 08:41:12 +0100

author
David Demelier <markand@malikania.fr>
date
Thu, 26 Mar 2020 08:41:12 +0100
changeset 24
90760aa2e7ed
parent 23
fe5437c0fa3d
child 25
3172e43c9b43

base64: support binary data

base64.c file | annotate | diff | comparison | revisions
base64.h file | annotate | diff | comparison | revisions
test/base64.c file | annotate | diff | comparison | revisions
--- a/base64.c	Wed Mar 25 16:54:36 2020 +0100
+++ b/base64.c	Thu Mar 26 08:41:12 2020 +0100
@@ -79,19 +79,24 @@
 }
 
 size_t
-b64_encode(const char src[], char dst[], size_t dstsz)
+b64_encode(const char src[], size_t srcsz, char dst[], size_t dstsz)
 {
 	assert(src);
 	assert(dst);
 
+	if (srcsz == 0)
+		srcsz = strlen(src);
+
 	size_t nwritten = 0;
 
-	while (*src && dstsz) {
+	while (srcsz && dstsz) {
 		char inputbuf[3] = { 0 };
 		int count = 0;
 
-		while (*src && count < 3)
+		while (srcsz && count < 3) {
 			inputbuf[count++] = *src++;
+			--srcsz;
+		}
 
 		if (dstsz < 4) {
 			errno = ERANGE;
@@ -129,25 +134,29 @@
 }
 
 size_t
-b64_decode(const char src[], char dst[], size_t dstsz)
+b64_decode(const char src[], size_t srcsz, char dst[], size_t dstsz)
 {
 	assert(src);
 	assert(dst);
 
+	if (srcsz == 0)
+		srcsz = strlen(src);
+
 	size_t nwritten = 0;
 
-	while (*src && dstsz) {
+	while (srcsz && dstsz) {
 		signed char inputbuf[4] = { -1, -1, -1, -1 };
 		int parsed = 0, required = 1;
 
-		for (; *src && parsed < 4; parsed++) {
+		for (; srcsz && parsed < 4; parsed++) {
 			/* '=' is only allowed in last 2 characters. */
 			if ((*src == '=' && parsed <= 1) || !b64_isvalid(*src))
 				goto eilseq;
 			if (b64_isbase64(*src))
 				inputbuf[parsed] = b64_rlookup(*src);
 
-			src++;
+			++src;
+			--srcsz;
 		}
 
 		if (required >= dstsz)
--- a/base64.h	Wed Mar 25 16:54:36 2020 +0100
+++ b/base64.h	Thu Mar 26 08:41:12 2020 +0100
@@ -34,49 +34,6 @@
 #endif
 
 /**
- * \page Base64 Base64
- * \brief Base64 encoding and decoding.
- *
- * The C functions does not allocate memory by itself and therefore user must
- * repeat the calls to the functions until all data has been processed.
- *
- * ## Encoding
- *
- * You can encode like this:
- *
- * ```c
- * const char *data = "hello";
- * char tmpbuf[5];
- *
- * // Note: data will be incremented, use a temporary variable if you need to
- * // reuse it
- * while (b64_encode_step(&data, tmpbuf))
- * 	printf("%s", tmpbuf);
- *
- * printf("\n");
- * ```
- *
- * ## Decoding
- *
- * And you can decode like this:
- *
- * ```c
- * const char *data = "aGVsbG8=";
- * char tmpbuf[4];
- *
- * // Note: data will be incremented, use a temporary variable if you need to
- * // reuse it
- * while (b64_decode_step(&data, tmpbuf))
- * 	printf("%s", tmpbuf);
- *
- * printf("\n");
- *
- * if (errno != 0)
- * 	perror("invalid sequence");
- * ```
- */
-
-/**
  * Check if the character is a %base64 character, A-Za-z0-9 and +/.
  *
  * \param ch the character to test
@@ -156,16 +113,21 @@
  * This function will write at most dstsz bytes into the buffer, including the
  * NUL terminator.
  *
+ * If srcsz is 0 then strlen(src) is called to determine the number of bytes in
+ * src.  Note that binary data may contain embedded 0 and therefore specifying
+ * the source size would be required in that case.
+ *
  * \pre src != NULL
  * \pre dst != NULL
  * \param src the source to encode
+ * \param srcsz the source size or 0 to read until NUL terminator
  * \param dst the destination buffer
  * \param dstsz the destination size
  * \return The number of bytes written or -1 on error and sets errno.
  * \see \ref b64_encode_length
  */
 size_t
-b64_encode(const char src[], char dst[], size_t dstsz);
+b64_encode(const char src[], size_t srcsz, char dst[], size_t dstsz);
 
 /**
  * Decode the given src into the destination buffer.
@@ -173,16 +135,20 @@
  * This function will write at most dstsz bytes into the buffer, including the
  * NUL terminator.
  *
+ * If srcsz is 0 then strlen(src) is called to determine the number of bytes in
+ * src.
+ *
  * \pre src != NULL
  * \pre dst != NULL
  * \param src the source to encode
+ * \param srcsz the source size or 0 to read until NUL terminator
  * \param dst the destination buffer
  * \param dstsz the destination size
  * \return The number of bytes written or -1 on error and sets errno.
  * \see \ref b64_decode_length
  */
 size_t
-b64_decode(const char src[], char dst[], size_t dstsz);
+b64_decode(const char src[], size_t srcsz, char dst[], size_t dstsz);
 
 #if defined(__cplusplus)
 }
--- a/test/base64.c	Wed Mar 25 16:54:36 2020 +0100
+++ b/test/base64.c	Thu Mar 26 08:41:12 2020 +0100
@@ -175,31 +175,43 @@
 	char buffer[BUFSIZ];
 	size_t r;
 
-	r = b64_encode("a", buffer, sizeof (buffer));
+	r = b64_encode("a", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 4U);
 	GREATEST_ASSERT_STR_EQ(buffer, "YQ==");
-	r = b64_encode("ab", buffer, sizeof (buffer));
+	r = b64_encode("ab", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 4U);
 	GREATEST_ASSERT_STR_EQ(buffer, "YWI=");
-	r = b64_encode("abc", buffer, sizeof (buffer));
+	r = b64_encode("abc", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 4U);
 	GREATEST_ASSERT_STR_EQ(buffer, "YWJj");
-	r = b64_encode("hello", buffer, sizeof (buffer));
+	r = b64_encode("hello", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 8U);
 	GREATEST_ASSERT_STR_EQ(buffer, "aGVsbG8=");
-	r = b64_encode("this is a long sentence", buffer, sizeof (buffer));
+	r = b64_encode("this is a long sentence", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 32U);
 	GREATEST_ASSERT_STR_EQ(buffer, "dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=");
 	GREATEST_PASS();
 }
 
 GREATEST_TEST
+test_b64_encode_binary(void)
+{
+	char buffer[BUFSIZ];
+	size_t r;
+
+	r = b64_encode("a\0bc", 4, buffer, sizeof (buffer));
+	GREATEST_ASSERT_EQ(r, 8U);
+	GREATEST_ASSERT_STR_EQ(buffer, "YQBiYw==");
+	GREATEST_PASS();
+}
+
+GREATEST_TEST
 test_b64_encode_toosmall(void)
 {
 	char buffer[10];
 	size_t r;
 
-	r = b64_encode("hello world", buffer, sizeof (buffer));
+	r = b64_encode("hello world", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, ERANGE);
 	GREATEST_PASS();
@@ -208,6 +220,7 @@
 GREATEST_SUITE(suite_b64_encode)
 {
 	GREATEST_RUN_TEST(test_b64_encode_basic);
+	GREATEST_RUN_TEST(test_b64_encode_binary);
 	GREATEST_RUN_TEST(test_b64_encode_toosmall);
 }
 
@@ -265,49 +278,61 @@
 	char buffer[BUFSIZ];
 	size_t r;
 
-	r = b64_decode("YQ==", buffer, sizeof (buffer));
+	r = b64_decode("YQ==", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 1U);
 	GREATEST_ASSERT_STR_EQ(buffer, "a");
-	r = b64_decode("YWI=", buffer, sizeof (buffer));
+	r = b64_decode("YWI=", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 2U);
 	GREATEST_ASSERT_STR_EQ(buffer, "ab");
-	r = b64_decode("YWJj", buffer, sizeof (buffer));
+	r = b64_decode("YWJj", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 3U);
 	GREATEST_ASSERT_STR_EQ(buffer, "abc");
-	r = b64_decode("aGVsbG8=", buffer, sizeof (buffer));
+	r = b64_decode("aGVsbG8=", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 5U);
 	GREATEST_ASSERT_STR_EQ(buffer, "hello");
-	r = b64_decode("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=", buffer, sizeof (buffer));
+	r = b64_decode("dGhpcyBpcyBhIGxvbmcgc2VudGVuY2U=", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 23U);
 	GREATEST_ASSERT_STR_EQ(buffer, "this is a long sentence");
-	r = b64_decode("V2VsY29tZSB0byBvdXIgc2VydmVyIGR1ZGU=", buffer, sizeof (buffer));
+	r = b64_decode("V2VsY29tZSB0byBvdXIgc2VydmVyIGR1ZGU=", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, 26U);
 	GREATEST_ASSERT_STR_EQ(buffer, "Welcome to our server dude");
 	GREATEST_PASS();
 }
 
 GREATEST_TEST
+test_b64_decode_sized(void)
+{
+	char buffer[BUFSIZ];
+	size_t r;
+
+	r = b64_decode("YQ==     ", 4, buffer, sizeof (buffer));
+	GREATEST_ASSERT_EQ(r, 1U);
+	GREATEST_ASSERT_STR_EQ(buffer, "a");
+	GREATEST_PASS();
+}
+
+GREATEST_TEST
 test_b64_decode_invalid(void)
 {
 	char buffer[BUFSIZ];
 	size_t r;
 
-	r = b64_decode("YW=", buffer, sizeof (buffer));
+	r = b64_decode("YW=", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, EILSEQ);
-	r = b64_decode("?!", buffer, sizeof (buffer));
+	r = b64_decode("?!", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, EILSEQ);
-	r = b64_decode("=ABC", buffer, sizeof (buffer));
+	r = b64_decode("=ABC", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, EILSEQ);
-	r = b64_decode("A=BC", buffer, sizeof (buffer));
+	r = b64_decode("A=BC", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, EILSEQ);
-	r = b64_decode("==BC", buffer, sizeof (buffer));
+	r = b64_decode("==BC", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, EILSEQ);
-	r = b64_decode("AB=C", buffer, sizeof (buffer));
+	r = b64_decode("AB=C", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, EILSEQ);
 	GREATEST_PASS();
@@ -319,7 +344,7 @@
 	char buffer[10];
 	size_t r;
 
-	r = b64_decode("V2VsY29tZSB0byBvdXIgc2VydmVyIGR1ZGU=", buffer, sizeof (buffer));
+	r = b64_decode("V2VsY29tZSB0byBvdXIgc2VydmVyIGR1ZGU=", 0, buffer, sizeof (buffer));
 	GREATEST_ASSERT_EQ(r, (size_t)-1);
 	GREATEST_ASSERT_EQ(errno, ERANGE);
 	GREATEST_PASS();
@@ -328,6 +353,7 @@
 GREATEST_SUITE(suite_b64_decode)
 {
 	GREATEST_RUN_TEST(test_b64_decode_basic);
+	GREATEST_RUN_TEST(test_b64_decode_sized);
 	GREATEST_RUN_TEST(test_b64_decode_invalid);
 	GREATEST_RUN_TEST(test_b64_decode_toosmall);
 }

mercurial