Secret Key

High-Level Description of the Algorithm

JavaScript Source Code


/* Helper Functions   < 1-49 > */

function AsciiToHexStr(a) {
   let h = []
   for (let i = 0; i < a.length; i++) {
      h.push(parseInt(a.charCodeAt(i), 10).toString(16).toUpperCase())
   }
   return h
}

function HexStrToAscii(h) {
   let a = ""
   for (let i = 0; (i < h.length && h.substr(i, 2) !== '00'); i += 2) {
      a += String.fromCharCode(parseInt(h.substr(i, 2), 16))
   }
   return a;
}

function DecIntToHexStr(i) {
   let s = parseInt(i).toString(16).toUpperCase()
   while (s.length < 2) {
      s = "0" + s
   }
   return s
}

function HexStrToDecInt(s) {
   return parseInt(s, 16)
}

function ArrayToString(list) {
   return list.toString().replace(/,/g, '')
}

function StringToArray(string) {
   string = cipherTextPadding(string)

   let list = []
   for (let i = 0; i < string.length; i += 2) {
      list.push(string.charAt(i) + string.charAt(i + 1))
   }
   return list
}

function getRandomInt(min, max) {
   min = Math.ceil(min)
   max = Math.floor(max)
   return Math.floor(Math.random() * (max - min + 1)) + min
}


/* Formatting Functions  < 52-98 > */

function plainTextPadding(plain) {
   plain = AsciiToHexStr(plain)

   while (plain.length % 16) {
      plain.push("00")
   }
   return plain
}

function cipherTextPadding(cipher) {
   if (cipher.length % 32) {
      console.log("CIPHER TEXT IS NOT VALID")
   }

   while (cipher.length % 32) {
      cipher += "0"
   }
   return cipher
}

function keyPadding() {
   let element = document.getElementById('key')
   console.log("%cPADDING: " + element.value, '')

   let key = element.value.replace(/[^\dA-F]/g, '')
   while (key.length < 32) {
      key = "0" + key
   }
   element.value = key
   keyFormatting()
   return element.value
}

function keyFormatting() {
   let element = document.getElementById('key')
   element.value = element.value.toUpperCase()
      .replace(/\s/g, '')
      .replace(/[^\dA-F]/g, '')
      .replace(/([\dA-F]{2})(?=[\dA-F])/g, '$1 ')
      .substring(0, 47)

   if (element.value.length === 47) {
      console.log("%cKEY: " + element.value, 'font-weight: bold;')
   }
}


/* AES Operations  < 101-301 > */

// the KeyExpansion step   < 103-174 >
const H = {
   1: ["01", "00", "00", "00"],
   2: ["02", "00", "00", "00"],
   3: ["04", "00", "00", "00"],
   4: ["08", "00", "00", "00"],
   5: ["10", "00", "00", "00"],
   6: ["20", "00", "00", "00"],
   7: ["40", "00", "00", "00"],
   8: ["80", "00", "00", "00"],
   9: ["1B", "00", "00", "00"],
   10: ["36", "00", "00", "00"]
}

function keyGenerate() {
   console.log("%cGENERATE", 'font-size: medium; font-weight: bold;')
   const map = "0123456789ABCDEF"

   let key = ""
   for (let i = 0; i < 32; i++) {
      let x = getRandomInt(0, 15)
      key += map.charAt(x)
   }

   console.log(key + " (" + key.length + ")")
   document.getElementById('key').value = key
   keyFormatting()
}

function keyExpansion(key) {
   function xor(a, b) {
      let c = a
      for (let i = 0; i < c.length; i++) {
         let x = HexStrToDecInt(a[i])
         let y = HexStrToDecInt(b[i])
         c[i] = DecIntToHexStr(x ^ y)
      }
      return c
   }

   function t(w, i) {
      w = [w[1], w[2], w[3], w[0]]

      for (let i = 0; i < w.length; i++) {
         let x = HexStrToDecInt(w[i].charAt(0))
         let y = HexStrToDecInt(w[i].charAt(1))
         w[i] = DecIntToHexStr(S[x][y])
      }

      return xor(w, H[i])
   }

   let keys = [key]
   for (let i = 1; i <= 10; i++) {
      let k = keys[keys.length - 1]

      let w0 = k.slice(0, 4)
      let w1 = k.slice(4, 8)
      let w2 = k.slice(8, 12)
      let w3 = k.slice(12, 16)

      let w4 = xor(w0, t(w3, i))
      let w5 = xor(w1, w4)
      let w6 = xor(w2, w5)
      let w7 = xor(w3, w6)

      let w = [].concat(w4, w5, w6, w7)
      keys.push(w)
   }

   return keys
}

// the SubBytes step   < 176-222 >
const S = [
   [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76],
   [0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0],
   [0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15],
   [0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75],
   [0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84],
   [0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf],
   [0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8],
   [0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2],
   [0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73],
   [0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb],
   [0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79],
   [0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08],
   [0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a],
   [0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e],
   [0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf],
   [0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]
]

const T = [
   [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb],
   [0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb],
   [0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e],
   [0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25],
   [0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92],
   [0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84],
   [0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06],
   [0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b],
   [0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73],
   [0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e],
   [0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b],
   [0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4],
   [0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f],
   [0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef],
   [0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61],
   [0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d]
]

function subBytes(list, table) {
   for (let i = 0; i < list.length; i++) {
      let x = HexStrToDecInt(list[i].charAt(0))
      let y = HexStrToDecInt(list[i].charAt(1))
      list[i] = DecIntToHexStr(table[x][y])
   }
   return list
}

// the ShiftRows step   < 224-242 >
function shiftRows(list, direction) {
   if (direction === 1) {
      list = [
         list[0], list[5], list[10], list[15],
         list[4], list[9], list[14], list[3],
         list[8], list[13], list[2], list[7],
         list[12], list[1], list[6], list[11],
      ]
   } else {
      list = [
         list[0], list[13], list[10], list[7],
         list[4], list[1], list[14], list[11],
         list[8], list[5], list[2], list[15],
         list[12], list[9], list[6], list[3],
      ]
   }
   return list
}

// the MixColumns step   < 244-293 >
function mixColumns(list, direction) {
   function multiply(i, n) {
      if (n === 1) {
         return i
      } else if (n === 2) {
         let j = i * n

         if (j > 255) {
            j = (j % 256) ^ 0x1B
         }
         return j
      }

      if (n % 2) {
         return multiply(i, 1) ^ multiply(i, n - 1)
      } else {
         return multiply(multiply(i, n / 2), 2)
      }
   }

   for (let i = 0; i < list.length; i++) {
      list[i] = HexStrToDecInt(list[i])
   }

   if (direction === 1) {
      for (let i = 0; i < list.length; i += 4) {
         let a = list[i], b = list[i + 1], c = list[i + 2], d = list[i + 3]

         list[i] = multiply(a, 2) ^ multiply(b, 3) ^ multiply(c, 1) ^ multiply(d, 1)
         list[i + 1] = multiply(a, 1) ^ multiply(b, 2) ^ multiply(c, 3) ^ multiply(d, 1)
         list[i + 2] = multiply(a, 1) ^ multiply(b, 1) ^ multiply(c, 2) ^ multiply(d, 3)
         list[i + 3] = multiply(a, 3) ^ multiply(b, 1) ^ multiply(c, 1) ^ multiply(d, 2)
      }
   } else {
      for (let i = 0; i < list.length; i += 4) {
         let a = list[i], b = list[i + 1], c = list[i + 2], d = list[i + 3]

         list[i] = multiply(a, 0xe) ^ multiply(b, 0xb) ^ multiply(c, 0xd) ^ multiply(d, 0x9)
         list[i + 1] = multiply(a, 0x9) ^ multiply(b, 0xe) ^ multiply(c, 0xb) ^ multiply(d, 0xd)
         list[i + 2] = multiply(a, 0xd) ^ multiply(b, 0x9) ^ multiply(c, 0xe) ^ multiply(d, 0xb)
         list[i + 3] = multiply(a, 0xb) ^ multiply(b, 0xd) ^ multiply(c, 0x9) ^ multiply(d, 0xe)
      }
   }

   for (let i = 0; i < list.length; i++) {
      list[i] = DecIntToHexStr(list[i])
   }
   return list
}

// the AddRoundKey step   < 296-301 >
function addRoundKey(list, key) {
   for (let i = 0; i < list.length; i++) {
      list[i] = DecIntToHexStr(HexStrToDecInt(list[i]) ^ HexStrToDecInt(key[i]))
   }
   return list
}


/* AES 128   < 304-338 > */

function aesEncrypt(text, keys) {
   text = addRoundKey(text, keys[0])

   for (let i = 1; i <= 9; i++) {
      text = subBytes(text, S)
      text = shiftRows(text, 1)
      text = mixColumns(text, 1)
      text = addRoundKey(text, keys[i])
   }

   text = subBytes(text, S)
   text = shiftRows(text, 1)
   text = addRoundKey(text, keys[10])

   return text
}

function aesDecrypt(text, keys) {
   text = addRoundKey(text, keys[10])

   for (let i = 9; i >= 1; i--) {
      text = shiftRows(text, -1)
      text = subBytes(text, T)
      text = addRoundKey(text, keys[i])
      text = mixColumns(text, -1)
   }

   text = shiftRows(text, -1)
   text = subBytes(text, T)
   text = addRoundKey(text, keys[0])

   return text
}


/* Event Handling   < 341-409 > */

document.addEventListener("DOMContentLoaded",
   function () {

      function textEncrypt() {
         console.log("%cENCRYPT", 'font-size: medium; font-weight: bold;')

         let keys = keyExpansion(keyPadding().split(' '))
         console.log(keys)

         let plain = document.getElementById('PlainText').value
         let cipher = []

         console.log("%cPLAIN TEXT: " + plain, 'font-weight: bold;')
         plain = plainTextPadding(plain)
         console.log(plain)

         for (let i = 0; i < plain.length; i += 16) {
            let c = aesEncrypt(plain.slice(i, i + 16), keys)
            console.log((i + 1) + "-" + (i + 16) + ": " + ArrayToString(c))
            cipher = cipher.concat(c)
         }

         console.log("%cCIPHER TEXT: " + ArrayToString(cipher), 'font-weight: bold;')
         console.log(cipher)

         document.getElementById('CipherText').value = ArrayToString(cipher)
      }

      function textDecrypt() {
         console.log("%cDECRYPT", 'font-size: medium; font-weight: bold;')

         let keys = keyExpansion(keyPadding().split(' '))
         console.log(keys)

         let cipher = document.getElementById('CipherText').value
         let plain = []

         console.log("%cCIPHER TEXT: " + cipher, 'font-weight: bold;')
         cipher = StringToArray(cipher)
         console.log(cipher)

         for (let i = 0; i < cipher.length; i += 16) {
            let p = aesDecrypt(cipher.slice(i, i + 16), keys)
            console.log((i + 1) + "-" + (i + 16) + ": " + ArrayToString(p))
            plain = plain.concat(p)
         }

         console.log("%cPLAIN TEXT: " + HexStrToAscii(ArrayToString(plain)), 'font-weight: bold;')
         console.log(plain)

         document.getElementById('PlainText').value = HexStrToAscii(ArrayToString(plain))
      }

      // Unobtrusive event binding
      document.querySelector("#encrypt")
         .addEventListener("click", textEncrypt)

      document.querySelector("#decrypt")
         .addEventListener("click", textDecrypt)

      document.querySelector("#generate")
         .addEventListener("click", keyGenerate)

      document.querySelector("#key")
         .addEventListener("input", keyFormatting)
   }
)

Reference