aboutsummaryrefslogtreecommitdiffstats
path: root/librecrypt_encode.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-04-26 22:36:47 +0200
committerMattias Andrée <m@maandree.se>2026-04-26 22:36:47 +0200
commitd77ab463184d113ca6119403887c9f4ed0dfdf0b (patch)
treeca8a1de443f90a4b7def56ea5b61c96aaa949f45 /librecrypt_encode.c
downloadlibrecrypt-d77ab463184d113ca6119403887c9f4ed0dfdf0b.tar.gz
librecrypt-d77ab463184d113ca6119403887c9f4ed0dfdf0b.tar.bz2
librecrypt-d77ab463184d113ca6119403887c9f4ed0dfdf0b.tar.xz
First commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'librecrypt_encode.c')
-rw-r--r--librecrypt_encode.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/librecrypt_encode.c b/librecrypt_encode.c
new file mode 100644
index 0000000..2199ac1
--- /dev/null
+++ b/librecrypt_encode.c
@@ -0,0 +1,177 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+#if defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif
+
+
+#define O1(I1, I2, I3) ((I1) >> 2)
+#define O2(I1, I2, I3) (((I1) << 4) | ((I2) >> 4))
+#define O3(I1, I2, I3) (((I2) << 2) | ((I3) >> 6))
+#define O4(I1, I2, I3) (I3)
+
+
+size_t
+librecrypt_encode(char *out_buffer, size_t size, const void *binary, size_t len,
+ const char lut[restrict static 256], char pad)
+{
+ const unsigned char *data = binary;
+ unsigned char a, b, c;
+ size_t q, r, i, j, n;
+
+ q = len / 3u;
+ r = len % 3u;
+ n = q * 4u + (!r ? 0u : pad ? 4u : r + 1u);
+
+ if (!size)
+ return n;
+
+ size -= 1u;
+ out_buffer[n < size ? n : size] = '\0';
+
+ if (r == 1u) {
+ a = data[q * 3u + 0u];
+ switch (size > q * 4u ? size - q * 4u : 0u) { /* fall-through */
+ default:
+ if (pad)
+ out_buffer[q * 4u + 3u] = pad;
+ case 3u:
+ if (pad)
+ out_buffer[q * 4u + 2u] = pad;
+ case 2u:
+ out_buffer[q * 4u + 1u] = lut[O2(a, 0, 0) & 255];
+ case 1u:
+ out_buffer[q * 4u + 0u] = lut[O1(a, 0, 0) & 255];
+ case 0u:
+ break;
+ }
+ } else if (r == 2u) {
+ a = data[q * 3u + 0u];
+ b = data[q * 3u + 1u];
+ switch (size > q * 4u ? size - q * 4u : 0u) { /* fall-through */
+ default:
+ if (pad)
+ out_buffer[q * 4u + 3u] = pad;
+ case 3u:
+ out_buffer[q * 4u + 2u] = lut[O3(a, b, 0) & 255];
+ case 2u:
+ out_buffer[q * 4u + 1u] = lut[O2(a, b, 0) & 255];
+ case 1u:
+ out_buffer[q * 4u + 0u] = lut[O1(a, b, 0) & 255];
+ case 0u:
+ break;
+ }
+ }
+
+ i = q * 4u;
+ j = q * 3u;
+ if (i > size) {
+ q = size / 4u;
+ r = size % 4u;
+ i = q * 4u;
+ j = q * 3u;
+ c = data[j + 2u];
+ b = data[j + 1u];
+ a = data[j + 0u];
+ switch (r) { /* fall-through */
+ case 3u:
+ out_buffer[i + 2u] = lut[O3(a, b, c) & 255];
+ case 2u:
+ out_buffer[i + 1u] = lut[O2(a, b, c) & 255];
+ case 1u:
+ out_buffer[i + 0u] = lut[O1(a, b, c) & 255];
+ default:
+ break;
+ }
+ }
+ while (i) {
+ i -= 4u;
+ j -= 3u;
+ c = data[j + 2u];
+ b = data[j + 1u];
+ a = data[j + 0u];
+ out_buffer[i + 3u] = lut[O4(a, b, c) & 255];
+ out_buffer[i + 2u] = lut[O3(a, b, c) & 255];
+ out_buffer[i + 1u] = lut[O2(a, b, c) & 255];
+ out_buffer[i + 0u] = lut[O1(a, b, c) & 255];
+ }
+
+ return n;
+}
+
+
+#else
+
+
+NONSTRING static const char lut[256u] = MAKE_ENCODING_LUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+
+
+#define CHECK(BINARY, ASCII)\
+ check((BINARY), sizeof(BINARY) - 1u, (ASCII), sizeof(ASCII) - 1u)
+
+
+static void
+check(const char *binary, size_t binary_len, const char *ascii, size_t ascii_len)
+{
+ size_t padded_ascii_len = ascii_len;
+ char buf[256u];
+ size_t i, j, n;
+
+ if (padded_ascii_len & 3u) {
+ padded_ascii_len |= 3u;
+ padded_ascii_len += 1u;
+ }
+ assert(padded_ascii_len % 4u == 0u);
+ assert(padded_ascii_len / 4u == (ascii_len + 3u) / 4u);
+ assert(padded_ascii_len >= ascii_len);
+
+ EXPECT(librecrypt_encode(NULL, 0u, binary, binary_len, lut, '\0') == ascii_len);
+ EXPECT(librecrypt_encode(buf, 0u, binary, binary_len, lut, '\0') == ascii_len);
+
+ EXPECT(librecrypt_encode(NULL, 0u, binary, binary_len, lut, '=') == padded_ascii_len);
+ EXPECT(librecrypt_encode(buf, 0u, binary, binary_len, lut, '=') == padded_ascii_len);
+
+ for (i = 0u; i <= ascii_len; i++) {
+ memset(buf, 99, sizeof(buf));
+ EXPECT(librecrypt_encode(buf, i + 1u, binary, binary_len, lut, '\0') == ascii_len);
+ EXPECT(!memcmp(buf, ascii, i));
+ EXPECT(buf[i] == '\0');
+ for (j = i + 1u; j < sizeof(buf); j++)
+ EXPECT(buf[j] == 99);
+ }
+
+ for (i = 0u; i <= padded_ascii_len; i++) {
+ memset(buf, 99, sizeof(buf));
+ EXPECT(librecrypt_encode(buf, i + 1u, binary, binary_len, lut, '=') == padded_ascii_len);
+ j = i < ascii_len ? i : ascii_len;
+ n = i < padded_ascii_len ? i : padded_ascii_len;
+ EXPECT(!memcmp(buf, ascii, j));
+ for (; j < n; j++)
+ EXPECT(buf[j] == '=');
+ EXPECT(buf[j++] == '\0');
+ for (; j < sizeof(buf); j++)
+ EXPECT(buf[j] == 99);
+ }
+}
+
+
+int
+main(void)
+{
+ SET_UP_ALARM();
+ CHECK("", "");
+ CHECK("\x00", "AA");
+ CHECK("\x00\x00", "AAA");
+ CHECK("\x00\x00\x00", "AAAA");
+ CHECK("12345678", "MTIzNDU2Nzg");
+ CHECK("testtest", "dGVzdHRlc3Q");
+ CHECK("zy[]y21 !", "enlbXXkyMSAh");
+ CHECK("{~|~}~~~\x7f\x7f", "e358fn1+fn5/fw");
+ return 0;
+}
+
+
+#endif