🏳️
The CTF Recipes
  • Introduction
  • Cryptography
    • Introduction
    • General knowledge
      • Encoding
        • Character encoding
          • ASCII
          • Unicode
          • UTF-8
        • Data encoding
          • Base16
          • Base32
          • Base64
      • Maths
        • Modular arithmetic
          • Greatest Common Divisor
          • Fermat's little theorem
          • Quadratic residues
          • Tonelli-Shanks
          • Chinese Remainder Theorem
          • Modular binomial
      • Padding
        • PKCS#7
    • Misc
      • XOR
    • Mono-alphabetic substitution
      • Index of coincidence
      • frequency analysis
      • Well known algorithms
        • πŸ”΄Scytale
        • πŸ”΄ROT
        • πŸ”΄Polybe
        • πŸ”΄Vigenere
        • πŸ”΄Pigpen cipher
        • πŸ”΄Affine cipher
    • Symmetric Cryptography
      • AES
        • Block Encryption procedure
          • Byte Substitution
          • Shift Row
          • Mix Column
          • Add Key
          • Key Expansion / Key Schedule
        • Mode of Operation
          • ECB
            • Block shuffling
              • Challenge example
            • ECB Oracle
              • Challenge example
          • CBC
            • Bit flipping
              • Challenge example
            • Padding oracle
              • Challenge example
          • OFB
            • Key stream reconstruction
            • Encrypt to Uncrypt
  • πŸ› οΈPwn
    • General knowledge
      • STACK
        • Variables storage
        • Stack frame
      • PLT and GOT
      • HEAP
        • HEAP operations
        • Chunk
        • Bins
        • Chunk allocation and reallocation
      • Syscall
    • Architectures
      • aarch32
        • Registers
        • Instruction set
        • Calling convention
      • aarch64
        • Registers
        • Instruction set
        • Calling convention
      • mips32
        • Registers
        • Instruction set
        • Calling convention
      • mips64
        • Registers
        • Instruction set
        • Calling convention
      • x86 / x64
        • Registers
        • Instruction set
        • Calling convention
    • Stack exploitation
      • Stack Buffer Overflow
        • Dangerous functions
          • gets
          • memcpy
          • sprintf
          • strcat
          • strcpy
        • Basics
          • Challenge example
        • Instruction pointer Overwrite
          • Challenge example
        • De Bruijn Sequences
        • Stack reading
          • Challenge example
      • Format string
        • Dangerous functions
          • printf
          • fprintf
        • Placeholder
        • Data Leak
          • Challenge example
        • Data modification
          • Challenge example
      • Arbitrary code execution
        • Shellcode
        • ret2reg
        • Code reuse attack
          • Ret2plt
          • Ret2dlresolve
          • GOT Overwrite
          • Ret2LibC
          • Leaking LibC
          • Ret2csu
          • Return Oriented Programming - ROP
          • Sigreturn Oriented Programming - SROP
          • Blind Return Oriented Programming - BROP
            • Challenge example
          • πŸ”΄Call Oriented Programming - COP
          • πŸ”΄Jump Oriented Programming - JOP
          • One gadget
        • Stack pivoting
    • πŸ› οΈHeap exploitation
      • Heap overflow
        • Challenge example
      • Use after free
        • Challenge example
      • πŸ› οΈDouble free
      • πŸ”΄Unlink exploit
    • Protections
      • Stack Canaries
      • No eXecute
      • PIE
      • ASLR
      • RELRO
    • Integer overflow
Powered by GitBook
On this page
  • Algorithm steps
  • The first four words group
  • The others words groups
  • The first word of each groups
  • The g() function
  • Python implementation
  1. Cryptography
  2. Symmetric Cryptography
  3. AES
  4. Block Encryption procedure

Key Expansion / Key Schedule

PreviousAdd KeyNextMode of Operation

Last updated 2 years ago

Each round use its own round key into the "" step that is derived from the original encryption key.

So, the Key Schedule will allow creating a different key for each encryption round, each of these sub-keys being 128 bits

The entire key length is then 128 times n+1 ( where n is the amount of round, depending of the original key size )

  • 128β‹…11=1408128 \cdot 11 = 1408128β‹…11=1408 bits for AES-128

  • 128β‹…13=1664128 \cdot 13 = 1664128β‹…13=1664 bits for AES-192

  • 128β‹…15=1920128 \cdot 15 = 1920128β‹…15=1920 bits for AES-256

To make the key expansion, the original key is divided into 32 bits blocks called words

Here is 4 words for AES-128, 6 for AES-192 and 8 for AES-256

The next words are calculated following these graph :

Algorithm steps

The following explanation will be based on AES-128. Some little ajustement ( such as the amount of generated words ) have to be done to make it applicable to the others key size.

As explained before, AES-128 need a total key lenght of 1408 bits ( 11β‹…12811 \cdot 12811β‹…128 ).

As each word has a size of 32 bits, ( 140832=44\frac{1408}{32} = 44321408​=44 )44 words are needed.

The first four words group

The first four words are provided by the original key.

  • W0 = key[0:31]

  • W1 = key[32:63]

  • W2 = key[64:95]

  • W3 = key[96:127]

Here, we have the four words of the key used for the round 0 ( before the 10 loops )

To serve as the round key for the ithi^{th}ith round, iii must be a multiple of 4.

These will obviously serve as the round key for the i/4thi/4^{th}i/4th round. For example :

  • w4, w5, w6, w7 is the round key for round 1

  • w8, w9, w10, w11 the round key for round 2,

  • and so on.

The others words groups

Let’s say that we have the four words of the round key for the i th round:

wiβ€…β€Šwi+1β€…β€Šwi+2β€…β€Šwi+3w_i \; w_{i+1} \; w_{i+2} \; w_{i+3}wi​wi+1​wi+2​wi+3​

And we need to determine the words

wi+4β€…β€Šwi+5β€…β€Šwi+6β€…β€Šwi+7w_{i+4} \; w_{i+5} \; w_{i+6} \; w_{i+7}wi+4​wi+5​wi+6​wi+7​

Using the Figure 1, we can write :

wi+5=wi+4βŠ•wi+1wi+6=wi+5βŠ•wi+2wi+7=wi+6βŠ•wi+3w_{i+5} = w_{i+4} \oplus w_{i+1} \\ w_{i+6} = w_{i+5} \oplus w_{i+2} \\ w_{i+7} = w_{i+6} \oplus w_{i+3} \\wi+5​=wi+4β€‹βŠ•wi+1​wi+6​=wi+5β€‹βŠ•wi+2​wi+7​=wi+6β€‹βŠ•wi+3​

Note that except for the first word in a new 4-word grouping, each word is an XOR of the previous word and the corresponding word in the previous 4-word grouping.

The first word of each groups

wi+4w_{i+4}wi+4​ is the beginning of the 4-word group and is obtained by using :

wi+4=wiβŠ•g(wi+3)w_{i+4} = w_i \oplus g(w_{i+3})wi+4​=wiβ€‹βŠ•g(wi+3​)

The first word of the new 4-word group is obtained by XOR’ing the first word of the last group ( wiw_iwi​ ) with the result of a function g() applied to the last word of the previous 4-word group

The g() function

The function g()g()g()consists of the following 3 steps :

  • Perform a one-byte left circular rotation on the argument 4-byte word.

  • XOR the bytes obtained from the previous step with a round constant.

The round constant is a word whose three rightmost bytes are always zero.

Therefore, XOR’ing with the round constant amounts to XOR’ing with just its leftmost byte.

The round constant for the ithi^{th}ithround is noted Rcon[i]Rcon[i]Rcon[i].

Rcon[i]=(RC[i],0,0,0)Rcon[i] = (RC[i], 0 , 0 ,0 )Rcon[i]=(RC[i],0,0,0)

The only non-zero byte in the round constants, RC[i]RC[i]RC[i], obeys the following recursion:

RC[1]=0x01RC[i]=0x02β‹…RC[iβˆ’1]RC[1] = 0x01 \\ RC[i] = 0x02 \cdot RC[i-1]RC[1]=0x01RC[i]=0x02β‹…RC[iβˆ’1]

Python implementation

def expand_key(master_key):
    """
    Expands and returns a list of key matrices for the given master_key.
    """

    # Round constants https://en.wikipedia.org/wiki/AES_key_schedule#Round_constants
    r_con = (
        0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
        0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
        0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
        0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,
    )

    # Initialize round keys with raw key material.
    key_columns = bytes2matrix(master_key)
    iteration_size = len(master_key) // 4

    # Each iteration has exactly as many columns as the key material.
    i = 1
    while len(key_columns) < (N_ROUNDS + 1) * 4:
        # Copy previous word.
        word = list(key_columns[-1])

        # Perform schedule_core once every "row".
        if len(key_columns) % iteration_size == 0:
            # Circular shift.
            word.append(word.pop(0))
            # Map to S-BOX.
            word = [s_box[b] for b in word]
            # XOR with first byte of R-CON, since the others bytes of R-CON are 0.
            word[0] ^= r_con[i]
            i += 1
        elif len(master_key) == 32 and len(key_columns) % iteration_size == 4:
            # Run word through S-box in the fourth iteration when using a
            # 256-bit key.
            word = [s_box[b] for b in word]

        # XOR with equivalent word from previous iteration.
        word = bytes(i^j for i, j in zip(word, key_columns[-iteration_size]))
        key_columns.append(word)

    # Group key words in 4x4 byte matrices.
    return [key_columns[4*i : 4*(i+1)] for i in range(len(key_columns) // 4)]
Figure 2. AES 192 Expansion key

Perform a byte substitution for each byte of the word using the same "S-box" in the step of the encryption rounds

The multiplication applied here is the same as in when multiplying by 2.

SubBytes
Mix Column operation
Add Key
Figure 1. AES 128 Expansion key
Figure 3. AES 256 Expansion key