diff options
| author | Mattias Andrée <m@maandree.se> | 2026-04-26 22:36:47 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-04-26 22:36:47 +0200 |
| commit | d77ab463184d113ca6119403887c9f4ed0dfdf0b (patch) | |
| tree | ca8a1de443f90a4b7def56ea5b61c96aaa949f45 /librecrypt_encode.c | |
| download | librecrypt-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 '')
| -rw-r--r-- | librecrypt_encode.c | 177 |
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 |
