base64.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * base64.c - Base64 with support for multiple variants
  4. *
  5. * Copyright (c) 2020 Hannes Reinecke, SUSE
  6. *
  7. * Based on the base64url routines from fs/crypto/fname.c
  8. * (which are using the URL-safe Base64 encoding),
  9. * modified to support multiple Base64 variants.
  10. */
  11. #include <linux/kernel.h>
  12. #include <linux/types.h>
  13. #include <linux/export.h>
  14. #include <linux/string.h>
  15. #include <linux/base64.h>
  16. static const char base64_tables[][65] = {
  17. [BASE64_STD] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
  18. [BASE64_URLSAFE] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
  19. [BASE64_IMAP] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,",
  20. };
  21. /*
  22. * Initialize the base64 reverse mapping for a single character
  23. * This macro maps a character to its corresponding base64 value,
  24. * returning -1 if the character is invalid.
  25. * char 'A'-'Z' maps to 0-25, 'a'-'z' maps to 26-51, '0'-'9' maps to 52-61,
  26. * ch_62 maps to 62, ch_63 maps to 63, and other characters return -1
  27. */
  28. #define INIT_1(v, ch_62, ch_63) \
  29. [v] = (v) >= 'A' && (v) <= 'Z' ? (v) - 'A' \
  30. : (v) >= 'a' && (v) <= 'z' ? (v) - 'a' + 26 \
  31. : (v) >= '0' && (v) <= '9' ? (v) - '0' + 52 \
  32. : (v) == (ch_62) ? 62 : (v) == (ch_63) ? 63 : -1
  33. /*
  34. * Recursive macros to generate multiple Base64 reverse mapping table entries.
  35. * Each macro generates a sequence of entries in the lookup table:
  36. * INIT_2 generates 2 entries, INIT_4 generates 4, INIT_8 generates 8, and so on up to INIT_32.
  37. */
  38. #define INIT_2(v, ...) INIT_1(v, __VA_ARGS__), INIT_1((v) + 1, __VA_ARGS__)
  39. #define INIT_4(v, ...) INIT_2(v, __VA_ARGS__), INIT_2((v) + 2, __VA_ARGS__)
  40. #define INIT_8(v, ...) INIT_4(v, __VA_ARGS__), INIT_4((v) + 4, __VA_ARGS__)
  41. #define INIT_16(v, ...) INIT_8(v, __VA_ARGS__), INIT_8((v) + 8, __VA_ARGS__)
  42. #define INIT_32(v, ...) INIT_16(v, __VA_ARGS__), INIT_16((v) + 16, __VA_ARGS__)
  43. #define BASE64_REV_INIT(ch_62, ch_63) { \
  44. [0 ... 0x1f] = -1, \
  45. INIT_32(0x20, ch_62, ch_63), \
  46. INIT_32(0x40, ch_62, ch_63), \
  47. INIT_32(0x60, ch_62, ch_63), \
  48. [0x80 ... 0xff] = -1 }
  49. static const s8 base64_rev_maps[][256] = {
  50. [BASE64_STD] = BASE64_REV_INIT('+', '/'),
  51. [BASE64_URLSAFE] = BASE64_REV_INIT('-', '_'),
  52. [BASE64_IMAP] = BASE64_REV_INIT('+', ',')
  53. };
  54. #undef BASE64_REV_INIT
  55. #undef INIT_32
  56. #undef INIT_16
  57. #undef INIT_8
  58. #undef INIT_4
  59. #undef INIT_2
  60. #undef INIT_1
  61. /**
  62. * base64_encode() - Base64-encode some binary data
  63. * @src: the binary data to encode
  64. * @srclen: the length of @src in bytes
  65. * @dst: (output) the Base64-encoded string. Not NUL-terminated.
  66. * @padding: whether to append '=' padding characters
  67. * @variant: which base64 variant to use
  68. *
  69. * Encodes data using the selected Base64 variant.
  70. *
  71. * Return: the length of the resulting Base64-encoded string in bytes.
  72. */
  73. int base64_encode(const u8 *src, int srclen, char *dst, bool padding, enum base64_variant variant)
  74. {
  75. u32 ac = 0;
  76. char *cp = dst;
  77. const char *base64_table = base64_tables[variant];
  78. while (srclen >= 3) {
  79. ac = src[0] << 16 | src[1] << 8 | src[2];
  80. *cp++ = base64_table[ac >> 18];
  81. *cp++ = base64_table[(ac >> 12) & 0x3f];
  82. *cp++ = base64_table[(ac >> 6) & 0x3f];
  83. *cp++ = base64_table[ac & 0x3f];
  84. src += 3;
  85. srclen -= 3;
  86. }
  87. switch (srclen) {
  88. case 2:
  89. ac = src[0] << 16 | src[1] << 8;
  90. *cp++ = base64_table[ac >> 18];
  91. *cp++ = base64_table[(ac >> 12) & 0x3f];
  92. *cp++ = base64_table[(ac >> 6) & 0x3f];
  93. if (padding)
  94. *cp++ = '=';
  95. break;
  96. case 1:
  97. ac = src[0] << 16;
  98. *cp++ = base64_table[ac >> 18];
  99. *cp++ = base64_table[(ac >> 12) & 0x3f];
  100. if (padding) {
  101. *cp++ = '=';
  102. *cp++ = '=';
  103. }
  104. break;
  105. }
  106. return cp - dst;
  107. }
  108. EXPORT_SYMBOL_GPL(base64_encode);
  109. /**
  110. * base64_decode() - Base64-decode a string
  111. * @src: the string to decode. Doesn't need to be NUL-terminated.
  112. * @srclen: the length of @src in bytes
  113. * @dst: (output) the decoded binary data
  114. * @padding: whether to append '=' padding characters
  115. * @variant: which base64 variant to use
  116. *
  117. * Decodes a string using the selected Base64 variant.
  118. *
  119. * Return: the length of the resulting decoded binary data in bytes,
  120. * or -1 if the string isn't a valid Base64 string.
  121. */
  122. int base64_decode(const char *src, int srclen, u8 *dst, bool padding, enum base64_variant variant)
  123. {
  124. u8 *bp = dst;
  125. s8 input[4];
  126. s32 val;
  127. const u8 *s = (const u8 *)src;
  128. const s8 *base64_rev_tables = base64_rev_maps[variant];
  129. while (srclen >= 4) {
  130. input[0] = base64_rev_tables[s[0]];
  131. input[1] = base64_rev_tables[s[1]];
  132. input[2] = base64_rev_tables[s[2]];
  133. input[3] = base64_rev_tables[s[3]];
  134. val = input[0] << 18 | input[1] << 12 | input[2] << 6 | input[3];
  135. if (unlikely(val < 0)) {
  136. if (!padding || srclen != 4 || s[3] != '=')
  137. return -1;
  138. padding = 0;
  139. srclen = s[2] == '=' ? 2 : 3;
  140. break;
  141. }
  142. *bp++ = val >> 16;
  143. *bp++ = val >> 8;
  144. *bp++ = val;
  145. s += 4;
  146. srclen -= 4;
  147. }
  148. if (likely(!srclen))
  149. return bp - dst;
  150. if (padding || srclen == 1)
  151. return -1;
  152. val = (base64_rev_tables[s[0]] << 12) | (base64_rev_tables[s[1]] << 6);
  153. *bp++ = val >> 10;
  154. if (srclen == 2) {
  155. if (val & 0x800003ff)
  156. return -1;
  157. } else {
  158. val |= base64_rev_tables[s[2]];
  159. if (val & 0x80000003)
  160. return -1;
  161. *bp++ = val >> 2;
  162. }
  163. return bp - dst;
  164. }
  165. EXPORT_SYMBOL_GPL(base64_decode);