前言

在上一篇介绍Tiny AES中,该库本身是没有提供填充模式的,所以,明文,密码,初始向量都是16字节的倍数,但是在实际使用中,明文是没有办法时时刻刻都是16字节的倍数的。所以需要找到一种填充方式。大部分PADDING模式为PKCS5PKCS7,由于PKCS5填充是的8字节,PKCS7填充的是16字节,所以推荐使用PKCS7

通过在网上搜索,找到了一篇教程:Tiny AES in CBC mode with PKCS7 padding written in C,于是记录下,直接放代码:

#define CBC 1
#include "aes.h"

//Initialization Vector
uint8_t iv[]  = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };
                             
char* report = "my super secret thing that needs to remain that way!";
char* key = "thisIstheKey";
  • 首先选择 CBC 模式
  • 定义16字节的初始化向量(对于正常使用,IV应当是随机产生的,后续文章将介绍如何随机产生IV)
  • 需要加密的明文和密码都不是16字节的倍数,需要填充
int dlen = strlen(report);          # the length of the report
int klen = strlen(key);             # the length of the key

int dlenu = dlen;
if (dlen % 16) {
    dlenu += 16 - (dlen % 16);      # make the length multiple of 16 bytes
}

int klenu = klen;
if (klen % 16) {
    klenu += 16 - (klen % 16);      # make the length multiple of 16 bytes
}

通过余数法来确定明文和密码所要达到16字节倍数的长度:
假设 dlen = 38 ====> dlen % 16 ======> 38 % 16 = 38-(16×2) = 38-32 = 6
dlenu += 16 -6 =====> 38 + 10 = 48 # 即长度应当是48

// Make the uint8_t arrays
uint8_t hexarray[dlenu];
uint8_t kexarray[klenu];

// Initialize them with zeros
memset( hexarray, 0, dlenu );
memset( kexarray, 0, klenu );


// Fill the uint8_t arrays
for (int i=0;i<dlen;i++) {
    hexarray[i] = (uint8_t)report[i];
}
for (int i=0;i<klen;i++) {
    kexarray[i] = (uint8_t)key[i];
}

定义上述长度的数组,初始化后将明文和密码各自写入数组

int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );

填充明文数组和密码数组

//start the encryption
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, kexarray, iv);
    
// encrypt
AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);

之后就是开始AES加密数据了

// reset the iv !! important to work!
AES_ctx_set_iv(&ctx,iv);

// start decryption
AES_CBC_decrypt_buffer(&ctx, hexarray, dlenu);

size_t actualDataLength = pkcs7_padding_data_length( hexarray, dlenu, 16);

printf("the decrypted STRING = ");
for (i=0; i<actualDataLength;i++){
    printf("%02x",hexarray[i]);
}
printf("\n");

解密数据,其中pkcs7_padding_data_length来计算移除填充之后的长度,即明文原本的长度


整个代码的结构:

.
├── aes.c
├── aes.h
├── pkcs7_padding.c
├── pkcs7_padding.h
└── test.c

test.c

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define CBC 1

#include "aes.h"
#include "pkcs7_padding.c"

int main(void)
{
    //Initialization Vector
    uint8_t iv[]  = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };

    uint8_t i;                               
    char* report = "my super secret thing that needs to remain that way!";
    char* key = "thisIstheKey";
    int dlen = strlen(report);
    int klen = strlen(key);
    
    printf("THE PLAIN TEXT STRING = ");
    for (i=0; i<dlen;i++){
        printf("%c", report[i]);
    }
    printf("\n");
    
   
    //Proper Length of report
    int dlenu = dlen;
    if (dlen % 16) {
        dlenu += 16 - (dlen % 16);
        printf("The original length of the STRING = %d and the length of the padded STRING = %d\n", dlen, dlenu);
    }
    
    //Proper length of key
    int klenu = klen;
    if (klen % 16) {
        klenu += 16 - (klen % 16);
        printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
    }
    
    // Make the uint8_t arrays
    uint8_t hexarray[dlenu];
    uint8_t kexarray[klenu];
    
    // Initialize them with zeros
    memset( hexarray, 0, dlenu );
    memset( kexarray, 0, klenu );
    
    // Fill the uint8_t arrays
    for (int i=0;i<dlen;i++) {
        hexarray[i] = (uint8_t)report[i];
    }
    for (int i=0;i<klen;i++) {
        kexarray[i] = (uint8_t)key[i];
    }                           
  
    int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
    int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );
    printf("Is the pkcs7 padding pad  report = %d  |  key = %d\n", reportPad, keyPad);
    
    printf("The padded STRING in hex is = ");
    for (i=0; i<dlenu;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");
    
    printf("The padded key in hex is = ");
    for (i=0; i<klenu;i++){
        printf("%02x",kexarray[i]);
    }
    printf("\n");
        
    // In case you want to check if the padding is valid
    int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
    int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
    printf("Is the pkcs7 padding valid  report = %d  |  key = %d\n", valid, valid2);
    
    //start the encryption
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, kexarray, iv);
    
    // encrypt
    AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);
    printf("the encrypted STRING = ");
    for (i=0; i<dlenu;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");
        
    // reset the iv !! important to work!
    AES_ctx_set_iv(&ctx,iv);
    
    // start decryption
    AES_CBC_decrypt_buffer(&ctx, hexarray, dlenu);
    
    size_t actualDataLength = pkcs7_padding_data_length( hexarray, dlenu, 16);
    printf("The actual data length (without the padding) = %ld\n", actualDataLength);
    
    printf("the decrypted STRING in hex = ");
    for (i=0; i<actualDataLength;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");

    return 0;
}

这里定义了两个变长数组,在GCC中没有关系,在Visual Studio 2019中会报错,可以将其改成指针的形式uint8_t * hexarray = malloc(dlenu);uint8_t * kexarray = malloc(klenu);,但是别忘了#include <stdlib.h>

pkcs7_padding.h

#ifndef _PKCS7_PADDING_H_
#define _PKCS7_PADDING_H_

#include <stdint.h>
#include <stddef.h>

/* Pad a buffer with bytes as defined in PKCS#7 
 * Returns the number of pad bytes added, or zero if
 * the buffer size is not large enough to hold the correctly padded data
 */
int pkcs7_padding_pad_buffer( uint8_t *buffer,  size_t data_length, size_t buffer_size, uint8_t modulus );

int pkcs7_padding_valid( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus );

/* Given a block of pkcs7 padded data, return the actual data length in the block based on the padding applied.
 * buffer_size must be a multiple of modulus
 * last byte 'x' in buffer must be between 1 and modulus
 * buffer_size must be at least x + 1 in size
 * last 'x' bytes in buffer must be same as 'x'
 * returned size will be buffer_size - 'x'
 */
size_t pkcs7_padding_data_length( uint8_t * buffer, size_t buffer_size, uint8_t modulus );

#endif

pkcs7_padding.c

#include "pkcs7_padding.h"

int pkcs7_padding_pad_buffer( uint8_t *buffer,  size_t data_length, size_t buffer_size, uint8_t modulus ){
  if ( data_length % modulus == 0) {
    return 0;
  }
  uint8_t pad_byte = modulus - ( data_length % modulus ) ;
  if( data_length + pad_byte > buffer_size ){
    return -pad_byte;
  }
  int i = 0;
  while( i <  pad_byte){
    buffer[data_length+i] = pad_byte;
    i++;
  }
  return pad_byte;
}

int pkcs7_padding_valid( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus ){  
  uint8_t expected_pad_byte = modulus - ( data_length % modulus ) ;
  if( data_length + expected_pad_byte > buffer_size ){
    return 0;
  }
  int i = 0;
  while( i < expected_pad_byte ){
    if( buffer[data_length + i] != expected_pad_byte){
      return 0;
    }
    i++;
  }
  return 1;
}

size_t pkcs7_padding_data_length( uint8_t * buffer, size_t buffer_size, uint8_t modulus ){
  /* test for valid buffer size */
  if( buffer_size % modulus != 0 ||
    buffer_size < modulus ){
    return 0;
  }
  uint8_t padding_value;
  padding_value = buffer[buffer_size-1];
  /* test for valid padding value */
  if( padding_value < 1 || padding_value > modulus ){
    return buffer_size;
  }
  /* buffer must be at least padding_value + 1 in size */
  if( buffer_size < padding_value + 1 ){
    return 0;
  }
  uint8_t count = 1;
  buffer_size --;
  for( ; count  < padding_value ; count++){
    buffer_size --;
    if( buffer[buffer_size] != padding_value ){
      return 0;
    }
  }
  return buffer_size;
}

aes.haes.c直接使用上一篇文章的即可。


本文参考了Tiny AES in CBC mode with PKCS7 padding written in C,对其给出的代码有修改。

标签: none

评论已关闭