Secret Key
High-Level Description of the Algorithm
KeyExpansion
– round keys are derived from the cipher key using the AES key schedule. AES requires a separate 128-bit key block for each round plus one more.- Initial round key addition:
AddRoundKey
– each byte of the state is combined with a byte of the round key using bitwise xor.
- 9, 11 or 13 rounds:
SubBytes
– a non-linear substitution step where each byte is replaced with another according to a lookup table.ShiftRows
– a transposition step where the last three rows of the state are shifted cyclically a certain number of steps.MixColumns
– a linear mixing operation which operates on the columns of the state, combining the four bytes in each column.AddRoundKey
- Final round (making 10, 12 or 14 rounds in total):
SubBytes
ShiftRows
AddRoundKey
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
- https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
- https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86
- https://blog.csdn.net/qq_28205153/article/details/55798628
- https://v5.getbootstrap.com/docs/5.0/getting-started/introduction/
- https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript