OpenSSL을 이용한 AES-CCM-128 암복호화 함수
- 개발/OpenSSL
- 2022. 3. 4.
반응형
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;
}
'개발 > OpenSSL' 카테고리의 다른 글
OpenSSL - 다운로드 및 빌드 방법(x64/x86 리눅스 플랫폼) (0) | 2020.01.31 |
---|