This commit is contained in:
2026-06-24 16:28:42 +02:00
commit 1922f2db62
19 changed files with 571 additions and 0 deletions
Executable
+67
View File
@@ -0,0 +1,67 @@
// https://en.wikipedia.org/wiki/Rijndael_S-box
export const SBox: number[] = [
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
]
export const RCons = [
[0x00, 0x00, 0x00, 0x00], [0x01, 0x00, 0x00, 0x00], [0x02, 0x00, 0x00, 0x00], [0x04, 0x00, 0x00, 0x00],
[0x08, 0x00, 0x00, 0x00], [0x10, 0x00, 0x00, 0x00], [0x20, 0x00, 0x00, 0x00], [0x40, 0x00, 0x00, 0x00],
[0x80, 0x00, 0x00, 0x00], [0x1b, 0x00, 0x00, 0x00], [0x36, 0x00, 0x00, 0x00]
]
export const mixColumnsMatrix: number[][] = [
[0x02, 0x03, 0x01, 0x01],
[0x01, 0x02, 0x03, 0x01],
[0x01, 0x01, 0x02, 0x03],
[0x03, 0x01, 0x01, 0x02]
]
export const invSBox: number[] = [
// 0x00 to 0x0f
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
// 0x10 to 0x1f
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
// 0x20 to 0x2f
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
// 0x30 to 0x3f
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
// 0x40 to 0x4f
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
// 0x50 to 0x5f
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
// 0x60 to 0x6f
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
// 0x70 to 0x7f
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
// 0x80 to 0x8f
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
// 0x90 to 0x9f
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
// 0xa0 to 0xaf
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
// 0xb0 to 0xbf
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
// 0xc0 to 0xcf
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
// 0xd0 to 0xdf
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
// 0xe0 to 0xef
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
// 0xf0 to 0xff
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
]
Executable
+22
View File
@@ -0,0 +1,22 @@
import { decrypt, encrypt } from './utils'
const plaintext = `
A network address is an identifier for a node or host on a telecommunications network. Network addresses are designed to be unique identifiers across the network, although some networks allow for local, private addresses, or locally administered addresses that may not be unique.[1] Special network addresses are allocated as broadcast or multicast addresses. These too are not unique.
In some cases, network hosts may have more than one network address. For example, each network interface controller may be uniquely identified. Further, because protocols are frequently layered, more than one protocol's network address can occur in any particular network interface or node and more than one type of network address may be used in any one network.[2]
Network addresses can be flat addresses which contain no information about the node's location in the network (such as a MAC address), or may contain structure or hierarchical information for the routing (such as an IP address).
`
const key = 'a10KDJIGkLPmfSRV33HkwVeYgPe8n9V4'
const encryptedBlocks = encrypt(plaintext, key)
console.log('Encrypted Blocks:')
encryptedBlocks.forEach(block => { console.table(block) })
console.log('\nDecrypted Text:')
// @ts-ignore
const lineLength = process.stdout.columns
console.log('-'.repeat(lineLength))
console.log(decrypt(encryptedBlocks, key))
console.log('-'.repeat(lineLength))
Executable
+29
View File
@@ -0,0 +1,29 @@
import { addRoundKey, expandKeyFake, preprocessKey, preprocessPlaintextHex, shiftRows, subBytes } from './utils'
const plaintext = '0f1e2d3c4b5a69788796a5b4c3d2e1f0'
const key = '00000000000000000000000000000000'
const plaintextBlock = preprocessPlaintextHex(plaintext)[0]
const keyBlock = preprocessKey(key)
const roundKeys = expandKeyFake(keyBlock)
let state = plaintextBlock
state = addRoundKey(state, roundKeys[0])
state = subBytes(state)
state = shiftRows(state)
console.log(`Plaintext: ${plaintext}`)
console.log(`Key: ${key}`)
console.log('State after applying the first two phases (SubBytes and ShiftRows) of the first round inside AES algorithm:')
console.table(state)
console.log('Equivalent state in hexadecimal:')
for (let rowIndex = 0; rowIndex < state.length; rowIndex++) {
const row = state[rowIndex]
for (let cellIndex = 0; cellIndex < row.length; cellIndex++) {
const cell = row[cellIndex]
state[rowIndex][cellIndex] = cell.toString(16).padStart(2, '0') as unknown as number
}
}
console.table(state)
+16
View File
@@ -0,0 +1,16 @@
export const addRoundKey = (state: number[][], roundKey: number[][]): number[][] => {
const result: number[][] = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
for (let row = 0;row < 4;row++) {
for (let col = 0;col < 4;col++) {
result[row][col] = state[row][col] ^ roundKey[row][col]
}
}
return result
}
+40
View File
@@ -0,0 +1,40 @@
import { addRoundKey, expandKey, flattenBlocks, invMixColumns, invShiftRows, invSubBytes, preprocessKey } from './'
export const decrypt = (encryptedBlocks: number[][][], key: string): string => {
/**
* @param {number[][][]} encryptedBlocks - An array containing blocks of cyphertext. Each block is a 4x4 matrix.
* @param {string} key - A string of 128, 192, or 256 bits.
* @returns {string} The original plaintext.
*/
const keyBlock = preprocessKey(key)
const roundKeys = expandKey(keyBlock)
const keySize = key.length // Key size in bytes (16 for 128-bit, 24 for 192-bit, 32 for 256-bit)
const N_k = keySize / 4 // Number of 32-bit words in the key (4 for 128-bit, 6 for 192-bit, 8 for 256-bit)
const N_r = N_k + 6
const decryptedBlocks: number[][][] = []
for (let i = 0;i < encryptedBlocks.length;i++) {
let state = encryptedBlocks[i]
state = addRoundKey(state, roundKeys[N_r])
state = invShiftRows(state)
state = invSubBytes(state)
for (let j = N_r - 1;j >= 1;j--) {
state = addRoundKey(state, roundKeys[j])
state = invMixColumns(state)
state = invShiftRows(state)
state = invSubBytes(state)
}
state = addRoundKey(state, roundKeys[0])
decryptedBlocks.push(state)
}
const flatArray = flattenBlocks(decryptedBlocks)
const decoder = new TextDecoder()
return decoder.decode(new Uint8Array(flatArray))
}
+38
View File
@@ -0,0 +1,38 @@
import { preprocessPlaintext, preprocessKey, expandKey, addRoundKey, subBytes } from './'
import { mixColumns } from './mixColumns'
import { shiftRows } from './shiftRows'
export const encrypt = (plaintext: string, key: string): number[][][] => {
/**
* @param {string} plaintext - The plaintext to encrypt.
* @param {string} key - A string of 128, 192, or 256 bits.
* @returns {string} The encrypted cyphertext.
*/
const plaintextBlocks = preprocessPlaintext(plaintext)
const keyBlock = preprocessKey(key)
const roundKeys = expandKey(keyBlock)
const keySize = key.length // Key size in bytes (16 for 128-bit, 24 for 192-bit, 32 for 256-bit)
const N_k = keySize / 4 // Number of 32-bit words in the key (4 for 128-bit, 6 for 192-bit, 8 for 256-bit)
const N_r = N_k + 6
const encryptedBlocks: number[][][] = []
for (let i = 0;i < plaintextBlocks.length;i++) {
let state = plaintextBlocks[i]
state = addRoundKey(state, roundKeys[0])
for (let j = 1;j <= N_r - 1;j++) {
state = subBytes(state)
state = shiftRows(state)
state = mixColumns(state)
state = addRoundKey(state, roundKeys[j])
}
state = subBytes(state)
state = shiftRows(state)
state = addRoundKey(state, roundKeys[N_r])
encryptedBlocks.push(state)
}
return encryptedBlocks
}
+64
View File
@@ -0,0 +1,64 @@
import { RCons, SBox } from '../constants'
const subWord = (word: number[]): number[] => {
/**
* @param {number[]} word - An an array containing 4 numbers, each number is a hex value represeting one byte.
* @returns {number[]} An array containing the corresponding values in the S-box.
*/
return word.map(byte => SBox[byte])
}
const rotWord = (word: number[]): number[] => {
return word.slice(1).concat(word[0])
}
export const expandKey = (key: number[][]): number[][][] => {
/**
* @param {string} key - A string of 128, 192, or 256 bits.
* @returns {string[]} An array of round keys.
*/
const N_k = key[0].length // Number of columns in the key matrix (4 for 128-bit, 6 for 192-bit, 8 for 256-bit)
const N_r = N_k + 6 // Number of rounds (10 for 128-bit, 12 for 192-bit, 14 for 256-bit)
const totalKeys = 4 * (N_r + 1) // Total number of 4-byte words required
const roundKeys: number[][] = []
// Step 1: Copy the initial key as the first round key (N_k columns)
for (let i = 0;i < N_k;i++) {
roundKeys.push([
key[0][i],
key[1][i],
key[2][i],
key[3][i]
])
}
// Step 2: Expand the key to generate the full round keys
for (let i = N_k;i < totalKeys;i++) {
let temp = roundKeys[i - 1] // Previous word
if (i % N_k === 0) {
temp = subWord(rotWord(temp)) // RotWord + SubWord
temp = temp.map((t, index) => t ^ RCons[i / N_k][index]) // XOR with Rcon
} else if (N_k > 6 && i % N_k === 4) {
temp = subWord(temp) // Apply SubWord for AES-256
}
// XOR with the word N_k positions earlier
roundKeys.push(roundKeys[i - N_k].map((w, index) => w ^ temp[index]))
}
// Step 3: Convert flat array into 4x4 round key matrices
const expandedKeys: number[][][] = []
for (let i = 0;i < totalKeys / 4;i++) {
const matrix: number[][] = [
[roundKeys[i * 4][0], roundKeys[i * 4 + 1][0], roundKeys[i * 4 + 2][0], roundKeys[i * 4 + 3][0]],
[roundKeys[i * 4][1], roundKeys[i * 4 + 1][1], roundKeys[i * 4 + 2][1], roundKeys[i * 4 + 3][1]],
[roundKeys[i * 4][2], roundKeys[i * 4 + 1][2], roundKeys[i * 4 + 2][2], roundKeys[i * 4 + 3][2]],
[roundKeys[i * 4][3], roundKeys[i * 4 + 1][3], roundKeys[i * 4 + 2][3], roundKeys[i * 4 + 3][3]],
]
expandedKeys.push(matrix)
}
return expandedKeys
}
+22
View File
@@ -0,0 +1,22 @@
export const expandKeyFake = (key: number[][]): number[][][] => {
/**
* @param {string} key - A string of 128, 192, or 256 bits.
* @returns {string[]} An array of round keys.
*/
const N_k = key[0].length // Number of columns in the key matrix (4 for 128-bit, 6 for 192-bit, 8 for 256-bit)
const N_r = N_k + 6 // Number of rounds (10 for 128-bit, 12 for 192-bit, 14 for 256-bit)
const totalKeys = 4 * (N_r + 1) // Total number of 4-byte words required
const expandedKeys: number[][][] = []
for (let i = 0;i < totalKeys / 4;i++) {
const matrix: number[][] = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
]
expandedKeys.push(matrix)
}
return expandedKeys
}
+13
View File
@@ -0,0 +1,13 @@
export const flattenBlocks = (blocks: number[][][]): number[] => {
const flatArray: number[] = []
for (const block of blocks) {
for (let col = 0; col < 4; col++) {
for (let row = 0; row < 4; row++) {
flatArray.push(block[row][col])
}
}
}
return flatArray
}
+14
View File
@@ -0,0 +1,14 @@
export * from './preprocessPlaintext'
export * from './preprocessKey'
export * from './expandKey'
export * from './expandKeyFake.ts'
export * from './addRoundKey'
export * from './subBytes'
export * from './shiftRows.ts'
export * from './invShiftRows'
export * from './invSubBytes'
export * from './invMixColumns'
export * from './decrypt.ts'
export * from './encrypt.ts'
export * from './flattenBlocks.ts'
export * from './preprocessPlaintextHex.ts'
+39
View File
@@ -0,0 +1,39 @@
import { galoisMultiplyBy2 } from './mixColumns'
const galoisMultiplyBy9 = (value: number): number => {
return galoisMultiplyBy2(galoisMultiplyBy2(galoisMultiplyBy2(value))) ^ value
}
const galoisMultiplyBy11 = (value: number): number => {
return galoisMultiplyBy2(galoisMultiplyBy2(galoisMultiplyBy2(value)) ^ value) ^ value
}
const galoisMultiplyBy13 = (value: number): number => {
return galoisMultiplyBy2(galoisMultiplyBy2(galoisMultiplyBy2(value) ^ value)) ^ value
}
const galoisMultiplyBy14 = (value: number): number => {
return galoisMultiplyBy2(galoisMultiplyBy2(galoisMultiplyBy2(value) ^ value) ^ value)
}
const clampToByte = (value: number): number => {
return value & 0xFF
}
export const invMixColumns = (state: number[][]): number[][] => {
const result: number[][] = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
for (let col = 0; col < 4; col++) {
result[0][col] = clampToByte(galoisMultiplyBy14(state[0][col]) ^ galoisMultiplyBy11(state[1][col]) ^ galoisMultiplyBy13(state[2][col]) ^ galoisMultiplyBy9(state[3][col]))
result[1][col] = clampToByte(galoisMultiplyBy9(state[0][col]) ^ galoisMultiplyBy14(state[1][col]) ^ galoisMultiplyBy11(state[2][col]) ^ galoisMultiplyBy13(state[3][col]))
result[2][col] = clampToByte(galoisMultiplyBy13(state[0][col]) ^ galoisMultiplyBy9(state[1][col]) ^ galoisMultiplyBy14(state[2][col]) ^ galoisMultiplyBy11(state[3][col]))
result[3][col] = clampToByte(galoisMultiplyBy11(state[0][col]) ^ galoisMultiplyBy13(state[1][col]) ^ galoisMultiplyBy9(state[2][col]) ^ galoisMultiplyBy14(state[3][col]))
}
return result
}
+9
View File
@@ -0,0 +1,9 @@
export const invShiftRows = (state: number[][]): number[][] => {
const result: number[][] = []
for (let i = 0;i < state.length;i++) {
result[i] = state[i].slice(-i).concat(state[i].slice(0, -i))
}
return result
}
+15
View File
@@ -0,0 +1,15 @@
import { invSBox } from '../constants'
export const invSubBytes = (state: number[][]): number[][] => {
const result: number[][] = []
for (let i = 0;i < state.length;i++) {
result[i] = []
for (let j = 0;j < state[i].length;j++) {
const byte = state[i][j]
result[i][j] = invSBox[byte]
}
}
return result
}
+29
View File
@@ -0,0 +1,29 @@
export const galoisMultiplyBy2 = (value: number): number => {
return (value << 1) ^ ((value & 0x80) ? 0x1b : 0x00)
}
export const galoisMultiplyBy3 = (value: number): number => {
return galoisMultiplyBy2(value) ^ value
}
const clampToByte = (value: number): number => {
return value & 0xFF
}
export const mixColumns = (state: number[][]): number[][] => {
const result: number[][] = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
for (let col = 0; col < 4; col++) {
result[0][col] = clampToByte(galoisMultiplyBy2(state[0][col]) ^ galoisMultiplyBy3(state[1][col]) ^ state[2][col] ^ state[3][col])
result[1][col] = clampToByte(state[0][col] ^ galoisMultiplyBy2(state[1][col]) ^ galoisMultiplyBy3(state[2][col]) ^ state[3][col])
result[2][col] = clampToByte(state[0][col] ^ state[1][col] ^ galoisMultiplyBy2(state[2][col]) ^ galoisMultiplyBy3(state[3][col]))
result[3][col] = clampToByte(galoisMultiplyBy3(state[0][col]) ^ state[1][col] ^ state[2][col] ^ galoisMultiplyBy2(state[3][col]))
}
return result
}
+34
View File
@@ -0,0 +1,34 @@
export const preprocessKey = (key: string): number[][] => {
/**
* @param {string} key - A string of 128, 192, or 256 bits.
* @returns {number[][]} A 4x4/6/8 matrix representing the key.
*/
const encoder = new TextEncoder()
const byteArray = encoder.encode(key)
const validKeyLengths = [128, 192, 256]
if (!validKeyLengths.includes(byteArray.length * 8)) {
throw new Error('Invalid Key!')
}
const N_k = (byteArray.length * 8) / 32
const matrix: number[][] = [
Array(N_k).fill(0),
Array(N_k).fill(0),
Array(N_k).fill(0),
Array(N_k).fill(0)
]
// Fill the matrix in column-major order, i.e., top-to-bottom + left-to-right
// byteArray.length is either 16, 24, or 32.
for (let i = 0;i < byteArray.length;i++) {
const row = i % 4
const col = Math.floor(i / 4)
matrix[row][col] = byteArray[i]
}
return matrix
}
+49
View File
@@ -0,0 +1,49 @@
export type PlaintextBlock = number[][]
export const preprocessPlaintext = (plaintext: string): PlaintextBlock[] => {
/**
* @param {string} plaintext - The plaintext to encrypt.
* @returns {PlaintextBlock[]} Blocks of 128 bits representing the plaintext. Each block is a 4x4 column-major matrix.
*/
const encoder = new TextEncoder()
const byteArray = encoder.encode(plaintext) // Array where each item is an 8-bit representation of a character of the string
const blockSize = 16 // 128 bits = 16 bytes
// Loop through the byteArray and split it into 128-bit blocks
const blocks: Uint8Array[] = []
for (let i = 0;i < byteArray.length;i += blockSize) {
const block = byteArray.slice(i, i + blockSize)
if (block.length < blockSize) {
// Pad the block with zeroes
const paddedBlock = new Uint8Array(blockSize)
paddedBlock.set(block)
blocks.push(paddedBlock)
} else {
blocks.push(block)
}
}
const plaintextBlocks: number[][][] = blocks.map(block => {
// Block is a 128-bit array containing 16 8-bit integers.
const matrix: number[][] = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
]
// Fill the matrix in column-major order, i.e., top-to-bottom + left-to-right
for (let i = 0;i < blockSize;i++) {
const row = i % 4
const col = Math.floor(i / 4)
matrix[row][col] = block[i]
}
return matrix
})
return plaintextBlocks
}
+49
View File
@@ -0,0 +1,49 @@
import { PlaintextBlock} from './'
export const preprocessPlaintextHex = (hexPlaintext: string): PlaintextBlock[] => {
/**
* @param {string} hexPlaintext - The plaintext to encrypt, as a hexadecimal string.
* @returns {PlaintextBlock[]} Blocks of 128 bits representing the plaintext. Each block is a 4x4 column-major matrix.
*/
const blockSize = 16 // 128 bits = 16 bytes
// Convert the hex string into a byte array, where each pair of hex digits is a byte
const byteArray = new Uint8Array(hexPlaintext.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16)))
// Loop through the byteArray and split it into 128-bit blocks
const blocks: Uint8Array[] = []
for (let i = 0;i < byteArray.length;i += blockSize) {
const block = byteArray.slice(i, i + blockSize)
if (block.length < blockSize) {
// Pad the block with zeroes
const paddedBlock = new Uint8Array(blockSize)
paddedBlock.set(block)
blocks.push(paddedBlock)
} else {
blocks.push(block)
}
}
const plaintextBlocks: number[][][] = blocks.map(block => {
// Block is a 128-bit array containing 16 8-bit integers.
const matrix: number[][] = [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
]
// Fill the matrix in column-major order, i.e., top-to-bottom + left-to-right
for (let i = 0;i < blockSize;i++) {
const row = i % 4
const col = Math.floor(i / 4)
matrix[row][col] = block[i]
}
return matrix
})
return plaintextBlocks
}
+9
View File
@@ -0,0 +1,9 @@
export const shiftRows = (state: number[][]): number[][] => {
const result: number[][] = []
for (let i = 0;i < state.length;i++) {
result[i] = state[i].slice(i).concat(state[i].slice(0, i))
}
return result
}
+13
View File
@@ -0,0 +1,13 @@
import { SBox } from '../constants'
export const subBytes = (state: number[][]): number[][] => {
const result: number[][] = []
for (let i = 0;i < state.length;i++) {
result[i] = []
for (let j = 0;j < state[0].length;j++) {
const byte = state[i][j]
result[i][j] = SBox[byte]
}
}
return result
}