如何在计数器模式下将AES_encrypt转换为EVP接口?

时间:2017-05-29 16:30:37

标签: c encryption openssl aes encryption-symmetric

我正在编写一些需要AES Ctr 128的常见加密的东西。所以我要用加密的东西挖掘一下。

目前我测试一个工作的代码(在这里找到)(加密/解密文件):

#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>


struct ctr_state {
  unsigned char ivec[AES_BLOCK_SIZE];
  unsigned int num;
  unsigned char ecount[AES_BLOCK_SIZE];
};

void print_hex(unsigned char *c) {
  for(int i = 0; i < 16; i++) {
    printf("%02X.", c[i]);
  }
  printf("\n");
}


void init_ctr(struct ctr_state *state, const unsigned char iv[16]) {
  state->num = 0;
  memset(state->ecount, 0, 16);
  memset(state->ivec + 8, 0, 8);
  memcpy(state->ivec, iv, 8);
}

void fencrypt(char* read, char* write, const unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;
  AES_KEY key;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  unsigned char iv[AES_BLOCK_SIZE];

  struct ctr_state state;


  RAND_bytes(iv, AES_BLOCK_SIZE);

  print_hex(iv);
  readFile = fopen(read,"rb");
  writeFile = fopen(write,"wb");


  AES_set_encrypt_key(enc_key, 128, &key);

  init_ctr(&state, iv);

  fwrite(state.ivec, 1, AES_BLOCK_SIZE, writeFile);

  print_hex(state.ivec);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);

    print_hex(state.ivec);
    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

void fdecrypt(char* read, char* write, const unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;
  AES_KEY key;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  unsigned char iv[AES_BLOCK_SIZE];

  struct ctr_state state;

  readFile=fopen(read,"rb");
  writeFile=fopen(write,"wb");

  fread(iv, 1, AES_BLOCK_SIZE, readFile);

  AES_set_encrypt_key(enc_key, 128, &key);

  init_ctr(&state, iv);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);

    print_hex(state.ivec);
    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
            break;
    }
  }
  fclose(writeFile);
  fclose(readFile);
}

int main(int argc, char *argv[]) {
 char* secret = "supersecret";

 fencrypt("encme.txt", "enced.enc", (const unsigned char*)secret);
 fdecrypt("enced.enc", "unenced.txt", (const unsigned char*)secret);
}

这项工作做得很好。但似乎标准是现在使用openssl使用EVP功能。所以我尝试调整我的代码但是我的实现显然有些问题。我不了解如何正确更新/增加IV矢量。

这里我的新代码包含EVP(工作但不是增量/计数器):

#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


struct ctr_state {
  EVP_CIPHER_CTX* cipher;
  int num;
};

void print_hex(unsigned char *c) {
  for(int i = 0; i < 16; i++) {
    printf("%02X.", c[i]);
  }
  printf("\n");
}

void init_ctr(struct ctr_state *state, unsigned char iv[16], unsigned char* key) {
    state->num = 0;
    state->cipher = EVP_CIPHER_CTX_new();
    EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ctr(), NULL, key, iv);
}

void fencrypt(char* read, char* write, unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  unsigned char iv[AES_BLOCK_SIZE];

  struct ctr_state state;

  RAND_bytes(iv, AES_BLOCK_SIZE);

  readFile = fopen(read,"rb");
  writeFile = fopen(write,"wb");

  fwrite(iv, 1, AES_BLOCK_SIZE, writeFile);
  init_ctr(&state, iv, enc_key);
  print_hex(iv);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);

    EVP_EncryptUpdate(state.cipher, outdata, &state.num, indata, bytes_read);

    EVP_EncryptUpdate(state.cipher, outdata, &state.num, indata, bytes_read);

    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

void fdecrypt(char* read, char* write, unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  unsigned char iv[AES_BLOCK_SIZE];

  struct ctr_state state;

  readFile = fopen(read,"rb");
  writeFile = fopen(write,"wb");

  fread(iv, 1, AES_BLOCK_SIZE, readFile);
  init_ctr(&state, iv, enc_key);

  print_hex(iv);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);

    EVP_EncryptUpdate(state.cipher, outdata, &state.num, indata, bytes_read);
    printf("Pass %d ",state.num);

    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

int main(int argc, char *argv[]) {
 char* secret = "supersecret";

 fencrypt("encme.txt", "enced.enc", (unsigned char*)secret);
 fdecrypt("enced.enc", "unenced.txt", (unsigned char*)secret);
}

任何帮助表示赞赏。谢谢。

3 个答案:

答案 0 :(得分:0)

当我不得不使用EVP openssl函数时,这是我的解决方案。应该使用每个可用的密码

/*! @file s70357.c
 * IS_Beleg by Markus Klemm
 * */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <openssl/evp.h>

#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


/*!
 * @param cipher_text Buffer, at least (plain_len + cipher_block_size - 1) bytes big,
 * where the encrypted data will be stored.
 * @param cipher_text_len Actual length of encrypted data in cipher_text in bytes
 */
bool mk_evp_encrypt(const unsigned char *plain_text,
                    const int plain_text_len,
                    unsigned char *cipher_text,
                    int *cipher_text_len,
                    const EVP_CIPHER *cipher,
                    unsigned char *key,
                    unsigned char *iv) {
    EVP_CIPHER_CTX *context = EVP_CIPHER_CTX_new();
    if (!context) {
        return false;
    }
    if (!EVP_EncryptInit_ex(context, cipher, NULL, key, iv)) {
        EVP_CIPHER_CTX_free(context);
        return false;
    }

    *cipher_text_len = 0;
    if (!EVP_EncryptUpdate(context, cipher_text, cipher_text_len, plain_text, plain_text_len)) {
        EVP_CIPHER_CTX_free(context);
        return false;
    }
    int final_block_len = 0;
    if (!EVP_EncryptFinal_ex(context, cipher_text + *cipher_text_len, &final_block_len)) {
        EVP_CIPHER_CTX_free(context);
        return false;
    }
    *cipher_text_len += final_block_len;

    EVP_CIPHER_CTX_free(context);
    return true;
}

/*! @param plain_text Buffer that must at least be cipher_text_len + cipher_block_size big
 * */
bool mk_evp_decrypt(const unsigned char *cipher_text,
                    const int cipher_text_len,
                    unsigned char *plain_text,
                    int *plain_text_len,
                    const EVP_CIPHER *cipher,
                    unsigned char *key,
                    unsigned char *iv) {
    EVP_CIPHER_CTX *context = EVP_CIPHER_CTX_new();
    if (!context) {
        return false;
    }
    if (!EVP_DecryptInit_ex(context, cipher, NULL, key, iv)) {
        EVP_CIPHER_CTX_free(context);
        return false;
    }

    *plain_text_len = 0;
    if (!EVP_DecryptUpdate(context, plain_text, plain_text_len, cipher_text, cipher_text_len)) {
        EVP_CIPHER_CTX_free(context);
        return false;
    }
    int final_block_size = 0;
    if (!EVP_DecryptFinal_ex(context, plain_text + *plain_text_len, &final_block_size)) {
        EVP_CIPHER_CTX_free(context);
        return false;
    }
    *plain_text_len += final_block_size;

    EVP_CIPHER_CTX_free(context);
    return true;
}

struct file_memory_map_meta {
    int file_desc;
    struct stat file_info;
};

/*! @param digest Must be big engough to hold at least EVP_MAX_MD_SIZE
 * */
bool mk_evp_digest(const unsigned char *text,
                   const size_t text_len,
                   unsigned char *digest,
                   unsigned int *digest_len,
                   const EVP_MD *digest_type) {
    EVP_MD_CTX *context = EVP_MD_CTX_create();
    if (!context) {
        return false;
    }
    if (!EVP_DigestInit_ex(context, digest_type, NULL)) {
        return false;
    }
    if (!EVP_DigestUpdate(context, text, text_len)) {
        return false;
    }
    if (!EVP_DigestFinal_ex(context, digest, digest_len)) {
        return false;
    }

    EVP_MD_CTX_destroy(context);

    return true;
}


void open_file_memory_mapped_read(char *file_path,
                                  void **file_memory,
                                  struct file_memory_map_meta *meta) {
    meta->file_desc = open(file_path, O_RDONLY);
    if (meta->file_desc == -1) {
        fprintf(stderr, "Can't open read file %s", file_path);
        perror(" ");
        exit(EXIT_FAILURE);
    }

    if (stat(file_path, &meta->file_info) != 0) {
        perror("Can't get source file infos");
        exit(EXIT_FAILURE);
    }
    void *source_mem = mmap(NULL, meta->file_info.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, meta->file_desc, 0);
    if (source_mem == MAP_FAILED) {
        perror("Mapping read file failed");
        exit(EXIT_FAILURE);
    }
    *file_memory = source_mem;
}

void open_file_memory_mapped_write(char *file_path,
                                   void **file_memory,
                                   struct file_memory_map_meta *meta,
                                   size_t size) {
    meta->file_desc = open(file_path, O_TRUNC | O_CREAT | O_RDWR, 744);
    if (meta->file_desc == -1) {
        fprintf(stderr, "Can't open write file %s", file_path);
        perror(" ");
        exit(EXIT_FAILURE);
    }

    if (stat(file_path, &meta->file_info) != 0) {
        perror("Can't get source file infos");
        exit(EXIT_FAILURE);
    }
    void *source_mem = mmap(NULL, size, PROT_WRITE, MAP_FILE | MAP_SHARED, meta->file_desc, 0);
    if (source_mem == MAP_FAILED) {
        perror("Mapping write file failed");
        exit(EXIT_FAILURE);
    }
    *file_memory = source_mem;
}

void close_file_memory_mapped(void **file_memory, struct file_memory_map_meta *meta) {
    munmap(*file_memory, meta->file_info.st_size);
    close(meta->file_desc);
}

void create_key_iv_from_file(char *key_iv_path, unsigned char **key, unsigned char **iv, const EVP_CIPHER *cipher) {
    FILE *f = fopen(key_iv_path, "rb");
    if (!f) {
        fprintf(stderr, "Could not open file %s", key_iv_path);
        perror(" ");
        exit(EXIT_FAILURE);
    }

    *key = malloc(EVP_CIPHER_key_length(cipher));
    if (*key == NULL) {
        perror("Could not alloc for key");
        exit(EXIT_FAILURE);
    }

    if (EVP_CIPHER_iv_length(cipher) != 0) {
        *iv = malloc(EVP_CIPHER_iv_length(cipher));
        if (*iv == NULL) {
            perror("Could not alloc for iv");
            exit(EXIT_FAILURE);
        }
    } else {
        *iv = NULL;
    }
    if (fread(*key, 1, EVP_CIPHER_key_length(cipher), f) != EVP_CIPHER_key_length(cipher)) {
        fprintf(stderr, "Error while reading key\n");
        exit(EXIT_FAILURE);
    }
    if (*iv != NULL) {
        if (fread(*iv, 1, EVP_CIPHER_iv_length(cipher), f) != EVP_CIPHER_iv_length(cipher)) {
            fprintf(stderr, "Error while reading iv\n");
            exit(EXIT_FAILURE);
        }
    }

    fclose(f);
}

unsigned char *permutate_key(unsigned char *key, unsigned corrupt_byte_pos) {
    key[corrupt_byte_pos] = key[corrupt_byte_pos] + 1; //although in DES, we could reduce to half because of parity bit

    return key;
}

bool is_pdf(unsigned char *data) {
    unsigned char pdf_start[] = {"%PDF"};
    unsigned char pdf_end[] = {"%%EOF"};

    return !memcmp(pdf_start, data, sizeof(pdf_start) - 1); //TODO check pdf_end, but cutaway the padding
}

void decrypt_mode(char *cipher_text_path,
                  char *plain_text_path,
                  char *key_iv,
                  unsigned corrupt_byte_pos,
                  char *cipher) {
    OpenSSL_add_all_algorithms();//Needed for older versions to use EVP_get_cipherbyname
    const EVP_CIPHER *evp_cipher = EVP_get_cipherbyname(cipher);
    EVP_cleanup(); //cleanup for OpenSSL_add_all_algorithms
    if (evp_cipher == NULL) {
        fprintf(stderr, "Cipher %s not found\n", cipher);
        exit(EXIT_FAILURE);
    }


    void *cipher_text_mem;
    struct file_memory_map_meta cipher_text_meta;
    open_file_memory_mapped_read(cipher_text_path,
                                 &cipher_text_mem, &cipher_text_meta);

    void *plain_text_mem;
    struct file_memory_map_meta plain_text_meta;
    open_file_memory_mapped_write(plain_text_path,
                                  &plain_text_mem, &plain_text_meta, cipher_text_meta.file_info.st_size);

    if (chmod(plain_text_path, cipher_text_meta.file_info.st_mode) != 0) {
        perror("Can't copy file permissions");
    }
    if (lseek(plain_text_meta.file_desc, cipher_text_meta.file_info.st_size - 1 + EVP_CIPHER_block_size(evp_cipher),
              SEEK_SET) == -1) {
        perror("Can't seek to new end of destination file");
    }
    unsigned char dummy = 0;
    if (write(plain_text_meta.file_desc, &dummy, 1) == -1) {
        perror("Couldn't write dummy byte");
    }

    unsigned char *key;
    unsigned char *iv;
    create_key_iv_from_file(key_iv, &key, &iv, evp_cipher);


    //now lets try the keys
    const unsigned key_len = EVP_CIPHER_key_length(evp_cipher);
    int plain_len = 0;

    bool decrypt_return = mk_evp_decrypt(cipher_text_mem,
                                         cipher_text_meta.file_info.st_size, plain_text_mem, &plain_len, evp_cipher,
                                         key, iv);
    while (!decrypt_return || !is_pdf(plain_text_mem)) {
        fprintf(stderr, "Key 0x");
        for (unsigned i = 0; i < key_len; ++i) {
            fprintf(stderr, "%02X", key[i]);
        }

        fprintf(stderr, " didn't catch it trying the next one\n");
        plain_len = 0;
        decrypt_return = mk_evp_decrypt(cipher_text_mem,
                                        cipher_text_meta.file_info.st_size, plain_text_mem, &plain_len, evp_cipher,
                                        permutate_key(key, corrupt_byte_pos), iv);
    }
    if (ftruncate(plain_text_meta.file_desc, plain_len) != 0) {
        perror("Trimming of final plain text failed");
        exit(EXIT_FAILURE);
    }

    free(key);
    free(iv);
    close_file_memory_mapped(&plain_text_mem, &plain_text_meta);
    close_file_memory_mapped(&cipher_text_mem, &cipher_text_meta);
}

void hash_mode(char *text_path,
               char *opt_hash_path,
               char *digest_name) {
    OpenSSL_add_all_digests();//Needed for older versions to use EVP_get_cipherbyname
    const EVP_MD *digest = EVP_get_digestbyname(digest_name);
    EVP_cleanup(); //cleanup for OpenSSL_add_all_algorithms
    if (digest == NULL) {
        fprintf(stderr, "Digest %s not found\n", digest_name);
        exit(EXIT_FAILURE);
    }
    void *text_mem;
    struct file_memory_map_meta text_meta;
    open_file_memory_mapped_read(text_path,
                                 &text_mem, &text_meta);


    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned hash_len = 0;
    mk_evp_digest(text_mem, text_meta.file_info.st_size, hash, &hash_len, digest);

    if (strlen(opt_hash_path) == 0) {
        for (unsigned i = 0; i < hash_len; ++i) {
            printf("%02X", hash[i]);
        }
        printf("\n");
    } else {
        FILE *out_file = fopen(opt_hash_path, "wb");
        if (!out_file) {
            perror("Could not open output file");
            exit(EXIT_FAILURE);
        }
        if (fwrite(hash, hash_len, 1, out_file) != 1) {
            fprintf(stderr, "Could not write the hash correctly\n");
            exit(EXIT_FAILURE);
        }
        fclose(out_file);
    }

    close_file_memory_mapped(&text_mem, &text_meta);
}

void encrypt_mode(char *plain_text_path,
                  char *cipher_text_path,
                  char *key_iv,
                  char *cipher) {
    OpenSSL_add_all_algorithms();//Needed for older versions to use EVP_get_cipherbyname
    const EVP_CIPHER *evp_cipher = EVP_get_cipherbyname(cipher);
    EVP_cleanup(); //cleanup for OpenSSL_add_all_algorithms
    if (evp_cipher == NULL) {
        fprintf(stderr, "Cipher %s not found\n", cipher);
        exit(EXIT_FAILURE);
    }

    void *plain_text_mem;
    struct file_memory_map_meta plain_text_meta;
    open_file_memory_mapped_read(plain_text_path,
                                 &plain_text_mem, &plain_text_meta);

    void *cipher_text_mem;
    struct file_memory_map_meta cipher_text_meta;
    open_file_memory_mapped_write(cipher_text_path,
                                  &cipher_text_mem, &cipher_text_meta, plain_text_meta.file_info.st_size);

    if (chmod(cipher_text_path, plain_text_meta.file_info.st_mode) != 0) {
        perror("Can't copy file permissions");
    }
    if (lseek(cipher_text_meta.file_desc, plain_text_meta.file_info.st_size + EVP_CIPHER_block_size(evp_cipher),
              SEEK_SET) == -1) {
        perror("Can't seek to new end of destination file");
    }
    unsigned char dummy = 0;
    if (write(cipher_text_meta.file_desc, &dummy, 1) == -1) {
        perror("Couldn't write dummy byte");
    }

    unsigned char *key;
    unsigned char *iv;
    create_key_iv_from_file(key_iv, &key, &iv, evp_cipher);

    int cipher_text_len = 0;
    if (!mk_evp_encrypt(plain_text_mem, plain_text_meta.file_info.st_size, cipher_text_mem, &cipher_text_len,
                        evp_cipher, key, iv)) {
        fprintf(stderr, "Encryption went wrong\n");
        exit(EXIT_FAILURE);
    }

    if (ftruncate(cipher_text_meta.file_desc, cipher_text_len) != 0) {
        perror("Trimming of final plain text failed");
        exit(EXIT_FAILURE);
    }
    free(key);
    free(iv);
    close_file_memory_mapped(&plain_text_mem, &plain_text_meta);
    close_file_memory_mapped(&cipher_text_mem, &cipher_text_meta);
}

int main(int argc, char *argv[]) {
    enum mode {
        none, decrypt, encrypt, hash
    } mode = none;
    char in_path[512];
    memset(in_path, '\0', sizeof(in_path));
    char out_path[512];
    memset(out_path, '\0', sizeof(out_path));
    char key_path[512];
    memset(key_path, '\0', sizeof(key_path));
    char cipher[512];
    memset(cipher, '\0', sizeof(cipher));
    unsigned corrupt_byte_pos = -1;

    int flag;
    while ((flag = getopt(argc, argv, "deh i:o:c:k:b:")) != -1) {
        switch (flag) {
            case 'e':
                mode = encrypt;
                break;
            case 'd':
                mode = decrypt;
                break;
            case 'h':
                mode = hash;
                break;
            case 'i':
                strncpy(in_path, optarg, sizeof(in_path) - 1);
                break;
            case 'o':
                strncpy(out_path, optarg, sizeof(out_path) - 1);
                break;
            case 'k':
                strncpy(key_path, optarg, sizeof(key_path) - 1);
                break;
            case 'c':
                strncpy(cipher, optarg, sizeof(cipher) - 1);
                break;
            case 'b':
                errno = 0;
                corrupt_byte_pos = strtol(optarg, NULL, 10);
                if (errno != 0) {
                    perror("Could not read byte position, assuming key is ok");
                    corrupt_byte_pos = -1;
                }
                break;
            default:
                return EXIT_FAILURE;
                break;
        }
    }

    switch (mode) {
        case decrypt:
            decrypt_mode(in_path, out_path, key_path, corrupt_byte_pos, cipher);
            break;
        case encrypt:
            encrypt_mode(in_path, out_path, key_path, cipher);
            break;
        case hash:
            hash_mode(in_path, out_path, cipher);
            break;
        case none:
        default:
            fprintf(stderr, "No mode was specified\n");
            printf("Usage %s -<MODE> -<PARAMETERS>\n", argv[0]);
            printf("\t<MODE>:\n");
            printf("\t\t e Encrypt aka Aufgabe 3\n");
            printf("\t\t d Decrypt aka Aufgabe 1\n");
            printf("\t\t h Hash aka Aufgabe 2\n");
            printf("\t<PARAMETERS>: \n");
            printf("\t\t i Input file path\n");
            printf("\t\t o Output file path, optional for hash mode\n");
            printf("\t\t k Key/IV file path, optional for hash mode\n");
            printf("\t\t c EVP Cipher/Digest to be used\n");
            printf("\t\t b Corrupt byte position, counted from 0, optional for hash mode\n");
            exit(EXIT_FAILURE);
            break;
    }

    return EXIT_SUCCESS;
}

答案 1 :(得分:0)

好吧,我想我明白了。

我将在这里复制我的两个程序示例:

AES_CTR_128(不含执行副总裁):

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

struct ctr_state {
  unsigned int num;
  unsigned char ivec[AES_BLOCK_SIZE];
  unsigned char ecount[AES_BLOCK_SIZE];
};

void init_ctr(struct ctr_state *state, const unsigned char iv[16]) {
  state->num = 0;
  memset(state->ecount, 0, 16);
  memset(state->ivec + 8, 0, 8);
  memcpy(state->ivec, iv, 8);
}

void fencrypt(char* read, char* write, const unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;
  AES_KEY key;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  struct ctr_state state;
  unsigned char *iv = (unsigned char *)"0123456789012345";

  readFile = fopen(read,"rb");
  writeFile = fopen(write,"wb");

  fwrite(iv, 1, AES_BLOCK_SIZE, writeFile);

  AES_set_encrypt_key(enc_key, 128, &key);
  init_ctr(&state, iv);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);

    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);

    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

void fdecrypt(char* read, char* write, const unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;
  AES_KEY key;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  unsigned char iv[AES_BLOCK_SIZE];
  struct ctr_state state;

  readFile=fopen(read,"rb");
  writeFile=fopen(write,"wb");

  fread(iv, 1, AES_BLOCK_SIZE, readFile);

  AES_set_encrypt_key(enc_key, 128, &key);
  init_ctr(&state, iv);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);

    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

int main(int argc, char *argv[]) {
 unsigned char *secret = (unsigned char *)"0123456789012345";

 fencrypt("encme.txt", "enced.enc", secret);
 fdecrypt("enced.enc", "unenced.txt", secret);
}

一切经典,如另一个例子。 IV(或nonce)是常量,以使调试更容易(不要这样做)。

在我的代码下面用EVP:

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


struct ctr_state {
  EVP_CIPHER_CTX* cipher;
  unsigned int num;
  unsigned char ivec[AES_BLOCK_SIZE];
  unsigned char ecount[AES_BLOCK_SIZE];
};

static void AES_ctr128_inc(unsigned char *counter) {
  unsigned char* cur_pos;

  for (cur_pos = counter + 15; cur_pos >= counter; cur_pos--) {
    (*cur_pos)++;
    if (*cur_pos != 0) {
      break;
    }
  }
}

void AES_ctr128_EVPencrypt(EVP_CIPHER_CTX* cipher, const unsigned char *in, unsigned char *out,
  const unsigned long length,
  unsigned char counter[AES_BLOCK_SIZE],
  unsigned char ecount_buf[AES_BLOCK_SIZE],
  unsigned int *num) {

  int nb;
  unsigned int n;
  unsigned long l=length;

  n = *num;

  while (l--) {
    if (n == 0) {
      EVP_EncryptUpdate(cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE);
      AES_ctr128_inc(counter);
    }
    *(out++) = *(in++) ^ ecount_buf[n];
    n = (n+1) % AES_BLOCK_SIZE;
  }

  *num=n;
}

void init_ctr(struct ctr_state *state, unsigned char iv[16], unsigned char* key) {
  state->num = 0;
  memset(state->ecount, 0, 16);
  memset(state->ivec + 8, 0, 8);
  memcpy(state->ivec, iv, 8);
  state->cipher = EVP_CIPHER_CTX_new();
  EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ecb(), NULL, key, NULL);
}

void fencrypt(char* read, char* write, unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  struct ctr_state state;
  unsigned char *iv = (unsigned char *)"0123456789012345";

  readFile = fopen(read,"rb");
  writeFile = fopen(write,"wb");

  fwrite(iv, 1, AES_BLOCK_SIZE, writeFile);

  init_ctr(&state, iv, enc_key);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);

    AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num);

    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

void fdecrypt(char* read, char* write, unsigned char* enc_key) {

  FILE *readFile;
  FILE *writeFile;

  int bytes_read;
  unsigned char indata[AES_BLOCK_SIZE];
  unsigned char outdata[AES_BLOCK_SIZE];
  unsigned char iv[AES_BLOCK_SIZE];
  struct ctr_state state;

  readFile = fopen(read,"rb");
  writeFile = fopen(write,"wb");

  fread(iv, 1, AES_BLOCK_SIZE, readFile);

  init_ctr(&state, iv, enc_key);

  while(1) {
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile);

    AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num);

    fwrite(outdata, 1, bytes_read, writeFile);
    if (bytes_read < AES_BLOCK_SIZE) {
      break;
    }
  }

  fclose(writeFile);
  fclose(readFile);
}

int main(int argc, char *argv[]) {
 unsigned char *secret = (unsigned char *)"0123456789012345";

 fencrypt("encme.txt", "enced.enc", (unsigned char*)secret);
 fdecrypt("enced.enc", "unenced.txt", secret);
}

所以我基本上复制了AES_ctr_encrypt函数以使用EVP,并对其进行调整。

它对我有用,因为我可以使用这两种实现来加密/解密同一个程序。

欢迎提出意见。 问题仍然是EVP中的aes_ctr_128是什么?以及如何使用它? 我想我已经重新发明了轮子。

答案 2 :(得分:0)

在{EVP_aes_N_ctr()}中,计数器状态保存在{ctx-> iv}中,被视为128个大端整数,从分配给Init函数的IV开始计算加密块。该计数器告诉下一个要加密/解密的加密块的编号。这适用于随机访问,即如果在加密块边界上,则可以在* Update调用之前手动计算ctx-> iv。从密码运算符返回至少一个字节后,计数器将更新以保存要处理的下一个加密块。如果需要字节级随机访问,则还有{ctx-> num}可以将字节偏移量保存到当前加密块中。为此,必须先启动该块,即必须发出{ctx-> num}等于零的密码op,才能正确设置内部状态。

下面是一个愚蠢但仍有效的示例,说明如何将CTR模式用于随机访问解密。

#include <openssl/evp.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>

static void add_be_128(uint8_t *ctr, ptrdiff_t delta) {
        for (int n = 15; delta != 0 && n >= 0; n--) {
                // The risk of overflow can safely be neglected.                
                ptrdiff_t nval = ctr[n] + delta;
                ctr[n] = nval & 0xff;
                delta = nval >> 8;
        }
}

int main() {
     uint8_t in[] =
          "0123456789abcdeffedcba9876543210fedcba98765432100123456789abcdef";
     uint8_t out[64];
     int sz;
     uint8_t cmp[33];
     uint8_t key[] = {
          0xde, 0xad, 0xbe, 0xef, 0xc0, 0x01, 0xd0, 0x0d,
          0xde, 0xad, 0xbe, 0xef, 0xc0, 0x01, 0xd0, 0x0d
     };
     uint8_t iv[] = {
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
     };

     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
     EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv);
     EVP_CIPHER_CTX_set_padding(ctx, 0);
     EVP_EncryptUpdate(ctx, out, &sz, in, sizeof in);
     EVP_EncryptFinal_ex(ctx, out + sz, &sz);
     EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv);
     EVP_CIPHER_CTX_set_padding(ctx, 0);
     add_be_128(ctx->iv, 1);
     EVP_DecryptUpdate(ctx, cmp, &sz, out + 16, 32);
     EVP_DecryptFinal_ex(ctx, cmp + sz, &sz);
     cmp[32] = 0;
     printf("%s %d\n", cmp, strncmp(in + 16, cmp, 32));
     EVP_DecryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv);
     EVP_CIPHER_CTX_set_padding(ctx, 0);
     int new = ctx->num + 8;
     add_be_128(ctx->iv, (new - 1) / 16);
     ctx->num = 0;
     EVP_DecryptUpdate(ctx, cmp, &sz, out, 1);
     ctx->num = new;
     EVP_DecryptUpdate(ctx, cmp, &sz, out + 8, 32);
     EVP_DecryptFinal_ex(ctx, cmp + sz, &sz);
     cmp[32] = 0;
     printf("%s %d\n", cmp, strncmp(in + 8, cmp, 32));
}