Compute CBC-MAC with AES-256 and openssl in C

I want to calculate the CBC-MAC for a given plaintext using openssl. I have the following plaintext (hexdump):

hexdump -C example.txt
00000000  4d 41 43 73 20 61 72 65  20 76 65 72 79 20 75 73  |MACs are very us|
00000010  65 66 75 6c 20 69 6e 20  63 72 79 70 74 6f 67 72  |eful in cryptogr|
00000020  61 70 68 79 21 20 20 20  20 20 20 20 20 20 20 20  |aphy!           |

      

If I use the openssl command line feature I get the following solution:

openssl aes-256-cbc -in example.txt -K 8000000000000000000000000000000000000000000000000000000000000001 -e -iv 00 | hexdump -C
00000000  e8 e9 a4 ce 5d 20 c4 ad  f5 52 b2 c6 38 2e 12 4e  |....] ...R..8..N|
00000010  20 f5 63 65 b4 b3 96 9f  ad 8d ca e4 e8 34 2a e5  | .ce.........4*.|
00000020  0d 82 0e 3a 1e 10 5d 30  72 16 fc 00 c7 a5 b4 49  |...:..]0r......I|
00000030  f5 63 9f 85 ff e3 a4 a4  23 6e 6f 09 20 ed b1 ae  |.c......#no. ...|

      

So far so good. I have one extra block because the first block needs to be an encrypted IV. The last line should now be my CBC-MAC if I understood it correctly. Then I tried to do the same in C, here is some sample code:

#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/aes.h>

int main(int argc, char *argv[])
{
    unsigned char obuf[64] = {0};
    unsigned char decbuf[48] = {0};
    unsigned char msg1[] = {0x4d, 0x41, 0x43, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x75, 0x73,
                            0x65, 0x66, 0x75, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, 
                            0x61, 0x70, 0x68, 0x79, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
    unsigned char key[] =  {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
    unsigned char ivenc[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    unsigned char ivdec[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    int i=0;

    AES_KEY enc_key, dec_key;
    AES_set_encrypt_key(key, 256, &enc_key);
    AES_cbc_encrypt(msg1, obuf, 48, &enc_key, ivenc, AES_ENCRYPT);

    for (i = 0; i < 64; i++) {
        if (!(i%16))
            printf("\n");
        printf("%02x ", obuf[i]);
    }
    printf("\n");

    AES_set_decrypt_key(key, 256, &dec_key);
    AES_cbc_encrypt(obuf, decbuf, 64, &dec_key, ivdec, AES_DECRYPT);

    for (i = 0; i < 48; i++) {
        if (!(i%16))
            printf("\n");
        printf("%02x ", decbuf[i]);
    }
    printf("\n");

    return 0;
}

      

I then decrypt the encrypted message to test my code. The output of my code is pretty awesome:

e8 e9 a4 ce 5d 20 c4 ad f5 52 b2 c6 38 2e 12 4e 
20 f5 63 65 b4 b3 96 9f ad 8d ca e4 e8 34 2a e5 
0d 82 0e 3a 1e 10 5d 30 72 16 fc 00 c7 a5 b4 49 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 

4d 41 43 73 20 61 72 65 20 76 65 72 79 20 75 73 
65 66 75 6c 20 69 6e 20 63 72 79 70 74 6f 67 72 
61 70 68 79 21 20 20 20 20 20 20 20 20 20 20 20 

      

The encrypted message is completely identical to the command line output, except for the last line - all 0. I thought the first line was an encrypted IV and the next three lines were an encrypted message, so with my interpretation, the last line of the message was not encrypted. But to my surprise, the decryption leads exactly to the text that I used as input, so there seems to be no loss of information.

Questions:

  • How is it possible that I can decrypt the output of my encryption even if I don't have the last line?
  • What is my CBC-MAC? is this the last line from my command line output or the last line of my C code output?
  • Am I doing something wrong in my C code? I used this question as a reference.
+3


source to share


1 answer


Your error is here:

I have one extra block because the first block needs to be an encrypted IV.

The extra block is because OpenSSL adds a padding to the plain text so that it is a multiple of the block size (16 bytes for AES). In this case, the plain text is already a multiple of 16 bytes, but the padding scheme used ( PKCS7 ) always adds padding, so here the whole block is added before encryption.



It's common to add an IV to the ciphertext front, but that's not what's happening here.

To get the same result from your code, you will need to add this addition yourself. In this case it is quite simple, just add sixteen 0x10

bytes to the end msg1

(so its processing length is 64) and change 48

in the call AES_cbc_encrypt

to 64

. In zeros, you only see the value that you initialize obuf

to, since you are only writing 48 bytes to this buffer.

+5


source







All Articles