Skip to content

Instantly share code, notes, and snippets.

@klustic
Last active August 13, 2021 06:14
Show Gist options
  • Save klustic/06659a8d9fdfd34babacd9d98201e015 to your computer and use it in GitHub Desktop.
Save klustic/06659a8d9fdfd34babacd9d98201e015 to your computer and use it in GitHub Desktop.
ServiceNow script include that implements AES in ECB and CFB modes
/*
License:
ServiceNow script include that implements AES in ECB and CFB modes.
Copyright (C) 2021 K. Lustic
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Adapted from:
http://point-at-infinity.org/jsaes/jsaes.js
Public Methods:
AES(key) - class constructor. The key is 16 bytes (AES-128), 24 bytes (AES-192), or 32 bytes (AES-256) represented as an array of integers.
AES_Encrypt(block) - Encrypts one block of plaintext in-place. A block is 16 bytes as an array of integers.
AES_Decrypt(block) - Decrypts one block of ciphertext in-place. A block is 16 bytes as an array of integers.
AES_ECB_Encrypt(plaintext) - AES encryption in ECB mode. plaintext is an array of integers and will be PKCS7-padded to the correct length prior to encryption.
AES_ECB_Decrypt(ciphertext) - AES decryption in ECB mode. ciphertext is an array of integers and will be PKCS7-unpadded after decryption.
AES_CFB_Encrypt(plaintext, iv) - AES encryption in CFB mode. plaintext is an array of integers, and iv is a 16-byte array of integers.
AES_CFB_Decrypt(ciphertext, iv) - AES decryption in CFB mode. ciphertext is an array of integers, and iv is a 16-byte array of integers.
AES_Done() - Destroys tables set up by AES_Init, which is called by the constructor here.
StringToArray(s) - Helper function to convert a string to an array of integers
ArrayToString(a) - Helper function to convert an array of integers to a string
Example usage for CFB mode:
var key = [73, 32, 97, 109, 32, 83, 117, 112, 101, 114, 32, 83, 101, 107, 117, 114, 32, 40, 84, 77, 41, 32, 65, 69, 83, 50, 53, 54, 32, 107, 101, 121]; // "I am Super Sekur (TM) AES256 key"
var iv = [73, 86, 32, 105, 115, 32, 102, 105, 110, 101, 44, 32, 116, 104, 111, 46]; // "IV is fine, tho."
gs.info('Key: ' + (key));
gs.info('IV: ' + (iv));
var aes = new AES(key);
var plaintext = aes.StringToArray('Hello World! Attack At Dawn.');
aes.AES_CFB_Encrypt(plaintext, iv);
gs.info("Encrypted: " + plaintext);
aes.AES_CFB_Decrypt(plaintext, iv);
gs.info("Decrypted: " + aes.ArrayToString(plaintext));
aes.AES_Done();
*/
var AES = Class.create();
AES.prototype = {
initialize: function(key) {
this._AES_Init();
this._key = key.concat();
this._AES_ExpandKey(this._key);
},
type: 'AES',
StringToArray: function(s) {
var output = new Array();
for (var i = 0; i < s.length; i++) {
output.push(s.charCodeAt(i));
}
return output;
},
ArrayToString: function(a) {
return String.fromCharCode.apply(null, a);
},
_AES_Init: function() {
var i;
this._AES_Sbox_Inv = new Array(256);
for (i = 0; i < 256; i++) {
this._AES_Sbox_Inv[this._AES_Sbox[i]] = i;
}
this._AES_ShiftRowTab_Inv = new Array(16);
for (i = 0; i < 16; i++) {
this._AES_ShiftRowTab_Inv[this._AES_ShiftRowTab[i]] = i;
}
this._AES_xtime = new Array(256);
for (i = 0; i < 128; i++) {
this._AES_xtime[i] = i << 1;
this._AES_xtime[128 + i] = (i << 1) ^ 0x1b;
}
},
AES_Done: function() {
delete this._AES_Sbox_Inv;
delete this._AES_ShiftRowTab_Inv;
delete this._AES_xtime;
},
_AES_ExpandKey: function(key) {
var kl = key.length,
ks, Rcon = 1;
switch (kl) {
case 16:
ks = 16 * (10 + 1);
break;
case 24:
ks = 16 * (12 + 1);
break;
case 32:
ks = 16 * (14 + 1);
break;
default:
gs.error("AES_ExpandKey: Only key lengths of 16, 24 or 32 bytes allowed!");
}
for (var i = kl; i < ks; i += 4) {
var temp = key.slice(i - 4, i);
if (i % kl == 0) {
temp = new Array(this._AES_Sbox[temp[1]] ^ Rcon, this._AES_Sbox[temp[2]], this._AES_Sbox[temp[3]], this._AES_Sbox[temp[0]]);
if ((Rcon <<= 1) >= 256) {
Rcon ^= 0x11b;
}
} else if ((kl > 24) && (i % kl == 16)) {
temp = new Array(this._AES_Sbox[temp[0]], this._AES_Sbox[temp[1]], this._AES_Sbox[temp[2]], this._AES_Sbox[temp[3]]);
}
for (var j = 0; j < 4; j++) {
key[i + j] = key[i + j - kl] ^ temp[j];
}
}
},
AES_Encrypt: function(block) {
var key = this._key;
var l = key.length;
this._AES_AddRoundKey(block, key.slice(0, 16));
for (var i = 16; i < l - 16; i += 16) {
this._AES_SubBytes(block, this._AES_Sbox);
this._AES_ShiftRows(block, this._AES_ShiftRowTab);
this._AES_MixColumns(block);
this._AES_AddRoundKey(block, key.slice(i, i + 16));
}
this._AES_SubBytes(block, this._AES_Sbox);
this._AES_ShiftRows(block, this._AES_ShiftRowTab);
this._AES_AddRoundKey(block, key.slice(i, l));
},
AES_Decrypt: function(block) {
var key = this._key;
var l = key.length;
this._AES_AddRoundKey(block, key.slice(l - 16, l));
this._AES_ShiftRows(block, this._AES_ShiftRowTab_Inv);
this._AES_SubBytes(block, this._AES_Sbox_Inv);
for (var i = l - 32; i >= 16; i -= 16) {
this._AES_AddRoundKey(block, key.slice(i, i + 16));
this._AES_MixColumns_Inv(block);
this._AES_ShiftRows(block, this._AES_ShiftRowTab_Inv);
this._AES_SubBytes(block, this._AES_Sbox_Inv);
}
this._AES_AddRoundKey(block, key.slice(0, 16));
},
_pkcs7_pad: function(s) {
var c = 256 - (s.length % 256);
var padding = new Array(c);
for (var i = 0; i < c; i++) {
padding[i] = c;
}
s.push.apply(s, padding);
},
_pkcs7_unpad: function(s) {
for (var i = 0; i < s[s.length - 1]; i++) {
s.pop();
}
},
AES_ECB_Encrypt: function(pt) {
var key = this._key;
this._pkcs7_pad(pt);
for (var pos = 0; pos < pt.length; pos += 16) {
var temp = pt.slice(pos, pos + 16);
this.AES_Encrypt(temp, key);
for (var i = 0; i < temp.length; i++) {
pt[pos + i] = temp[i];
}
}
},
AES_ECB_Decrypt: function(ct) {
var key = this._key;
for (var pos = 0; pos < ct.length; pos += 16) {
var temp = ct.slice(pos, pos + 16);
this.AES_Decrypt(temp, key);
for (var i = 0; i < temp.length; i++) {
ct[pos + i] = temp[i];
}
}
ct = this._pkcs7_unpad(ct);
},
AES_CFB_Encrypt: function(pt, iv) {
var key = this._key;
var ks = iv.concat();
for (var pos = 0; pos < pt.length; pos += 16) {
this.AES_Encrypt(ks, key);
for (var i = 0; i < Math.min(ks.length, pt.length - pos); i++) {
pt[pos + i] = ks[i] = pt[pos + i] ^ ks[i];
}
}
},
AES_CFB_Decrypt: function(ct, iv) {
var key = this._key;
var ks = iv.concat();
for (var pos = 0; pos < ct.length; pos += 16) {
this.AES_Encrypt(ks, key);
var temp = ct.slice(pos, pos + 16);
for (var i = 0; i < Math.min(ks.length, ct.length - pos); i++) {
ct[pos + i] ^= ks[i];
ks[i] = temp[i];
}
}
},
_AES_SubBytes: function(state, sbox) {
for (var i = 0; i < 16; i++)
state[i] = sbox[state[i]];
},
_AES_AddRoundKey: function(state, rkey) {
for (var i = 0; i < 16; i++)
state[i] ^= rkey[i];
},
_AES_ShiftRows: function(state, shifttab) {
var h = new Array().concat(state);
for (var i = 0; i < 16; i++)
state[i] = h[shifttab[i]];
},
_AES_MixColumns: function(state) {
for (var i = 0; i < 16; i += 4) {
var s0 = state[i + 0],
s1 = state[i + 1];
var s2 = state[i + 2],
s3 = state[i + 3];
var h = s0 ^ s1 ^ s2 ^ s3;
state[i + 0] ^= h ^ this._AES_xtime[s0 ^ s1];
state[i + 1] ^= h ^ this._AES_xtime[s1 ^ s2];
state[i + 2] ^= h ^ this._AES_xtime[s2 ^ s3];
state[i + 3] ^= h ^ this._AES_xtime[s3 ^ s0];
}
},
_AES_MixColumns_Inv: function(state) {
for (var i = 0; i < 16; i += 4) {
var s0 = state[i + 0],
s1 = state[i + 1];
var s2 = state[i + 2],
s3 = state[i + 3];
var h = s0 ^ s1 ^ s2 ^ s3;
var xh = this._AES_xtime[h];
var h1 = this._AES_xtime[this._AES_xtime[xh ^ s0 ^ s2]] ^ h;
var h2 = this._AES_xtime[this._AES_xtime[xh ^ s1 ^ s3]] ^ h;
state[i + 0] ^= h1 ^ this._AES_xtime[s0 ^ s1];
state[i + 1] ^= h2 ^ this._AES_xtime[s1 ^ s2];
state[i + 2] ^= h1 ^ this._AES_xtime[s2 ^ s3];
state[i + 3] ^= h2 ^ this._AES_xtime[s3 ^ s0];
}
},
_AES_Sbox: new Array(99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171,
118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253,
147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154,
7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227,
47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170,
251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245,
188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61,
100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224,
50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213,
78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221,
116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29,
158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161,
137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22),
_AES_ShiftRowTab: new Array(0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11),
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment