b64encode.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #include <stdint.h>
  2. #include <string.h>
  3. #include "b64encode.h"
  4. static char b64chars[] =
  5. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  6. /**
  7. * b64encode(in, out, len):
  8. * Convert ${len} bytes from ${in} into RFC 1421 base-64 encoding, writing
  9. * the resulting ((len + 2) / 3) * 4 bytes to ${out}; and append a NUL byte.
  10. */
  11. void
  12. b64encode(const uint8_t * in, char * out, size_t len)
  13. {
  14. uint32_t t;
  15. size_t j;
  16. /* Repeat {read up to 3 bytes; write 4 bytes} until we're done. */
  17. while (len) {
  18. /* Read up to 3 bytes. */
  19. for (t = 0, j = 0; j < 3; j++) {
  20. t <<= 8;
  21. if (j < len)
  22. t += *in++;
  23. }
  24. /* Write 4 bytes. */
  25. for (j = 0; j < 4; j++) {
  26. if (j <= len)
  27. *out++ = b64chars[(t >> 18) & 0x3f];
  28. else
  29. *out++ = '=';
  30. t <<= 6;
  31. }
  32. /* Adjust the remaining length. */
  33. if (len < 3)
  34. len = 0;
  35. else
  36. len -= 3;
  37. }
  38. /* NUL terminate. */
  39. *out++ = '\0';
  40. }
  41. /**
  42. * b64decode(in, inlen, out, outlen):
  43. * Convert ${inlen} bytes of RFC 1421 base-64 encoding from ${in}, writing
  44. * the resulting bytes to ${out}; and pass the number of bytes output back
  45. * via ${outlen}. The buffer ${out} must contain at least (inlen/4)*3 bytes
  46. * of space; but ${outlen} might be less than this. Return non-zero if the
  47. * input ${in} is not valid base-64 encoded text.
  48. */
  49. int
  50. b64decode(const char * in, size_t inlen, uint8_t * out, size_t * outlen)
  51. {
  52. uint32_t t;
  53. ptrdiff_t pos;
  54. size_t deadbytes = 0;
  55. size_t i;
  56. /* We must have a multiple of 4 input bytes. */
  57. if (inlen % 4 != 0)
  58. goto bad;
  59. /* Check that we have valid input and count trailing '='s. */
  60. for (i = 0; i < inlen; i++) {
  61. /* Must be characters from our b64 character set. */
  62. if ((in[i] == '\0') || (strchr(b64chars, in[i]) == NULL))
  63. goto bad;
  64. /* Is this a '=' character? */
  65. if (in[i] == '=')
  66. deadbytes += 1;
  67. /* No non-'=' bytes after a '=' byte. */
  68. if ((in[i] != '=') && (deadbytes > 0))
  69. goto bad;
  70. }
  71. /* Can't have more than 2 trailing '=' bytes. */
  72. if (deadbytes > 2)
  73. goto bad;
  74. /* We have no output yet. */
  75. *outlen = 0;
  76. /* Loop until we run out of data. */
  77. while (inlen) {
  78. /* Parse 4 bytes. */
  79. for (t = 0, i = 0; i < 4; i++) {
  80. t <<= 6;
  81. pos = strchr(b64chars, in[i]) - b64chars;
  82. t += (uint32_t)(pos & 0x3f);
  83. }
  84. /* Output 3 bytes. */
  85. for (i = 0; i < 3; i++) {
  86. out[i] = (t >> 16) & 0xff;
  87. t <<= 8;
  88. }
  89. /* Adjust pointers and lengths for the completed block. */
  90. in += 4;
  91. inlen -= 4;
  92. out += 3;
  93. *outlen += 3;
  94. }
  95. /* Ignore dead bytes. */
  96. *outlen -= deadbytes;
  97. /* Success! */
  98. return (0);
  99. bad:
  100. /* The input is not valid base-64 encoded text. */
  101. return (1);
  102. }