cryptopals

Cryptopals Challenges
git clone https://git.pngu.org/cryptopals
Log | Files | Refs

27_recover_key_cbc.c (2557B)


// Recover the key from CBC with IV=Key
#include "aes.h"
#include "random.h"

/*
 * take your code from the cbc exercise and modify so that it repurposes the key
 * for cbc encryption as the iv. applications sometimes use the key as an iv on
 * the auspices that both the sender and the receiver have to know the key
 * already, and can save some space by using it as both a key and an iv. using
 * the key as an iv is insecure; an attacker can modify ciphertext in flight can
 * get the receiver to decrypt a value that will reveal the key. the cbc code
 * from exercise 16 encrypts a url string. verify each byte of the plaintext for
 * ascii compliacne (ie. look for high-ascii values). noncompliant messages
 * should raise an exception or return an error that includes the decrypted
 * plaintext (this happens all the time in real systems, for what it's worth).
 * use your code to encrypt a message that is at least 3 blocks long:
 * 'aes_cbc(p1, p2, p3) -> c1, c2, c3'. modify the message (you are now the
 * attacker): 'c1, c2, c3 -> c1, 0, c1'. decrypt the message (you are now the
 * receiver) and raise the appropriate error if high-ascii is found. as the
 * attacker, recovering the plaintext from the error, extract the key: 'p1 ^
 * p3'.
 */

unsigned char *key;
unsigned char *iv;

struct bytes validate(struct bytes input) {
  struct bytes decrypted = aes_decrypt_cbc(input.data, input.len, key, 16, iv);
  if (!decrypted.data || !decrypted.len)
    return (struct bytes){NULL, 0};
  return decrypted;
}

int main(void) {
  srand(time(NULL));
  key = iv = (unsigned char *)"ITSNOTCOMINGHOME";
  while (true) {
    unsigned char *rand_data = random_bytes(16);
    unsigned char attack_blocks[48];
    struct bytes payload = {attack_blocks, sizeof(attack_blocks)};
    memcpy(attack_blocks, rand_data, AES_BLOCK_SIZE);
    memset(attack_blocks + AES_BLOCK_SIZE, 0, AES_BLOCK_SIZE);
    memcpy(attack_blocks + AES_BLOCK_SIZE * 2, rand_data, AES_BLOCK_SIZE);
    struct bytes valid = validate(payload);
    if (!valid.data) {
      free(rand_data);
      continue;
    }
    if (valid.len < AES_BLOCK_SIZE * 3) {
      free(valid.data);
      free(rand_data);
      continue;
    }
    unsigned char recovered_key[17] = {0};
    for (size_t i = 0; i < AES_BLOCK_SIZE; i++)
      recovered_key[i] = valid.data[i] ^ valid.data[i + AES_BLOCK_SIZE * 2];
    printf("Recovered key: ");
    for (size_t i = 0; i < 16; i++)
      printf("%c", recovered_key[i]);
    printf("\n");
    free(valid.data);
    free(rand_data);
    break;
  }
  return 0;
}