base64url.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // for embedded scripts, quoted and modified from https://github.com/swansontec/rfc4648.js by William Swanson
  2. 'use strict';
  3. var base64url = base64url || {};
  4. (function(base64url) {
  5. function parse (string, encoding, opts = {}) {
  6. // Build the character lookup table:
  7. if (!encoding.codes) {
  8. encoding.codes = {};
  9. for (let i = 0; i < encoding.chars.length; ++i) {
  10. encoding.codes[encoding.chars[i]] = i;
  11. }
  12. }
  13. // The string must have a whole number of bytes:
  14. if (!opts.loose && (string.length * encoding.bits) & 7) {
  15. throw new SyntaxError('Invalid padding');
  16. }
  17. // Count the padding bytes:
  18. let end = string.length;
  19. while (string[end - 1] === '=') {
  20. --end;
  21. // If we get a whole number of bytes, there is too much padding:
  22. if (!opts.loose && !(((string.length - end) * encoding.bits) & 7)) {
  23. throw new SyntaxError('Invalid padding');
  24. }
  25. }
  26. // Allocate the output:
  27. const out = new (opts.out || Uint8Array)(((end * encoding.bits) / 8) | 0);
  28. // Parse the data:
  29. let bits = 0; // Number of bits currently in the buffer
  30. let buffer = 0; // Bits waiting to be written out, MSB first
  31. let written = 0; // Next byte to write
  32. for (let i = 0; i < end; ++i) {
  33. // Read one character from the string:
  34. const value = encoding.codes[string[i]];
  35. if (value === void 0) {
  36. throw new SyntaxError('Invalid character ' + string[i]);
  37. }
  38. // Append the bits to the buffer:
  39. buffer = (buffer << encoding.bits) | value;
  40. bits += encoding.bits;
  41. // Write out some bits if the buffer has a byte's worth:
  42. if (bits >= 8) {
  43. bits -= 8;
  44. out[written++] = 0xff & (buffer >> bits);
  45. }
  46. }
  47. // Verify that we have received just enough bits:
  48. if (bits >= encoding.bits || 0xff & (buffer << (8 - bits))) {
  49. throw new SyntaxError('Unexpected end of data');
  50. }
  51. return out
  52. }
  53. function stringify (data, encoding, opts = {}) {
  54. const { pad = true } = opts;
  55. const mask = (1 << encoding.bits) - 1;
  56. let out = '';
  57. let bits = 0; // Number of bits currently in the buffer
  58. let buffer = 0; // Bits waiting to be written out, MSB first
  59. for (let i = 0; i < data.length; ++i) {
  60. // Slurp data into the buffer:
  61. buffer = (buffer << 8) | (0xff & data[i]);
  62. bits += 8;
  63. // Write out as much as we can:
  64. while (bits > encoding.bits) {
  65. bits -= encoding.bits;
  66. out += encoding.chars[mask & (buffer >> bits)];
  67. }
  68. }
  69. // Partial character:
  70. if (bits) {
  71. out += encoding.chars[mask & (buffer << (encoding.bits - bits))];
  72. }
  73. // Add padding characters until we hit a byte boundary:
  74. if (pad) {
  75. while ((out.length * encoding.bits) & 7) {
  76. out += '=';
  77. }
  78. }
  79. return out
  80. }
  81. const encoding = {
  82. chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
  83. bits: 6
  84. }
  85. base64url.decode = function (string, opts) {
  86. return parse(string, encoding, opts);
  87. }
  88. base64url.encode = function (data, opts) {
  89. return stringify(data, encoding, opts)
  90. }
  91. return base64url;
  92. }(base64url));