Base64 encode explained
Published on 20 January 2020 at 16:22 by
Originally posted on May 2nd, 2016 (more info)
Part 1 – base64 encode explained Part 2 – base64 decode explained
I've used base64 a lot but never have I delved into it enough to understand exactly what goes on. So I took the time to explain via inline comments. I hope you enjoy reading it as much as I enjoyed writing it.
String.prototype.toBase64 = function () {
const base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef' +
'ghijklmnopqrstuvwxyz0123456789+/';
let plaintext = this,
result = '',
padding = '';
// step 1. ensure that the string is a multiple of three chars
if (plaintext.length % 3 > 0) {
for(let count = 0; count < 3; count++) {
padding += '=';
plaintext += '\0';
}
}
// step 2. iterate over the input string, three chars at a time
for (let i = 0; i < plaintext.length; i += 3) {
// step 3. take three 8-bit (ASCII) chars, and store
// them as one single 24-bit number
//
// 00000000 8-bit (1 byte)
// 00000000 00000000 00000000 24-bit (3 bytes)
//
// we'll bitshift the first number two bytes (16 bits)
// we'll bitshift the second number one byte (8 bits)
// and we'll pop the other number into the third byte
//
// 00000000 <------- -------- first char << 16
// 00000000 <------- second char << 8
// 00000000 third char (no shift)
const a = plaintext.charCodeAt(i) << 16;
const b = plaintext.charCodeAt(i + 1) << 8;
const c = plaintext.charCodeAt(i + 2);
const n = a + b + c;
// step 4. separate this 24-bit number into four 6-bit numbers
//
// we'll do this by shifting to the right (with zero pad >>>)
// and then doing a logical AND (&) to strip everything after
// the first (right most) six bits
//
// e.g. 00001111 00000101 00001010
// to |----||- ---||--- -||----|
// d e f g
//
// >>> 18 00000000 00000000 00000011
// & 63 00000000 00000000 00111111
// d = 00000000 00000000 00000011
//
// >>> 12 00000000 00000000 11110000
// & 63 00000000 00000000 00111111
// e = 00000000 00000000 00110000
//
// >>> 6 00000000 00111100 00010100
// & 63 00000000 00000000 00111111
// f = 00000000 00000000 00010100
//
// noop 00001111 00000101 00001010
// & 63 00000000 00000000 00111111
// g = 00000000 00000000 00001010
const d = (n >>> 18) & 63;
const e = (n >>> 12) & 63;
const f = (n >>> 6) & 63;
const g = n & 63;
// step 5. use the four 6-bit numbers as indices to pluck
// chars from our char array above, and add to the
// result string, before continuing with the loop
//
// this works because a 6-bit number is 0-63, and we have
// 64 characters above. Therefore, we can pluck out chars
result += base64chars[d];
result += base64chars[e];
result += base64chars[f];
result += base64chars[g];
}
// step 6. finally, we'll remove the zero pad we added above
// and add the actual padding, then return this string
return result.substring(0, result.length - padding.length) + padding;
};