cryptopals

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

commit 6f6e9b9d3b4bfee48d3874df10d7cc854d05bfc5
parent 5ba2137db9ed643b499a3ce25b56ca55398a1467
Author: Jack Sangdahl <jack@pngu.org>
Date:   Tue, 28 Oct 2025 09:23:35 -0600

challenge 27: recover the key from cbc with iv=key

Diffstat:
Asrc/27_recover_key_cbc.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+), 0 deletions(-)

diff --git a/src/27_recover_key_cbc.c b/src/27_recover_key_cbc.c @@ -0,0 +1,66 @@ +// 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; +}