OpenSSL을 이용한 AES-CCM-128 암복호화 함수

반응형

OpenSSL이 제공하는 기능을 이용하여 다음과 같이 AES-CCM-128 암호화 및 복호화 함수를 만들 수 있습니다.

 

#include <stdint.h>
#include <string.h>
#include "openssl/evp.h"


#define AES_CCM_128_TAG_LEN (16) ///< AES-CCM-128 암호화 Tag 바이트열 길이
#define AES_CCM_128_KEY_LEN (16) ///< AES-CCM-128 키 바이트열 길이
#define AES_CCM_128_NONCE_LEN (12) ///< AES-CCM-128 Nonce 바이트열 길이


/**
 * @brief AES-CCM-128 암호화를 수행한다.
 * @param[in] plaintext 암호화할 평문 (Null 전달 시 EVP_EncryptUpdate()에서 암호화에 실패한다)
 * @param[in] plaintext_len plaintext의 길이
 * @param[in] key AES_CCM_128_KEY_LEN 길이의 암호화 키 (Null 전달 시 EVP_EncryptInit_ex()에서 설정에 실패한다)
 * @param[in] nonce AES_CCM_128_NONCE_LEN 길이의 Nonce N (Null 전달 시 EVP_EncryptInit_ex()에서 설정에 실패한다)
 * @param[out] ciphertext_and_tag_len {암호문||tag}의 길이가 반환될 변수 포인터
 * @return {암호문||tag}
 * @retval NULL: 실패
 */
uint8_t * AES_CCM_128_Encrypt(
  const uint8_t *plaintext,
  size_t plaintext_len,
  const uint8_t *key,
  const uint8_t *nonce
  int *ciphertext_and_tag_len)
{
  int len;
  int ciphertext_len;
  EVP_CIPHER_CTX *ctx = NULL;
  uint8_t *ciphertext_and_tag = (uint8_t *)malloc(plaintext_len + AES_CCM_128_TAG_LEN);
  if ((ciphertext_and_tag) &&
      (ctx = EVP_CIPHER_CTX_new()) && // 컨텍스트 초기화
      (EVP_EncryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL) == 1) && // 암호화 동작 초기화
      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, AES_CCM_128_NONCE_LEN, NULL) == 1) && // Nonce(IV) 길이 설정
      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, AES_CCM_128_TAG_LEN, NULL) == 1) && // Tag 길이 설정
      (EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce) == 1) && // 암호화키와 Nonce 설정
      (EVP_EncryptUpdate(ctx, NULL, &len, NULL, plaintext_len) == 1) && // 평문 길이 설정
      (EVP_EncryptUpdate(ctx, ciphertext_and_tag, &len, plaintext, plaintext_len) == 1) && // 암호화 수행 (len에 암호문의 길이가 저장됨)
      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, AES_CCM_128_TAG_LEN, ciphertext_and_tag + len) == 1)) { // Tag 획득
    *ciphertext_and_tag_len = len + AES_CCM_128_TAG_LEN;
  } else {
    if (ciphertext_and_tag) {
      free(ciphertext_and_tag);
      ciphertext_and_tag = NULL;
    }
  }
  if (ctx) {
    EVP_CIPHER_CTX_free(ctx);
  }
  return ciphertext_and_tag;
}


/**
 * @brief AES-CCM-128 복호화를 수행한다.
 * @param[in] ciphertext_and_tag "Ciphertext || Tag" 바이트열 (Null 전달 불가 - tag 값 접근 시 segmentation fault 발생)
 * @param[in] ciphertext_and_tag_len ciphertext_and_tag 바이트열의 길이
 * @param[in] key AES_CCM_128_KEY_LEN 길이의 복호화 키 (Null 전달 시 EVP_DecryptInit_ex()에서 설정에 실패한다)
 * @param[in] nonce AES_CCM_128_NONCE_LEN 길이의 Nonce N (Null 전달 시 EVP_DecryptInit_ex()에서 설정에 실패한다)
 * @param[out] plaintext_len 복호화된 데이터의 길이가 반환될 변수 포인터 
 * @return 복호화된 데이터 바이트열
 * @retval NULL: 실패
 */
uint8_t * AES_CCM_128_Decrypt(
  uint8_t *ciphertext_and_tag,
  size_t ciphertext_and_tag_len,
  const uint8_t *key,
  const uint8_t *nonce,
  int *plaintext_len)
{
  EVP_CIPHER_CTX *ctx = NULL;
  int len, ciphertext_len = (int)ciphertext_and_tag_len - AES_CCM_128_TAG_LEN;
  int decrypted_len = ciphertext_len;
  uint8_t *ciphertext = ciphertext_and_tag;
  uint8_t *tag = ciphertext_and_tag + ciphertext_len;
  uint8_t *plaintext = (uint8_t *)malloc(decrypted_len);
  if ((plaintext) &&
      (ctx = EVP_CIPHER_CTX_new()) && // 컨텍스트 초기화
      (EVP_DecryptInit_ex(ctx, EVP_aes_128_ccm(), NULL, NULL, NULL) == 1) && // 복호화 동작 초기화
      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, AES_CCM_128_NONCE_LEN, NULL) == 1) && // Nonce(IV) 길이 설정
      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, AES_CCM_128_TAG_LEN, tag) == 1) && // Tag 설정
      (EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce) == 1) && // Key 및 Nonce 설정
      (EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len) == 1) && // 암호문 길이 설정
      (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) == 1)) { // 복호화 수행
    *plaintext_len = decrypted_len;
  } else {
    if (plaintext) {
      free(plaintext);
      plaintext = NULL;
    }
  }
  if (ctx) {
    EVP_CIPHER_CTX_free(ctx);
  }
  return res;
}

 

댓글

Designed by JB FACTORY