Crypto API (Crypto API)

Linux 커널 Crypto API, 암호화 알고리즘, AES-NI 하드웨어 가속.

관련 표준: FIPS 197 (AES), FIPS 180-4 (SHA), FIPS 198-1 (HMAC), NIST SP 800-38A~F (블록 암호 운용 모드), FIPS 140-3 (암호 모듈 인증) — 커널 Crypto API가 구현하는 암호화 알고리즘 표준입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

Crypto API 개요

Linux 커널의 Crypto API는 암호화 알고리즘을 커널 내에서 사용할 수 있도록 하는 통합 프레임워크입니다. 소프트웨어 구현과 하드웨어 가속(AES-NI, SHA Extensions 등)을 동일한 인터페이스로 제공합니다.

Crypto API 아키텍처 원리

Crypto API는 알고리즘 구현과 사용을 분리하는 프레임워크 패턴으로 설계되어 있습니다. 핵심 개념은 세 가지입니다:

Crypto API 핵심 구조 사용자 IPsec, dm-crypt, kTLS tfm (Transform) 알고리즘 인스턴스+키 request 데이터+IV+콜백 algorithm SW 또는 HW 구현 알고리즘 선택 (Priority 기반) aesni_intel(400) > aes_generic(100) → H/W가 있으면 자동 선택
Crypto API: 사용자 → Transform(인스턴스) → Request(작업) → Algorithm(구현)

알고리즘 조합 (Template) 원리

Crypto API의 강력한 특성 중 하나는 템플릿 기반 알고리즘 조합입니다. "cbc(aes)"에서 cbc는 운용 모드 템플릿이고 aes는 기본 블록 암호입니다. 커널은 이를 재귀적으로 해석하여:

/* 알고리즘 이름 해석 예시 */
/* "authenc(hmac(sha256),cbc(aes))" 는 다음과 같이 분해됩니다:
 *   authenc ← 인증+암호화 템플릿
 *     hmac  ← MAC 템플릿
 *       sha256 ← 해시 알고리즘
 *     cbc   ← 블록 암호 모드 템플릿
 *       aes ← 블록 암호 알고리즘
 *
 * 이 구조 덕분에 새 블록 암호(예: SM4)를 추가하면
 * 기존 모든 템플릿(cbc, gcm, xts 등)과 자동 조합됩니다
 */
💡

Fallback 메커니즘: H/W 가속기가 특정 키 크기나 입력 크기를 지원하지 못하면, 자동으로 priority가 낮은 S/W 구현으로 fallback됩니다. 이는 CRYPTO_ALG_NEED_FALLBACK 플래그와 crypto_alloc_*의 type/mask 매개변수로 제어됩니다.

알고리즘 유형

유형API예시
대칭 암호 (Cipher)crypto_skcipherAES-CBC, AES-CTR, ChaCha20
AEADcrypto_aeadAES-GCM, ChaCha20-Poly1305
해시 (Hash)crypto_shash / crypto_ahashSHA-256, SHA-3, BLAKE2
키 합의 (KPP)crypto_kppECDH, DH
난수 생성 (RNG)crypto_rngDRBG, Jitter RNG
압축crypto_compLZ4, ZSTD, Deflate

해시 사용 예제

#include <crypto/hash.h>

static int calc_sha256(const u8 *data, unsigned int len, u8 *digest)
{
    struct crypto_shash *tfm;
    SHASH_DESC_ON_STACK(desc, tfm);
    int ret;

    tfm = crypto_alloc_shash("sha256", 0, 0);
    if (IS_ERR(tfm))
        return PTR_ERR(tfm);

    desc->tfm = tfm;
    ret = crypto_shash_digest(desc, data, len, digest);
    crypto_free_shash(tfm);
    return ret;
}

대칭 암호 예제

#include <crypto/skcipher.h>

struct crypto_skcipher *tfm;
struct skcipher_request *req;
struct scatterlist sg;

tfm = crypto_alloc_skcipher("cbc(aes)", 0, 0);
crypto_skcipher_setkey(tfm, key, key_len);

req = skcipher_request_alloc(tfm, GFP_KERNEL);
sg_init_one(&sg, data, data_len);
skcipher_request_set_crypt(req, &sg, &sg, data_len, iv);

/* 암호화 */
crypto_skcipher_encrypt(req);

/* 복호화 */
crypto_skcipher_decrypt(req);

skcipher_request_free(req);
crypto_free_skcipher(tfm);

하드웨어 가속

커널은 CPU의 암호화 명령어를 자동으로 활용합니다:

# 사용 가능한 암호화 알고리즘 확인
cat /proc/crypto | grep -E "^name|^driver|^priority"

# AES-NI가 활성화되었는지 확인
grep -o aes /proc/cpuinfo | head -1

Crypto API는 우선순위(priority)에 따라 알고리즘 구현을 선택합니다. 하드웨어 가속 구현의 우선순위가 소프트웨어보다 높으므로 자동으로 선택됩니다.

비동기 암호화 (Async Crypto)

커널 Crypto API는 비동기 처리를 지원하여 H/W 가속기와 효율적으로 연동합니다:

/* 비동기 대칭 암호 사용 예 */
struct crypto_skcipher *tfm;
struct skcipher_request *req;

tfm = crypto_alloc_skcipher("cbc(aes)", 0, 0);
req = skcipher_request_alloc(tfm, GFP_KERNEL);

/* 비동기 완료 콜백 설정 */
skcipher_request_set_callback(req,
    CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
    my_crypto_done, &result);

/* 키 설정 */
crypto_skcipher_setkey(tfm, key, key_len);

/* scatterlist로 데이터 설정 */
struct scatterlist sg;
sg_init_one(&sg, data, data_len);
skcipher_request_set_crypt(req, &sg, &sg, data_len, iv);

/* 비동기 암호화 수행 */
int err = crypto_skcipher_encrypt(req);
if (err == -EINPROGRESS || err == -EBUSY) {
    /* H/W 가속기에서 비동기 처리 중 → 콜백으로 완료 통보 */
    wait_for_completion(&result.completion);
    err = result.err;
}

AEAD (Authenticated Encryption)

AEAD는 암호화와 인증을 동시에 수행합니다 (예: AES-GCM, ChaCha20-Poly1305):

struct crypto_aead *aead;
struct aead_request *req;

aead = crypto_alloc_aead("gcm(aes)", 0, 0);
crypto_aead_setkey(aead, key, key_len);
crypto_aead_setauthsize(aead, 16);  /* 128-bit 인증 태그 */

req = aead_request_alloc(aead, GFP_KERNEL);
aead_request_set_crypt(req, src_sg, dst_sg, plaintext_len, iv);
aead_request_set_ad(req, aad_len);  /* 추가 인증 데이터 */

crypto_aead_encrypt(req);  /* 암호화 + MAC 생성 */

커널 난수 생성기

/* 커널 CSPRNG */
#include <linux/random.h>

/* 암호학적으로 안전한 난수 */
get_random_bytes(buf, nbytes);

/* 범위 내 난수 */
u32 val = get_random_u32_below(100);  /* 0~99 */

/* /dev/urandom의 커널 구현 */
/* ChaCha20 기반 DRBG, 하드웨어 엔트로피 소스 자동 수집 */

주요 알고리즘 카탈로그

카테고리알고리즘커널 이름용도
블록 암호AES-128/256aes디스크 암호화, IPsec
스트림 암호ChaCha20chacha20WireGuard, TLS
해시SHA-256sha256무결성 검증
해시BLAKE2bblake2b-256고속 해싱
MACHMAC-SHA256hmac(sha256)메시지 인증
AEADAES-GCMgcm(aes)TLS, IPsec
AEADChaCha20-Poly1305rfc7539(chacha20,poly1305)WireGuard
KDFHKDFhkdf(hmac(sha256))키 유도
압축LZ4, ZSTDlz4, zstdzswap, 파일시스템

사용자 공간 인터페이스 (AF_ALG)

/* 사용자 공간에서 커널 Crypto API 사용 */
int sockfd = socket(AF_ALG, SOCK_SEQPACKET, 0);

struct sockaddr_alg sa = {
    .salg_family = AF_ALG,
    .salg_type   = "hash",
    .salg_name   = "sha256",
};
bind(sockfd, (struct sockaddr *)&sa, sizeof(sa));

int opfd = accept(sockfd, NULL, NULL);
write(opfd, data, data_len);
read(opfd, digest, 32);  /* SHA-256 결과 */

H/W 가속 상세

# 사용 가능한 암호 알고리즘 확인 (H/W 가속 포함)
cat /proc/crypto | head -40
# name         : __cbc(aes)
# driver       : __cbc-aes-aesni  ← AES-NI 하드웨어 가속
# module       : aesni_intel
# priority     : 400             ← 높은 우선순위 = 자동 선택
# type         : skcipher

# AES-NI 지원 확인
grep aes /proc/cpuinfo | head -1
# flags : ... aes ...

커널은 동일 알고리즘의 여러 구현 중 priority가 가장 높은 것을 자동 선택합니다. H/W 가속기 드라이버가 로드되면 자동으로 소프트웨어 구현보다 우선 사용됩니다.

AES-NI 심화

AES-NI(Advanced Encryption Standard New Instructions)는 Intel이 2010년(Westmere)에 도입하고 AMD가 2011년(Bulldozer)부터 지원하는 AES 전용 하드웨어 명령어 세트입니다. 소프트웨어 AES 대비 3~10배 이상 빠른 처리량을 제공하며, 타이밍 기반 부채널 공격(cache-timing attack)에 근본적으로 면역입니다.

AES-NI 명령어 세트

AES-NI는 6개의 핵심 명령어로 구성됩니다. 모든 명령어는 128-bit XMM 레지스터에서 동작합니다:

명령어동작설명
AESENC1라운드 암호화ShiftRows → SubBytes → MixColumns → AddRoundKey
AESENCLAST마지막 라운드 암호화ShiftRows → SubBytes → AddRoundKey (MixColumns 생략)
AESDEC1라운드 복호화InvShiftRows → InvSubBytes → InvMixColumns → AddRoundKey
AESDECLAST마지막 라운드 복호화InvShiftRows → InvSubBytes → AddRoundKey
AESKEYGENASSIST키 확장 보조라운드 키 생성에 필요한 SubWord/RotWord 수행
AESIMC역 MixColumns복호화용 라운드 키 변환 (Equivalent Inverse Cipher)

AES 라운드 수: AES-128은 10라운드, AES-192는 12라운드, AES-256은 14라운드입니다. 각 라운드마다 AESENC 1개 명령어가 전체 라운드 변환을 수행합니다. 소프트웨어 구현에서는 S-Box 테이블 룩업, 행 시프트, 열 혼합, 키 합성을 별도로 수행하지만 AES-NI는 이를 단일 명령어로 처리합니다.

AES-NI 키 확장 (Key Expansion)

AES 키 확장은 원본 키(128/192/256비트)로부터 각 라운드에 사용할 라운드 키를 생성합니다. 커널의 aesni-intel_glue.c는 키 설정 시 모든 라운드 키를 미리 확장하여 crypto_aes_ctx에 저장합니다:

; AES-128 키 확장 (arch/x86/crypto/aesni-intel_asm.S 참고)
; 입력: %xmm0 = 원본 128-bit 키
; 출력: key_schedule[0..10] = 11개 라운드 키

_aesni_key_expansion_128:
    movaps  %xmm0, (%rdi)          ; key_schedule[0] = 원본 키

    ; 라운드 1: RCON = 0x01
    aeskeygenassist $0x01, %xmm0, %xmm1
    call    _key_expansion_128
    movaps  %xmm0, 0x10(%rdi)    ; key_schedule[1]

    ; 라운드 2: RCON = 0x02
    aeskeygenassist $0x02, %xmm0, %xmm1
    call    _key_expansion_128
    movaps  %xmm0, 0x20(%rdi)    ; key_schedule[2]
    ; ... 라운드 3~10까지 반복 (RCON: 0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36)

; 키 확장 보조 루틴
_key_expansion_128:
    pshufd  $0xff, %xmm1, %xmm1  ; AESKEYGENASSIST 결과를 broadcast
    shufps  $0x10, %xmm0, %xmm4  ; temp = [0,0,key[0],key[1]]
    pxor    %xmm4, %xmm0          ; key ^= temp
    shufps  $0x8c, %xmm0, %xmm4
    pxor    %xmm4, %xmm0          ; key ^= temp (cascade)
    pxor    %xmm1, %xmm0          ; key ^= RCON 결과
    ret
/* 커널 glue 코드: 키 설정 (arch/x86/crypto/aesni-intel_glue.c) */
static int aesni_set_key(struct crypto_aes_ctx *ctx,
                        const u8 *in_key, unsigned int key_len)
{
    if (!crypto_simd_usable())
        return aes_expandkey(ctx, in_key, key_len);  /* SW fallback */

    kernel_fpu_begin();
    aesni_set_key_common(ctx, in_key, key_len);  /* AES-NI 키 확장 */
    kernel_fpu_end();

    return 0;
}

/* struct crypto_aes_ctx — 확장된 키 스케줄 저장 */
struct crypto_aes_ctx {
    u32 key_enc[AES_MAX_KEYLENGTH_U32];  /* 암호화용 라운드 키 (60 u32) */
    u32 key_dec[AES_MAX_KEYLENGTH_U32];  /* 복호화용 라운드 키 (Inv) */
    u32 key_length;                      /* 16, 24, 또는 32 */
};

운용 모드별 AES-NI 구현

커널의 aesni_intel 모듈은 다양한 운용 모드를 AES-NI로 가속합니다. 각 모드의 병렬화 특성에 따라 성능이 크게 달라집니다:

모드커널 드라이버 이름병렬 처리특성
ECBecb-aes-aesni완전 병렬각 블록이 독립적이라 파이프라인 최대 활용
CBC 암호화cbc-aes-aesni직렬 (체인)이전 블록 암호문이 다음 블록 입력에 필요
CBC 복호화cbc-aes-aesni완전 병렬복호화는 모든 암호문 블록을 이미 알고 있으므로 병렬 가능
CTRctr-aes-aesni완전 병렬카운터 값이 독립적, IPsec/TLS에서 주력으로 사용
XTSxts-aes-aesni완전 병렬디스크 암호화(dm-crypt, LUKS) 표준 모드
GCMgcm-aes-aesniAES 병렬 + GHASHAEAD, PCLMULQDQ로 GHASH 가속 포함
; AES-CBC 암호화 — 직렬 체인 (arch/x86/crypto/aesni-intel_asm.S 참고)
; 각 블록: C[i] = AES_ENC(P[i] XOR C[i-1])
_aesni_enc_cbc:
    movups  (%r8), %xmm2           ; IV 로드
.Lcbc_enc_loop:
    movups  (%rsi), %xmm3          ; 평문 블록 로드
    pxor    %xmm2, %xmm3           ; P[i] XOR C[i-1] (CBC 체이닝)

    ; 10라운드 AES-128 암호화
    pxor    0x00(%rdi), %xmm3     ; AddRoundKey (라운드 0)
    aesenc  0x10(%rdi), %xmm3     ; 라운드 1
    aesenc  0x20(%rdi), %xmm3     ; 라운드 2
    ; ... 라운드 3~9 ...
    aesenclast 0xa0(%rdi), %xmm3  ; 라운드 10 (마지막)

    movups  %xmm3, (%rdx)          ; 암호문 저장
    movaps  %xmm3, %xmm2           ; C[i] → 다음 블록의 IV
    add     $16, %rsi
    add     $16, %rdx
    dec     %ecx
    jnz     .Lcbc_enc_loop

; AES-CTR 암호화 — 병렬 처리 (4블록 동시)
; C[i] = P[i] XOR AES_ENC(CTR+i)
_aesni_enc_ctr:
    ; 4개 카운터를 병렬로 준비
    movaps  %xmm0, %xmm1           ; CTR+0
    movaps  %xmm0, %xmm2           ; CTR+1 (inc)
    movaps  %xmm0, %xmm3           ; CTR+2 (inc)
    movaps  %xmm0, %xmm4           ; CTR+3 (inc)
    ; ... 각 카운터 증가 ...

    ; 4블록 동시 AES 라운드 (파이프라인 활용)
    pxor    (%rdi), %xmm1
    pxor    (%rdi), %xmm2
    pxor    (%rdi), %xmm3
    pxor    (%rdi), %xmm4
    aesenc  0x10(%rdi), %xmm1
    aesenc  0x10(%rdi), %xmm2
    aesenc  0x10(%rdi), %xmm3
    aesenc  0x10(%rdi), %xmm4
    ; ... 나머지 라운드 인터리빙 ...
💡

CTR/ECB 병렬화 핵심: 최신 CPU의 AES-NI 파이프라인은 4사이클 레이턴시, 1사이클 스루풋입니다. AESENC 명령어는 이전 명령어 완료를 기다리지 않고 파이프라인에 투입되므로, 독립적인 블록 4~8개를 인터리빙하면 이론적 최대 스루풋에 도달합니다.

AES-GCM과 PCLMULQDQ

AES-GCM(Galois/Counter Mode)은 TLS 1.3, IPsec에서 가장 널리 사용되는 AEAD 모드입니다. GCM은 CTR 모드 암호화와 GHASH 인증을 결합합니다. 커널은 AES-NI와 PCLMULQDQ(Carry-less Multiplication) 명령어를 함께 사용하여 두 연산을 모두 하드웨어로 가속합니다:

AES-GCM 동작 구조 Counter (J₀+i) AES-NI ENC AESENC × 10 rounds XOR ⊕ Plaintext Ciphertext AAD 추가 인증 데이터 XOR ⊕ H_i GHASH (GF(2¹²⁸)) PCLMULQDQ 가속 H_{i+1} GHASH Final ⊕ AES_ENC(J₀) Auth Tag (128-bit) ← 기밀성 ← 무결성/인증
AES-GCM: CTR 모드(기밀성) + GHASH(인증)를 병렬 수행
/* GHASH에서 GF(2^128) 곱셈 — PCLMULQDQ 하드웨어 가속 */
/* PCLMULQDQ: Carry-less multiplication (XOR 기반 다항식 곱셈) */
/* 소프트웨어로 구현하면 수십 사이클 걸리는 연산을 단일 명령어로 수행 */

/* arch/x86/crypto/ghash-clmulni-intel_asm.S 에서: */
; GHASH 블록 처리:
;   xmm0 = 현재 해시 값 (H_i)
;   xmm1 = 해시 키 (H = AES_ENC(0))
;   XOR → CLMUL → reduction → 새 해시 값
pxor    %xmm2, %xmm0              ; H_i XOR C_i (입력 블록과 XOR)

; Karatsuba 분해를 사용한 128×128 bit carry-less 곱셈
movdqa  %xmm0, %xmm3
pclmulqdq $0x00, %xmm1, %xmm3    ; a0 × b0 (하위 64-bit 곱)
movdqa  %xmm0, %xmm4
pclmulqdq $0x11, %xmm1, %xmm4    ; a1 × b1 (상위 64-bit 곱)
movdqa  %xmm0, %xmm5
pclmulqdq $0x10, %xmm1, %xmm5    ; a0 × b1 (교차 곱)
pclmulqdq $0x01, %xmm1, %xmm0    ; a1 × b0 (교차 곱)
pxor    %xmm5, %xmm0              ; 교차 곱 합산
; → GF(2^128) 환원(reduction): x^128 + x^7 + x^2 + x + 1

커널 GCM 구현 구조: generic-gcm-aesni 드라이버(priority 400)는 AES-NI + PCLMULQDQ를 결합한 최적화 구현입니다. CTR 암호화와 GHASH 인증을 인터리빙하여 AES 파이프라인 대기 시간 동안 GHASH를 수행합니다. 이 기법으로 GCM은 CTR 단독 대비 거의 추가 비용 없이 인증을 제공합니다.

aesni_intel 모듈 아키텍처

커널의 AES-NI 지원은 arch/x86/crypto/ 디렉터리에 위치하며, glue 코드와 어셈블리 구현으로 분리됩니다:

arch/x86/crypto/
├── aesni-intel_glue.c        ← C glue: 알고리즘 등록, 키 설정, FPU 관리
├── aesni-intel_asm.S          ← 어셈블리: ECB/CBC/CTR AES-NI 구현
├── aes_ctrby8_avx-x86_64.S   ← AVX 최적화 CTR (8블록 병렬)
├── aesni-intel_avx-x86_64.S   ← AVX 최적화 GCM
├── ghash-clmulni-intel_glue.c ← GHASH C glue
├── ghash-clmulni-intel_asm.S  ← GHASH PCLMULQDQ 어셈블리
└── aes-gcm-aesni-x86_64.S    ← AES-GCM 통합 최적화 (6.x+)
/* aesni-intel_glue.c — 주요 알고리즘 등록 구조 */

/* 1. 기본 블록 암호 (AES 단일 블록, 128-bit) */
static struct crypto_alg aesni_cipher_alg = {
    .cra_name           = "aes",
    .cra_driver_name    = "aes-aesni",
    .cra_priority       = 300,
    .cra_flags          = CRYPTO_ALG_TYPE_CIPHER,
    .cra_blocksize      = AES_BLOCK_SIZE,       /* 16 */
    .cra_u.cipher = {
        .cia_min_keysize = AES_MIN_KEY_SIZE,    /* 16 */
        .cia_max_keysize = AES_MAX_KEY_SIZE,    /* 32 */
        .cia_setkey      = aes_set_key,
        .cia_encrypt     = aesni_encrypt,
        .cia_decrypt     = aesni_decrypt,
    },
};

/* 2. skcipher 알고리즘들 (CBC, CTR, XTS, ECB) */
static struct skcipher_alg aesni_skciphers[] = {
    {   /* ECB — 병렬 처리, priority 400 */
        .base.cra_name          = "__ecb(aes)",
        .base.cra_driver_name   = "__ecb-aes-aesni",
        .base.cra_priority      = 400,
        .base.cra_flags         = CRYPTO_ALG_INTERNAL,
        .setkey                 = aesni_skcipher_setkey,
        .encrypt                = ecb_encrypt,
        .decrypt                = ecb_decrypt,
    },
    {   /* CBC */
        .base.cra_name          = "__cbc(aes)",
        .base.cra_driver_name   = "__cbc-aes-aesni",
        .base.cra_priority      = 400,
        .base.cra_flags         = CRYPTO_ALG_INTERNAL,
        .setkey                 = aesni_skcipher_setkey,
        .encrypt                = cbc_encrypt,
        .decrypt                = cbc_decrypt,
    },
    {   /* CTR */
        .base.cra_name          = "__ctr(aes)",
        .base.cra_driver_name   = "__ctr-aes-aesni",
        .base.cra_priority      = 400,
        .base.cra_flags         = CRYPTO_ALG_INTERNAL,
        .setkey                 = aesni_skcipher_setkey,
        .encrypt                = ctr_crypt,
        .decrypt                = ctr_crypt,    /* CTR 모드: enc == dec */
    },
    {   /* XTS — 디스크 암호화 (dm-crypt) */
        .base.cra_name          = "__xts(aes)",
        .base.cra_driver_name   = "__xts-aes-aesni",
        .base.cra_priority      = 401,
        .setkey                 = xts_aesni_setkey,
        .encrypt                = xts_encrypt,
        .decrypt                = xts_decrypt,
    },
};

/* 3. AEAD (GCM) */
static struct aead_alg aesni_aeads[] = {
    {
        .base.cra_name          = "__gcm(aes)",
        .base.cra_driver_name   = "__generic-gcm-aesni",
        .base.cra_priority      = 400,
        .base.cra_flags         = CRYPTO_ALG_INTERNAL,
        .setkey                 = gcm_setkey,
        .setauthsize            = gcm_setauthsize,
        .encrypt                = gcm_encrypt,
        .decrypt                = gcm_decrypt,
        .ivsize                 = GCM_AES_IV_SIZE,    /* 12 */
        .maxauthsize            = 16,
    },
};

INTERNAL 플래그와 SIMD 래퍼: __ecb-aes-aesni__ 접두사 알고리즘은 CRYPTO_ALG_INTERNAL 플래그를 가지며 직접 사용할 수 없습니다. simd_register_skciphers_compat()가 이를 감싸서 ecb-aes-aesni(접두사 없음)를 외부에 공개합니다. SIMD 래퍼는 process context에서 직접 SIMD 실행, softirq/hardirq에서는 cryptd kthread로 위임하여 컨텍스트 안전성을 보장합니다.

aesni_intel 모듈 초기화 흐름

/* aesni-intel_glue.c — 모듈 초기화 */
static int __init aesni_init(void)
{
    int err;

    /* 1. CPU가 AES-NI를 지원하는지 확인 */
    if (!boot_cpu_has(X86_FEATURE_AES))
        return -ENODEV;       /* AES-NI 미지원 → 모듈 로드 실패 → generic 사용 */

    /* 2. PCLMULQDQ 지원 여부 확인 (GCM GHASH에 필요) */
    if (!boot_cpu_has(X86_FEATURE_PCLMULQDQ))
        pr_info("PCLMULQDQ not available, GCM acceleration disabled\n");

    /* 3. 기본 AES 블록 암호 등록 */
    err = crypto_register_alg(&aesni_cipher_alg);
    if (err)
        return err;

    /* 4. skcipher 알고리즘 등록 (INTERNAL 버전) */
    err = crypto_register_skciphers(aesni_skciphers,
                                    ARRAY_SIZE(aesni_skciphers));

    /* 5. SIMD 래퍼 등록 (외부 공개 버전)
     * __ecb-aes-aesni → ecb-aes-aesni
     * __cbc-aes-aesni → cbc-aes-aesni
     * ... */
    err = simd_register_skciphers_compat(aesni_skciphers,
                                         ARRAY_SIZE(aesni_skciphers),
                                         aesni_simd_skciphers);

    /* 6. AEAD (GCM) 등록 */
    if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) {
        err = crypto_register_aeads(aesni_aeads,
                                    ARRAY_SIZE(aesni_aeads));
        err = simd_register_aeads_compat(aesni_aeads,
                                         ARRAY_SIZE(aesni_aeads),
                                         aesni_simd_aeads);
    }

    return 0;
}

VAES: AVX-512 벡터 AES

VAES(Vector AES)는 Intel Ice Lake(2019)부터 도입된 확장으로, AES-NI를 AVX-512 레지스터(512-bit)에서 동작하게 합니다. 128-bit XMM 대신 512-bit ZMM 레지스터를 사용하여 한 명령어로 4개 AES 블록을 동시에 처리합니다:

세대레지스터블록/명령어인터리빙 시 최대
AES-NI (SSE)XMM (128-bit)1블록~4블록 (파이프라인)
VAES + AVX2YMM (256-bit)2블록~8블록
VAES + AVX-512ZMM (512-bit)4블록~16블록
; VAES 예시: 512-bit ZMM 레지스터로 4블록 동시 AES 라운드
; 기존 AES-NI: aesenc %xmm_key, %xmm_data  → 1블록 (128-bit)
; VAES:       vaesenc %zmm_key, %zmm_data, %zmm_out → 4블록 (512-bit)

vaesenc %zmm1, %zmm0, %zmm0     ; 4블록 × 라운드 1
vaesenc %zmm2, %zmm0, %zmm0     ; 4블록 × 라운드 2
vaesenc %zmm3, %zmm0, %zmm0     ; 4블록 × 라운드 3
; ... 라운드 4~9 ...
vaesenclast %zmm11, %zmm0, %zmm0 ; 4블록 × 라운드 10 (마지막)

; 2개 ZMM을 인터리빙하면 8블록(1024-bit) 동시 처리:
vaesenc %zmm1, %zmm10, %zmm10   ; 블록 0~3
vaesenc %zmm1, %zmm11, %zmm11   ; 블록 4~7 (파이프라인 활용)
# VAES 지원 확인
grep vaes /proc/cpuinfo | head -1
# flags : ... vaes avx512f avx512bw ...

# 커널에서 VAES 가속 드라이버 확인
grep -E "aes.*(vaes|avx512)" /proc/crypto
# driver : xts-aes-vaes-avx512
# driver : gcm-aes-vaes-avx512

커널 VAES 지원: Linux 6.4+에서 arch/x86/crypto/aes-xts-avx-x86_64.S 등의 파일이 VAES+AVX-512 최적화를 제공합니다. VAES 드라이버는 AES-NI보다 높은 priority(500)로 등록되어, VAES 지원 CPU에서 자동 선택됩니다.

AES-NI를 활용하는 주요 커널 서브시스템

서브시스템사용 모드설정/모듈설명
dm-crypt / LUKS XTS(AES-256) CONFIG_DM_CRYPT 디스크 전체 암호화. cryptsetup으로 설정, 기본 aes-xts-plain64
IPsec (XFRM) GCM(AES-128/256) CONFIG_XFRM VPN 터널 암호화. ESP 프로토콜에서 AES-GCM이 기본 선택
kTLS GCM(AES-128/256) CONFIG_TLS 커널 내 TLS 오프로드. setsockopt(SOL_TLS)로 활성화
WireGuard CONFIG_WIREGUARD ChaCha20-Poly1305 사용 (AES-NI 미사용), 대신 SSSE3/AVX로 가속
eCryptfs CBC(AES-256) CONFIG_ECRYPT_FS 파일 단위 스택 암호화 파일시스템
fscrypt (ext4/f2fs) XTS(AES-256), CTS-CBC CONFIG_FS_ENCRYPTION 파일시스템 레벨 암호화. 파일명은 CTS-CBC, 데이터는 XTS
TCP-AO CMAC(AES-128) CONFIG_TCP_AO TCP 인증 옵션(RFC 5925). BGP 세션 보호
# dm-crypt에서 AES-NI 가속 확인 (LUKS 디스크)
cryptsetup luksDump /dev/sda2 | grep cipher
# cipher: aes-xts-plain64

# IPsec GCM 설정 예시 (strongSwan)
# ike=aes256gcm16-sha384-ecp384
# esp=aes256gcm16

# 현재 사용 중인 AES 구현 확인
cat /proc/crypto | grep -A4 "name.*: xts(aes)"
# name         : xts(aes)
# driver       : xts-aes-aesni
# module       : aesni_intel
# priority     : 401

# dm-crypt I/O 중 AES-NI CPU 사용 확인
perf top -e cycles -g -- -p $(pgrep -f kcryptd)
# aesni_xts_encrypt  ← AES-NI가 사용되고 있음

AES-NI 성능 특성

# tcrypt 모듈로 AES-NI 벤치마크
modprobe tcrypt mode=211 sec=2
# mode=211: CBC(AES) 암호화 벤치마크
# mode=215: GCM(AES) 벤치마크
# mode=210: ECB(AES) 벤치마크

dmesg | tail -30
# 참고 결과 예시 (Xeon, AES-NI):
# testing speed of async cbc(aes) (cbc-aes-aesni) encryption
# test  0 (128 bit key, 16 byte blocks):  18742341 operations in 2 seconds (299877456 bytes)
# test  1 (128 bit key, 64 byte blocks):  12952847 operations in 2 seconds (828982208 bytes)
# test  2 (128 bit key, 256 byte blocks):  7348142 operations in 2 seconds (1881124352 bytes)
# test  3 (128 bit key, 1024 byte blocks): 3128904 operations in 2 seconds (3203997696 bytes)
# test  4 (128 bit key, 1472 byte blocks): 2352961 operations in 2 seconds (3463558592 bytes)
# test  5 (128 bit key, 8192 byte blocks):  537420 operations in 2 seconds (4402585600 bytes)
#                                                          → ~2.2 GB/s (CBC, 단일 코어)
모드구현1KB 블록8KB 블록비고
ECB(AES-128)aes_generic~200 MB/s~200 MB/s순수 소프트웨어, 테이블 룩업
ECB(AES-128)aesni~3.5 GB/s~5.0 GB/sAES-NI, 4블록 인터리빙
CBC(AES-128) 암호화aesni~1.5 GB/s~2.2 GB/s직렬 체인 → 파이프라인 제한
CBC(AES-128) 복호화aesni~3.5 GB/s~4.8 GB/s복호화는 병렬 가능
CTR(AES-128)aesni~3.5 GB/s~5.0 GB/s카운터 병렬 → ECB급 성능
GCM(AES-128)aesni + clmul~3.0 GB/s~4.5 GB/sCTR + GHASH 인터리빙
XTS(AES-256)aesni~2.5 GB/s~3.5 GB/stweak 연산 + 2배 키 길이
성능 측정 시 주의:
  • 터보 부스트 영향 — AVX-512/VAES 사용 시 CPU가 다운클럭할 수 있어 순수 AES-NI 대비 의외로 느릴 수 있음. 워크로드 특성에 따라 판단 필요
  • kernel_fpu_begin() 오버헤드 — FPU 상태 저장/복원 비용(~수백 나노초)이 있어, 16바이트 단일 블록에서는 소프트웨어 구현이 더 빠를 수 있음
  • dm-crypt 실제 성능 — I/O 스택 오버헤드(scatterlist 구성, bio 처리)로 인해 tcrypt 벤치마크의 50~70% 수준이 일반적
  • NUMA 고려 — 암호화 워커 스레드가 데이터의 NUMA 노드와 다른 노드에서 실행되면 메모리 접근 지연으로 성능 저하

AES-NI 보안 이점: 부채널 공격 방어

소프트웨어 AES 구현은 S-Box 테이블 룩업을 사용하는데, 이는 cache-timing 부채널 공격에 취약합니다. 공격자는 AES 연산 중 캐시 접근 패턴을 관측하여 키를 추출할 수 있습니다:

/* 소프트웨어 AES — cache-timing 취약점 */
/* crypto/aes_generic.c 의 S-Box 테이블 룩업 */
static const u32 Te0[256] = { ... };  /* 1KB 룩업 테이블 */
static const u32 Te1[256] = { ... };
static const u32 Te2[256] = { ... };
static const u32 Te3[256] = { ... };

/* 키에 의존하는 인덱스로 테이블 접근 → 캐시 라인 접근 패턴 노출 */
s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ ...;
/* ↑ 공격자가 cache-line hit/miss 패턴으로 t0, t1 값을 추론 가능 */

/* AES-NI — 하드웨어 구현으로 근본 면역 */
/* AESENC 명령어는 CPU 내부 회로에서 S-Box를 수행하므로
 * 메모리 접근 패턴이 존재하지 않음 → cache-timing 공격 불가
 * 또한 연산 시간이 데이터에 무관하게 일정 (constant-time) */
aesenc %xmm_key, %xmm_state   /* 메모리 접근 없음, 고정 사이클 */
💡

FIPS 및 보안 권장: 보안에 민감한 환경에서는 aes_generic(소프트웨어)이 fallback으로 사용되지 않도록 aesni_intel 모듈이 확실히 로드되었는지 확인하세요. cat /proc/crypto | grep -B1 "aes-aesni"로 확인 가능합니다. 가상 머신(VM)에서는 호스트가 AES-NI CPUID 플래그를 패스스루하는지 확인해야 합니다(QEMU: -cpu host 또는 +aes).

Intel® QuickAssist Technology (Intel® QAT)

Intel QAT는 암호화(Cipher), 해싱(Hash), 압축/해제(Compression)를 하드웨어로 가속하는 기술입니다. 커널 Crypto API에 통합되어 IPsec, TLS(kTLS), dm-crypt, 스토리지 압축 등에서 CPU 오프로드를 제공합니다.

QAT 아키텍처

Application Kernel Crypto API / kTLS QAT Driver (qat_4xxx, qat_c62x) QAT HW Engine AE (Accel Engine) Ring Pairs Request/Response SRIOV VF (VM 직접 접근)
QAT 세대디바이스커널 드라이버지원 기능
QAT 1.x DH895xCC, C3xxx, C62x qat_dh895xcc, qat_c62x Crypto (AES, SHA, RSA), 압축 (Deflate)
QAT 2.0 (4xxx) 4xxx (Sapphire Rapids 내장) qat_4xxx Crypto + 압축 + SM2/SM3/SM4 + 향상된 RSA
QAT 연동 서비스 IPsec (ESP), kTLS (kernel TLS offload), dm-crypt, zswap 압축, DPDK crypto

QAT 커널 드라이버 설정

# QAT 디바이스 확인
lspci -d :4940   # QAT 4xxx (Sapphire Rapids)
lspci -d :37c8   # QAT C62x

# QAT 드라이버 로드
modprobe qat_4xxx       # 또는 qat_c62x, qat_dh895xcc
modprobe intel_qat       # 공통 프레임워크

# QAT 서비스 구성 (crypto, compression, 또는 둘 다)
# /etc/4xxx_dev0.conf 설정 파일
# ServicesEnabled = cy;dc  (cy=crypto, dc=data compression)

# QAT 인스턴스 확인
cat /sys/kernel/debug/qat_4xxx_0000:6b:00.0/fw_counters

# QAT가 crypto API에 등록된 알고리즘 확인
cat /proc/crypto | grep -A5 qat
# driver: qat_aes_cbc
# priority: 4001  ← S/W(aesni=400)보다 높음 → 자동 선택

# SRIOV VF 생성 (VM에 QAT 인스턴스 직접 할당)
echo 16 > /sys/bus/pci/devices/0000:6b:00.0/sriov_numvfs

QAT + IPsec 연동

# QAT IPsec offload — ESP 암호화/복호화를 QAT로 오프로드
# strongSwan 또는 Libreswan에서 자동 감지

# IPsec SA에서 QAT 사용 확인
ip xfrm state | grep -A2 enc
# enc aes-cbc ... offload dev qat_4xxx

# 성능 비교 (예시: AES-128-CBC + SHA-256)
# SW (AES-NI):     ~10 Gbps (CPU 100%)
# QAT offload:     ~40 Gbps (CPU ~20%)

# kTLS + QAT (커널 TLS 오프로드)
# KTLS_TX: 송신 데이터를 QAT가 AES-GCM 암호화
# 커널 5.3+ kTLS + QAT crypto 드라이버 자동 연동

QAT 사용 시 주의사항

QAT 운영 시 고려사항:
  • 소량 데이터 오버헤드 — 작은 패킷(64B 이하)에서는 QAT 오프로드 지연(~10μs)이 S/W AES-NI(~0.1μs)보다 클 수 있음. 일정 크기 이상에서만 이점
  • 큐 깊이 관리 — Ring pair가 포화되면 요청이 거절됨. 비동기 API(crypto_async) 사용 시 완료 콜백에서 에러 처리 필수
  • NUMA 인지 — QAT 디바이스가 특정 NUMA 노드에 연결. 다른 노드의 CPU가 사용하면 cross-node 메모리 접근으로 지연 증가
  • SR-IOV VF 개수 — VF당 할당되는 Ring pair 수가 줄어들어 VM별 처리량 제한. 적절한 VF 수 계획 필요
  • 펌웨어 의존성 — QAT 펌웨어 로딩이 필요하며, 커널 빌드 시 CONFIG_CRYPTO_DEV_QAT_4XXX 등 활성화 필요
  • fallback 메커니즘 — QAT 장애 시 자동으로 S/W 구현(aesni_intel)으로 fallback. priority 기반 선택

알고리즘 구현 가이드

커널 Crypto API에 새로운 알고리즘을 추가하려면 알고리즘 구조체 정의 → 콜백 구현 → 등록 → 테스트 벡터 추가의 과정을 거칩니다. 이 섹션에서는 각 단계를 상세히 다룹니다.

struct crypto_alg — 알고리즘 등록의 핵심

모든 암호화 알고리즘은 struct crypto_alg을 기반으로 등록됩니다. 이 구조체는 알고리즘의 메타데이터와 구현 콜백을 담는 컨테이너입니다:

/* include/linux/crypto.h */
struct crypto_alg {
    struct list_head     cra_list;        /* 내부 연결 리스트 */
    struct list_head     cra_users;       /* 이 알고리즘을 사용하는 tfm 목록 */

    u32                  cra_flags;       /* 알고리즘 속성 플래그 */
    unsigned int         cra_blocksize;   /* 블록 크기 (해시: 입력 블록, 암호: 블록 크기) */
    unsigned int         cra_ctxsize;     /* tfm 컨텍스트 크기 (키 상태 등) */
    unsigned int         cra_alignmask;   /* 데이터 정렬 요구사항 (0 = 1바이트 정렬) */

    int                  cra_priority;    /* 우선순위: 높을수록 먼저 선택 */
    unsigned int         cra_refcnt;      /* 참조 카운터 */

    char                 cra_name[128];       /* 알고리즘 정규 이름 ("sha256") */
    char                 cra_driver_name[128]; /* 드라이버 고유 이름 ("sha256-avx2") */

    const struct crypto_type *cra_type;    /* 알고리즘 유형 (skcipher, shash 등) */

    union {
        struct cipher_alg cipher;         /* 단일 블록 암호 (raw block cipher) */
    } cra_u;

    int (*cra_init)(struct crypto_tfm *tfm);    /* tfm 생성 시 초기화 */
    void (*cra_exit)(struct crypto_tfm *tfm);   /* tfm 해제 시 정리 */
    void (*cra_destroy)(struct crypto_alg *alg); /* 알고리즘 등록 해제 시 */

    struct module        *cra_module;     /* 소속 커널 모듈 */
};

주요 필드 상세

필드설명예시 값
cra_name사용자가 요청하는 정규 이름. 같은 이름의 여러 구현이 공존 가능"sha256", "aes"
cra_driver_name구현체를 고유하게 식별하는 이름"sha256-avx2", "aes-aesni"
cra_priority같은 cra_name 중 가장 높은 priority가 선택됨generic: 100, AES-NI: 400, QAT: 4001
cra_flags알고리즘 속성 비트마스크CRYPTO_ALG_ASYNC, CRYPTO_ALG_NEED_FALLBACK
cra_blocksize처리 단위 크기 (바이트)AES: 16, SHA-256: 64, 스트림 암호: 1
cra_ctxsizetfm당 할당할 private 컨텍스트 크기sizeof(struct my_alg_ctx)
cra_alignmask데이터 정렬 마스크. 예: 0x3이면 4바이트 정렬 필요보통 0 (S/W) 또는 0xf (H/W)

cra_flags 주요 플래그

플래그설명
CRYPTO_ALG_ASYNC비동기 알고리즘. 완료 콜백으로 결과를 통보. H/W 가속기에서 주로 사용
CRYPTO_ALG_NEED_FALLBACK일부 입력을 처리하지 못할 때 S/W fallback이 필요함을 표시
CRYPTO_ALG_INTERNAL내부 전용 알고리즘. AF_ALG(사용자 공간)에 노출되지 않음
CRYPTO_ALG_OPTIONAL_KEY키 설정이 선택사항 (예: 키 없는 해시)
CRYPTO_ALG_DEAD해제 진행 중인 알고리즘 (내부 사용)

shash 알고리즘 구현 (동기 해시)

가장 단순한 구현 유형인 shash (synchronous hash)로 시작합니다. 커널 소스의 crypto/sha256_generic.c를 참고한 전체 구현 예제입니다:

#include <crypto/internal/hash.h>
#include <linux/module.h>

#define MY_HASH_DIGEST_SIZE  32   /* 출력 해시 크기 (바이트) */
#define MY_HASH_BLOCK_SIZE   64   /* 내부 처리 블록 크기 */

/* tfm당 할당되는 컨텍스트 (키, 상태 등) */
struct my_hash_tfm_ctx {
    u8 key[32];
    bool has_key;
};

/* 요청(desc)당 할당되는 상태 (중간 해시 상태 등) */
struct my_hash_desc_ctx {
    u64 state[4];
    u8  buf[MY_HASH_BLOCK_SIZE];
    unsigned int buflen;
    u64 count;        /* 처리한 총 바이트 수 */
};

/* ① 해시 초기화: 내부 상태를 초기값으로 설정 */
static int my_hash_init(struct shash_desc *desc)
{
    struct my_hash_desc_ctx *dctx = shash_desc_ctx(desc);

    dctx->state[0] = 0x6a09e667f3bcc908ULL;  /* 초기 해시 값 */
    dctx->state[1] = 0xbb67ae8584caa73bULL;
    dctx->state[2] = 0x3c6ef372fe94f82bULL;
    dctx->state[3] = 0xa54ff53a5f1d36f1ULL;
    dctx->buflen = 0;
    dctx->count = 0;
    return 0;
}

/* ② 데이터 입력: 여러 번 호출될 수 있음 (스트리밍 해시) */
static int my_hash_update(struct shash_desc *desc,
                          const u8 *data, unsigned int len)
{
    struct my_hash_desc_ctx *dctx = shash_desc_ctx(desc);

    dctx->count += len;

    /* 버퍼에 남은 데이터와 새 데이터를 합쳐 블록 단위로 처리 */
    if (dctx->buflen + len < MY_HASH_BLOCK_SIZE) {
        memcpy(dctx->buf + dctx->buflen, data, len);
        dctx->buflen += len;
        return 0;
    }

    /* 블록 단위 처리 (실제 해시 압축 함수 호출) */
    /* my_hash_compress(dctx->state, data_blocks, nblocks); */
    return 0;
}

/* ③ 최종 해시 값 출력 */
static int my_hash_final(struct shash_desc *desc, u8 *out)
{
    struct my_hash_desc_ctx *dctx = shash_desc_ctx(desc);

    /* 패딩 적용 + 마지막 블록 처리 */
    /* my_hash_final_block(dctx); */

    /* 내부 상태를 출력 형식으로 변환 */
    memcpy(out, dctx->state, MY_HASH_DIGEST_SIZE);
    return 0;
}

/* ④ 선택사항: 한 번에 처리 (init+update+final 최적화) */
static int my_hash_digest(struct shash_desc *desc,
                          const u8 *data, unsigned int len,
                          u8 *out)
{
    my_hash_init(desc);
    my_hash_update(desc, data, len);
    return my_hash_final(desc, out);
}

/* ⑤ 선택사항: 키 설정 (HMAC, keyed hash용) */
static int my_hash_setkey(struct crypto_shash *tfm,
                          const u8 *key, unsigned int keylen)
{
    struct my_hash_tfm_ctx *ctx = crypto_shash_ctx(tfm);

    if (keylen > 32)
        return -EINVAL;

    memcpy(ctx->key, key, keylen);
    ctx->has_key = true;
    return 0;
}

/* ⑥ 상태 import/export (중간 상태 저장/복원, 선택사항) */
static int my_hash_export(struct shash_desc *desc, void *out)
{
    struct my_hash_desc_ctx *dctx = shash_desc_ctx(desc);
    memcpy(out, dctx, sizeof(*dctx));
    return 0;
}

static int my_hash_import(struct shash_desc *desc, const void *in)
{
    struct my_hash_desc_ctx *dctx = shash_desc_ctx(desc);
    memcpy(dctx, in, sizeof(*dctx));
    return 0;
}

/* ⑦ shash_alg 구조체 정의 */
static struct shash_alg my_hash_alg = {
    .init        = my_hash_init,
    .update      = my_hash_update,
    .final       = my_hash_final,
    .digest      = my_hash_digest,       /* 선택: 한 번에 처리 */
    .setkey      = my_hash_setkey,       /* 선택: keyed hash */
    .export      = my_hash_export,       /* 선택: 상태 저장 */
    .import      = my_hash_import,       /* 선택: 상태 복원 */
    .descsize    = sizeof(struct my_hash_desc_ctx),
    .statesize   = sizeof(struct my_hash_desc_ctx),
    .digestsize  = MY_HASH_DIGEST_SIZE,
    .base        = {
        .cra_name        = "myhash256",
        .cra_driver_name = "myhash256-generic",
        .cra_priority    = 100,
        .cra_blocksize   = MY_HASH_BLOCK_SIZE,
        .cra_ctxsize     = sizeof(struct my_hash_tfm_ctx),
        .cra_module      = THIS_MODULE,
    }
};

/* ⑧ 모듈 초기화/해제 */
static int __init my_hash_mod_init(void)
{
    return crypto_register_shash(&my_hash_alg);
}

static void __exit my_hash_mod_exit(void)
{
    crypto_unregister_shash(&my_hash_alg);
}

module_init(my_hash_mod_init);
module_exit(my_hash_mod_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example hash algorithm");
MODULE_ALIAS_CRYPTO("myhash256");
MODULE_ALIAS_CRYPTO("myhash256-generic");
💡

shash vs ahash: shash는 동기(synchronous) 해시로 구현이 단순합니다. S/W 구현은 shash를 사용하세요. ahash는 비동기(async) 해시로, H/W 가속기처럼 처리 완료까지 대기가 필요한 경우 사용합니다. 커널은 내부적으로 shash를 ahash로 자동 래핑하여 비동기 인터페이스에서도 사용할 수 있게 합니다.

shash 콜백 호출 흐름

shash 콜백 호출 순서 init() update() chunk 1 update() chunk N final() digest() = init+update+final ... (반복 가능) ...
스트리밍 해시: init → update × N → final | 한 번에: digest

skcipher 알고리즘 구현 (대칭 암호)

대칭 블록/스트림 암호의 구현입니다. struct skcipher_alg을 사용하여 등록합니다:

#include <crypto/internal/skcipher.h>
#include <linux/module.h>

#define MY_CIPHER_BLOCK_SIZE  16
#define MY_CIPHER_KEY_SIZE    32
#define MY_CIPHER_IV_SIZE     16

/* tfm 컨텍스트: 확장된 키 스케줄 등 */
struct my_cipher_ctx {
    u32 enc_key_sched[60];     /* 암호화 키 스케줄 */
    u32 dec_key_sched[60];     /* 복호화 키 스케줄 */
    unsigned int key_length;
};

/* 키 설정: 키 스케줄(라운드 키) 생성 */
static int my_cipher_setkey(struct crypto_skcipher *tfm,
                            const u8 *key, unsigned int keylen)
{
    struct my_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);

    if (keylen != 16 && keylen != 24 && keylen != 32)
        return -EINVAL;

    ctx->key_length = keylen;
    /* 키 스케줄 확장 수행 */
    /* my_expand_key(ctx->enc_key_sched, key, keylen); */
    /* my_expand_key_dec(ctx->dec_key_sched, key, keylen); */
    return 0;
}

/* 암호화: scatterlist 기반 데이터 처리 */
static int my_cipher_encrypt(struct skcipher_request *req)
{
    struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
    struct my_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);
    struct skcipher_walk walk;
    int err;

    /* skcipher_walk: scatterlist를 선형 버퍼로 순회하는 헬퍼 */
    err = skcipher_walk_virt(&walk, req, false);

    while (walk.nbytes) {
        unsigned int nbytes = walk.nbytes;
        const u8 *src = walk.src.virt.addr;
        u8 *dst = walk.dst.virt.addr;
        u8 *iv = walk.iv;

        /* 블록 단위로 CBC 암호화 수행 */
        while (nbytes >= MY_CIPHER_BLOCK_SIZE) {
            /* XOR plaintext with IV/previous ciphertext (CBC 모드) */
            crypto_xor_cpy(dst, src, iv, MY_CIPHER_BLOCK_SIZE);
            /* 블록 암호화 */
            /* my_encrypt_block(ctx->enc_key_sched, dst, dst); */
            iv = dst;
            src += MY_CIPHER_BLOCK_SIZE;
            dst += MY_CIPHER_BLOCK_SIZE;
            nbytes -= MY_CIPHER_BLOCK_SIZE;
        }

        /* IV 업데이트 (다음 walk 반복을 위해) */
        memcpy(walk.iv, iv, MY_CIPHER_IV_SIZE);
        err = skcipher_walk_done(&walk, nbytes);
    }

    return err;
}

/* 복호화 */
static int my_cipher_decrypt(struct skcipher_request *req)
{
    /* 암호화와 유사하나 역방향 처리 + 복호화 키 스케줄 사용 */
    /* ... */
    return 0;
}

static struct skcipher_alg my_cipher_alg = {
    .setkey      = my_cipher_setkey,
    .encrypt     = my_cipher_encrypt,
    .decrypt     = my_cipher_decrypt,
    .min_keysize = 16,
    .max_keysize = 32,
    .ivsize      = MY_CIPHER_IV_SIZE,
    .chunksize   = MY_CIPHER_BLOCK_SIZE,  /* 최소 처리 단위 */
    .walksize    = MY_CIPHER_BLOCK_SIZE,  /* walk 반복당 최소 크기 */
    .base        = {
        .cra_name        = "cbc(mycipher)",
        .cra_driver_name = "cbc-mycipher-generic",
        .cra_priority    = 100,
        .cra_flags       = CRYPTO_ALG_ASYNC,  /* H/W인 경우 */
        .cra_blocksize   = MY_CIPHER_BLOCK_SIZE,
        .cra_ctxsize     = sizeof(struct my_cipher_ctx),
        .cra_module      = THIS_MODULE,
    }
};

static int __init my_cipher_mod_init(void)
{
    return crypto_register_skcipher(&my_cipher_alg);
}

static void __exit my_cipher_mod_exit(void)
{
    crypto_unregister_skcipher(&my_cipher_alg);
}

module_init(my_cipher_mod_init);
module_exit(my_cipher_mod_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_CRYPTO("cbc(mycipher)");

skcipher_walk 상세

skcipher_walk은 scatterlist 기반의 분산된 데이터를 선형 버퍼처럼 순회하는 핵심 헬퍼입니다:

함수설명사용 맥락
skcipher_walk_virt()가상 주소 기반 walk 시작일반적인 S/W 구현
skcipher_walk_aead()AEAD용 walk 시작 (AAD + ciphertext)AEAD 구현
skcipher_walk_done()현재 chunk 완료, 다음으로 이동반복 루프에서 호출
skcipher_walk_complete()walk 종료 (남은 바이트가 블록 미만일 때)CTS 등 특수 모드
walk.nbytes현재 chunk에서 처리 가능한 바이트 수반복 조건
walk.src.virt.addr입력 데이터 포인터소스 읽기
walk.dst.virt.addr출력 데이터 포인터결과 쓰기
walk.iv현재 IV 포인터IV 업데이트

scatterlist와 zero-copy: 커널의 암호 데이터는 물리적으로 연속되지 않은 페이지에 분산되어 있을 수 있습니다. skcipher_walk은 이런 scatterlist를 임시 선형 버퍼로 매핑하거나, 이미 연속된 경우 직접 포인터를 제공하여 불필요한 복사를 방지합니다.

AEAD 알고리즘 구현

AEAD(Authenticated Encryption with Associated Data)는 암호화와 무결성 검증을 동시에 수행합니다. 구현은 struct aead_alg을 사용합니다:

#include <crypto/internal/aead.h>
#include <crypto/scatterwalk.h>

struct my_aead_ctx {
    struct crypto_skcipher *enc_tfm;  /* 내부 암호화 tfm */
    struct crypto_shash    *mac_tfm;  /* 내부 MAC tfm */
    unsigned int authsize;
};

static int my_aead_setkey(struct crypto_aead *tfm,
                          const u8 *key, unsigned int keylen)
{
    struct my_aead_ctx *ctx = crypto_aead_ctx(tfm);

    /* 키를 암호화용과 MAC용으로 분리 */
    struct crypto_authenc_keys keys;
    if (crypto_authenc_extractkeys(&keys, key, keylen))
        return -EINVAL;

    /* 각 내부 알고리즘에 키 설정 */
    crypto_skcipher_setkey(ctx->enc_tfm, keys.enckey, keys.enckeylen);
    crypto_shash_setkey(ctx->mac_tfm, keys.authkey, keys.authkeylen);

    return 0;
}

static int my_aead_setauthsize(struct crypto_aead *tfm,
                               unsigned int authsize)
{
    struct my_aead_ctx *ctx = crypto_aead_ctx(tfm);

    if (authsize > 16)
        return -EINVAL;

    ctx->authsize = authsize;
    return 0;
}

static int my_aead_encrypt(struct aead_request *req)
{
    struct crypto_aead *tfm = crypto_aead_reqtfm(req);
    struct my_aead_ctx *ctx = crypto_aead_ctx(tfm);

    /* 1단계: 평문을 암호화 (cbc, ctr 등) */
    /* 2단계: AAD + 암호문에 대해 MAC 계산 */
    /* 3단계: 인증 태그를 출력 끝에 추가 */

    /* req->src: [AAD | 평문]
     * req->dst: [AAD | 암호문 | 인증태그]
     * req->assoclen: AAD 길이
     * req->cryptlen: 평문 길이
     */
    return 0;
}

static int my_aead_decrypt(struct aead_request *req)
{
    /* 1단계: AAD + 암호문에 대해 MAC 재계산
     * 2단계: 수신된 인증 태그와 비교
     * 3단계: 일치하면 복호화, 불일치시 -EBADMSG 반환
     *
     * req->src: [AAD | 암호문 | 인증태그]
     * req->cryptlen: 암호문 + 인증태그 길이
     * 실제 암호문 길이 = req->cryptlen - ctx->authsize
     */
    return 0;
}

static int my_aead_init_tfm(struct crypto_aead *tfm)
{
    struct my_aead_ctx *ctx = crypto_aead_ctx(tfm);

    /* 내부 알고리즘 할당 */
    ctx->enc_tfm = crypto_alloc_skcipher("cbc(aes)", 0, 0);
    if (IS_ERR(ctx->enc_tfm))
        return PTR_ERR(ctx->enc_tfm);

    ctx->mac_tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
    if (IS_ERR(ctx->mac_tfm)) {
        crypto_free_skcipher(ctx->enc_tfm);
        return PTR_ERR(ctx->mac_tfm);
    }

    /* AEAD 요청 크기 설정: 내부 skcipher 요청도 포함 */
    crypto_aead_set_reqsize(tfm,
        sizeof(struct skcipher_request) +
        crypto_skcipher_reqsize(ctx->enc_tfm));

    return 0;
}

static void my_aead_exit_tfm(struct crypto_aead *tfm)
{
    struct my_aead_ctx *ctx = crypto_aead_ctx(tfm);

    crypto_free_skcipher(ctx->enc_tfm);
    crypto_free_shash(ctx->mac_tfm);
}

static struct aead_alg my_aead_alg = {
    .setkey      = my_aead_setkey,
    .setauthsize = my_aead_setauthsize,
    .encrypt     = my_aead_encrypt,
    .decrypt     = my_aead_decrypt,
    .init        = my_aead_init_tfm,
    .exit        = my_aead_exit_tfm,
    .ivsize      = 16,
    .maxauthsize = 16,
    .chunksize   = 16,
    .base        = {
        .cra_name        = "authenc(hmac(sha256),cbc(aes))",
        .cra_driver_name = "my-authenc-hmac-sha256-cbc-aes",
        .cra_priority    = 100,
        .cra_flags       = CRYPTO_ALG_ASYNC,
        .cra_blocksize   = 1,
        .cra_ctxsize     = sizeof(struct my_aead_ctx),
        .cra_module      = THIS_MODULE,
    }
};
AEAD scatterlist 레이아웃 주의:

AEAD의 scatterlist는 [AAD | 데이터 | 인증태그] 순서입니다. 암호화 시 req->cryptlen은 평문 길이이고 출력에 인증태그가 추가됩니다. 복호화 시 req->cryptlen암호문 + 인증태그 길이이므로, 실제 암호문 길이는 req->cryptlen - authsize입니다. 이 비대칭적 규약을 잘못 구현하면 데이터 손상이 발생합니다.

템플릿(Template) 구현

템플릿은 기존 알고리즘을 래핑하여 새로운 알고리즘을 자동 생성하는 메커니즘입니다. 예를 들어 cbc 템플릿은 임의의 블록 암호를 CBC 모드로 래핑합니다:

#include <crypto/internal/skcipher.h>

/* 템플릿 인스턴스의 컨텍스트 */
struct my_tmpl_instance_ctx {
    struct crypto_skcipher_spawn spawn;  /* 내부 알고리즘 참조 */
};

struct my_tmpl_tfm_ctx {
    struct crypto_skcipher *child;      /* 실제 내부 tfm */
};

/* 템플릿 인스턴스 생성: "mymode(aes)" 요청 시 호출 */
static int my_tmpl_create(struct crypto_template *tmpl,
                          struct rtattr **tb)
{
    struct skcipher_instance *inst;
    struct my_tmpl_instance_ctx *ictx;
    struct skcipher_alg_common *alg;
    u32 mask;
    int err;

    /* 내부 알고리즘 이름 추출 (예: "aes" from "mymode(aes)") */
    err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_SKCIPHER, &mask);
    if (err)
        return err;

    /* 인스턴스 할당 */
    inst = kzalloc(sizeof(*inst) + sizeof(*ictx), GFP_KERNEL);
    if (!inst)
        return -ENOMEM;

    ictx = skcipher_instance_ctx(inst);

    /* 내부 알고리즘(spawn) 바인딩 */
    err = crypto_grab_skcipher(&ictx->spawn,
                              skcipher_crypto_instance(inst),
                              crypto_attr_alg_name(tb[1]),
                              0, mask);
    if (err)
        goto err_free;

    alg = crypto_spawn_skcipher_alg_common(&ictx->spawn);

    /* 인스턴스 이름 조합: "mymode(inner_alg_name)" */
    err = snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
                   "mymode(%s)", alg->base.cra_name);

    err = snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
                   "mymode(%s)", alg->base.cra_driver_name);

    /* 내부 알고리즘 속성 상속 */
    inst->alg.base.cra_priority = alg->base.cra_priority;
    inst->alg.base.cra_blocksize = alg->base.cra_blocksize;
    inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
    inst->alg.base.cra_ctxsize = sizeof(struct my_tmpl_tfm_ctx);

    inst->alg.ivsize = alg->ivsize;
    inst->alg.min_keysize = alg->min_keysize;
    inst->alg.max_keysize = alg->max_keysize;

    /* 콜백 등록 */
    inst->alg.setkey  = my_tmpl_setkey;
    inst->alg.encrypt = my_tmpl_encrypt;
    inst->alg.decrypt = my_tmpl_decrypt;
    inst->alg.init    = my_tmpl_init_tfm;
    inst->alg.exit    = my_tmpl_exit_tfm;

    inst->free = my_tmpl_free_instance;

    /* 인스턴스 등록 */
    err = skcipher_register_instance(tmpl, inst);
    if (err)
        goto err_free;

    return 0;

err_free:
    kfree(inst);
    return err;
}

/* 템플릿 등록 */
static struct crypto_template my_tmpl = {
    .name   = "mymode",
    .create = my_tmpl_create,
    .module = THIS_MODULE,
};

static int __init my_tmpl_mod_init(void)
{
    return crypto_register_template(&my_tmpl);
}

static void __exit my_tmpl_mod_exit(void)
{
    crypto_unregister_template(&my_tmpl);
}

Spawn 메커니즘: crypto_spawn은 템플릿이 내부 알고리즘의 생명주기를 추적하도록 합니다. 내부 알고리즘이 해제되면 이를 사용하는 모든 템플릿 인스턴스도 자동으로 무효화됩니다. 이로써 모듈 unload 시 dangling reference를 방지합니다.

테스트 프레임워크

새 알고리즘을 추가하면 반드시 테스트 벡터를 함께 제공해야 합니다. 커널의 Crypto API는 알고리즘 등록 시 자동으로 자가 테스트를 수행합니다.

testmgr — 알고리즘 자동 검증

crypto/testmgr.c는 등록된 모든 알고리즘에 대해 Known Answer Test(KAT)를 수행합니다. 알고리즘이 crypto_register_*()로 등록되면 cryptomgr가 비동기적으로 테스트를 트리거합니다:

/* crypto/testmgr.c — 테스트 벡터 구조체 */

/* 해시 테스트 벡터 */
struct hash_testvec {
    const char *key;          /* HMAC 키 (선택) */
    const char *plaintext;    /* 입력 데이터 */
    const char *digest;       /* 기대 해시 값 */
    unsigned int psize;       /* 입력 길이 */
    unsigned int ksize;       /* 키 길이 */
};

/* 대칭 암호 테스트 벡터 */
struct cipher_testvec {
    const char *key;          /* 암호 키 */
    const char *iv;           /* 초기화 벡터 */
    const char *ptext;        /* 평문 */
    const char *ctext;        /* 기대 암호문 */
    unsigned int klen;        /* 키 길이 */
    unsigned int len;         /* 데이터 길이 */
};

/* AEAD 테스트 벡터 */
struct aead_testvec {
    const char *key;
    const char *iv;
    const char *assoc;        /* AAD (추가 인증 데이터) */
    const char *ptext;        /* 평문 */
    const char *ctext;        /* 암호문 + 인증태그 */
    unsigned int klen;
    unsigned int alen;        /* AAD 길이 */
    unsigned int plen;        /* 평문 길이 */
    unsigned int clen;        /* 암호문+태그 길이 */
};

테스트 벡터 추가 방법

새 알고리즘의 테스트 벡터를 추가하려면 두 파일을 수정합니다:

/* 1단계: crypto/testmgr.h — 테스트 벡터 데이터 정의 */

static const struct hash_testvec myhash256_tv_template[] = {
    {   /* 벡터 #1: 빈 입력 */
        .plaintext = "",
        .psize     = 0,
        .digest    = "\xe3\xb0\xc4\x42...",  /* 기대 해시 값 */
    },
    {   /* 벡터 #2: "abc" */
        .plaintext = "abc",
        .psize     = 3,
        .digest    = "\xba\x78\x16\xbf...",
    },
    {   /* 벡터 #3: 긴 입력 (NIST 표준 벡터 등) */
        .plaintext = "abcdbcdecdefdefg...",
        .psize     = 56,
        .digest    = "\x24\x8d\x6a\x61...",
    },
};

/* 2단계: crypto/testmgr.c — 테스트 벡터를 알고리즘에 연결 */

static const struct alg_test_desc alg_test_descs[] = {
    /* ... 기존 항목들 (알파벳순 정렬!) ... */
    {
        .alg  = "myhash256",
        .test = alg_test_hash,      /* 테스트 함수 유형 */
        .suite = {
            .hash = __VECS(myhash256_tv_template)
        }
    },
    /* ... */
};
알파벳순 정렬 필수:

alg_test_descs[] 배열은 알고리즘 이름의 알파벳순으로 정렬되어 있어야 합니다. 커널은 이진 탐색으로 테스트 항목을 찾으므로, 정렬이 어긋나면 테스트가 실행되지 않습니다.

tcrypt — 성능 벤치마크

crypto/tcrypt.c는 암호 알고리즘의 성능을 측정하는 커널 모듈입니다:

# tcrypt 모듈 로드 — mode 번호로 테스트 유형 선택
modprobe tcrypt mode=200    # SHA-256 속도 테스트
modprobe tcrypt mode=500    # AES-CBC 속도 테스트
modprobe tcrypt mode=211    # HMAC-SHA256 속도 테스트

# 결과 확인
dmesg | tail -50
# testing speed of async cbc(aes) encryption
# test 0 (128 bit key, 16 byte blocks): 1 operation in 1234 cycles (16 bytes)
# test 1 (128 bit key, 64 byte blocks): 1 operation in 2345 cycles (64 bytes)

# 모든 알고리즘 셀프 테스트 실행
modprobe tcrypt mode=0      # 전체 알고리즘 테스트

# 특정 알고리즘만 테스트 (sec 파라미터로 벤치마크 시간 조절)
modprobe tcrypt mode=500 sec=5   # 5초간 벤치마크
tcrypt mode테스트 대상
0모든 알고리즘 자가 테스트 (KAT)
200~215해시 속도 (SHA-1, SHA-256, MD5, BLAKE2 등)
300~399AEAD 속도 (AES-GCM, ChaCha20-Poly1305 등)
500~599skcipher 속도 (AES-CBC, AES-CTR, AES-XTS 등)

Kconfig / Makefile 통합

커널 트리에 새 알고리즘을 통합하려면 crypto/ 디렉터리의 Kconfig와 Makefile을 수정합니다:

# crypto/Kconfig — 알고리즘 빌드 옵션 추가

config CRYPTO_MYHASH256
    tristate "My Hash 256 algorithm"
    select CRYPTO_HASH           # 해시 프레임워크 의존성
    help
      This is an example hash algorithm producing a 256-bit digest.

config CRYPTO_MYCIPHER
    tristate "My Block Cipher (CBC mode)"
    select CRYPTO_SKCIPHER       # skcipher 프레임워크 의존성
    select CRYPTO_LIB_AES        # AES 라이브러리 의존성 (필요 시) 
    help
      My custom block cipher with CBC mode support.

# H/W 가속 구현인 경우
config CRYPTO_MYCIPHER_X86_64
    tristate "My Block Cipher (x86_64/AES-NI 가속)"
    depends on X86 && 64BIT
    select CRYPTO_MYCIPHER       # generic 구현 fallback용 
    select CRYPTO_SIMD           # SIMD 헬퍼 */
    help
      x86_64 AES-NI accelerated implementation.
# crypto/Makefile — 빌드 규칙 추가

obj-$(CONFIG_CRYPTO_MYHASH256)       += myhash256.o
obj-$(CONFIG_CRYPTO_MYCIPHER)        += mycipher.o
obj-$(CONFIG_CRYPTO_MYCIPHER_X86_64) += mycipher-x86_64.o

# 어셈블리 구현이 있는 경우
mycipher-x86_64-y := mycipher_glue.o mycipher-x86_64-asm.o

주요 Kconfig select 대상

의존성설명사용 경우
CRYPTO_HASH해시 프레임워크 (shash/ahash)해시 알고리즘
CRYPTO_SKCIPHER대칭 암호 프레임워크skcipher 알고리즘
CRYPTO_AEADAEAD 프레임워크AEAD 알고리즘
CRYPTO_SIMDSIMD 컨텍스트 관리 헬퍼SSE/AVX/NEON 사용 구현
CRYPTO_LIB_AESAES 키 스케줄 라이브러리AES 기반 구현
CRYPTO_LIB_SHA256SHA-256 라이브러리 함수SHA-256 기반 구현

MODULE_ALIAS_CRYPTO 매크로

/* 모듈 자동 로딩을 위한 alias 등록 */
MODULE_ALIAS_CRYPTO("myhash256");
MODULE_ALIAS_CRYPTO("myhash256-generic");

/* 커널이 "myhash256" 알고리즘을 요청하면:
 * 1. crypto_has_alg("myhash256") → 등록 안 됨
 * 2. request_module("crypto-myhash256") 호출
 * 3. MODULE_ALIAS_CRYPTO 매칭 → myhash256.ko 자동 로드
 * 4. module_init 실행 → crypto_register_shash()
 * 5. 이제 crypto_alloc_shash("myhash256") 성공
 */

cryptomgr와 알고리즘 탐색 메커니즘

cryptomgr(Crypto Manager)는 알고리즘 등록/해제/탐색을 관리하는 커널 서브시스템입니다:

알고리즘 탐색/등록 흐름 crypto_alloc_skcipher() "cbc(aes)" 요청 crypto_alg_lookup() 등록된 알고리즘 검색 Found! priority 최고 선택 crypto_larval 생성 대기 객체 등록 request_module() crypto-cbc + crypto-aes Template 매칭 cbc(X) → cbc + aes 인스턴스 자동 생성 cbc(aes) 등록 완료 testmgr 자동 검증 있음 없음
알고리즘 탐색 흐름: lookup → (없으면) larval → modprobe → template 매칭 → 인스턴스 생성 → 테스트

crypto_larval — 알고리즘 대기 객체

/* crypto/api.c — 알고리즘 탐색 핵심 로직 (간략화) */

struct crypto_alg *crypto_alg_mod_lookup(const char *name,
                                         u32 type, u32 mask)
{
    struct crypto_alg *alg;

    /* 1. 이미 등록된 알고리즘 검색 */
    alg = crypto_alg_lookup(name, type, mask);
    if (alg)
        return alg;

    /* 2. 모듈 자동 로드 시도 (MODULE_ALIAS_CRYPTO 매칭) */
    request_module("crypto-%s", name);

    /* 3. larval (대기 객체) 생성 — 비동기 등록 대기 */
    struct crypto_larval *larval = crypto_larval_alloc(name, type, mask);

    /* 4. cryptomgr에 알고리즘 탐색 요청 (kthread) */
    crypto_probing_notify(CRYPTO_MSG_ALG_REQUEST, larval);

    /* 5. 등록 완료 대기 (타임아웃 60초) */
    alg = crypto_larval_wait(larval);

    /* cryptomgr는 템플릿 매칭을 시도:
     * "cbc(aes)" → cbc 템플릿 + aes 알고리즘으로 분해
     * → cbc 템플릿의 create() 호출
     * → cbc(aes) 인스턴스 자동 생성 및 등록
     */

    return alg;
}

FIPS 모드와 자가 테스트

FIPS(Federal Information Processing Standards)는 미국 NIST(National Institute of Standards and Technology)가 발행하는 연방 정보 처리 표준입니다. 암호화 모듈의 보안 요구사항을 정의하며, 미국 연방 정부 기관은 물론 금융, 의료, 방위산업 등 규제 환경에서 필수적으로 요구됩니다. 리눅스 커널은 부트 파라미터를 통해 FIPS 모드를 활성화하여 인증된 암호 알고리즘만 사용하도록 강제할 수 있습니다.

FIPS 140 표준 개요

FIPS 140은 암호화 모듈의 보안 요구사항을 정의하는 NIST 표준으로, 현재 FIPS 140-3(ISO/IEC 19790:2012 기반)이 최신 버전입니다. FIPS 140-2는 2021년 9월 이후 신규 인증이 중단되었으나, 기존 인증은 유효합니다.

보안 레벨요구사항적용 예
Level 1승인된 알고리즘/함수 사용, 소프트웨어 전용 구현 허용리눅스 커널 Crypto API (소프트웨어)
Level 2Level 1 + 물리적 탬퍼 증거(tamper-evidence), 역할 기반 인증HSM(Hardware Security Module) 기본 등급
Level 3Level 2 + 물리적 탬퍼 저항(tamper-resistance), ID 기반 인증결제 단말기, 군사용 장비
Level 4Level 3 + 환경적 공격 방어 (전압, 온도 변조 감지)최고 보안 등급 하드웨어 모듈

리눅스 커널의 FIPS 인증: 커널 Crypto API는 FIPS 140-2/140-3 Level 1 인증을 대상으로 합니다. 소프트웨어 암호화 모듈로서 승인된 알고리즘의 올바른 구현과 자가 테스트를 증명합니다. RHEL, Ubuntu Pro 등 주요 엔터프라이즈 배포판에서 FIPS 인증을 획득하여 제공합니다.

주요 FIPS/NIST 암호화 표준

표준 번호명칭내용커널 대응
FIPS 140-3Security Requirements for Cryptographic Modules암호 모듈 보안 요구사항 (4단계)fips_enabled 전역 변수, 자가 테스트
FIPS 197Advanced Encryption Standard (AES)128/192/256비트 대칭 블록 암호aes_generic, aesni_intel
FIPS 180-4Secure Hash Standard (SHS)SHA-1, SHA-224/256/384/512sha256_generic, sha512_generic
FIPS 198-1HMAC해시 기반 메시지 인증 코드hmac 템플릿
FIPS 186-5Digital Signature Standard (DSS)RSA, ECDSA, EdDSA 전자서명rsa_generic, ecdsa_generic
FIPS 202SHA-3 StandardSHA3-224/256/384/512, SHAKE128/256sha3_generic
SP 800-38ABlock Cipher ModesECB, CBC, CFB, OFB, CTRecb, cbc, ctr 템플릿
SP 800-38DGCM ModeGalois/Counter Mode (인증 암호화)gcm 템플릿, gcm-aes-aesni
SP 800-38FKey WrapAES Key Wrap (KW, KWP)kw(aes)
SP 800-56A/BKey EstablishmentDH, ECDH 키 합의dh_generic, ecdh_generic
SP 800-90ADRBG결정론적 난수 생성기 (CTR, Hash, HMAC)drbg_pr_*, drbg_nopr_*
SP 800-132PBKDF비밀번호 기반 키 유도사용자 공간에서 주로 사용

FIPS 모드 활성화

FIPS 140-3 인증이 필요한 환경에서는 커널을 FIPS 모드로 부팅합니다. 이 모드에서는 모든 암호 알고리즘이 반드시 자가 테스트(KAT: Known Answer Test)를 통과해야 사용 가능합니다:

# ─── FIPS 모드 활성화 (부트 파라미터) ───
# GRUB 설정: /etc/default/grub
GRUB_CMDLINE_LINUX="fips=1"
# /boot 파티션이 별도인 경우 boot= 지정 필요
GRUB_CMDLINE_LINUX="fips=1 boot=/dev/sda1"

# GRUB 설정 반영
grub2-mkconfig -o /boot/grub2/grub.cfg

# ─── 현재 FIPS 모드 확인 ───
cat /proc/sys/crypto/fips_enabled
# 1 = FIPS 모드, 0 = 일반 모드

# sysctl로도 확인 가능
sysctl crypto.fips_enabled
# crypto.fips_enabled = 1

# FIPS 모드에서 알고리즘 등록 실패 시 로그
dmesg | grep -i fips
# alg: fips already enabled
# alg: self-tests for myhash256 (myhash256-generic) failed
# (result -2) → 테스트 벡터 불일치

FIPS 모드 전환 시 주의: FIPS 모드는 런타임에 동적으로 전환할 수 없습니다. 반드시 부트 파라미터 fips=1로 커널 초기화 시점에 활성화해야 합니다. 이는 부팅 초기 단계에서 암호 모듈 무결성 검증과 자가 테스트가 수행되어야 하기 때문입니다. /proc/sys/crypto/fips_enabled는 읽기 전용이며 쓰기가 불가합니다.

FIPS 모드 커널 내부 구현

커널 내부에서 FIPS 모드는 전역 변수 fips_enabled로 관리됩니다. 이 변수는 부팅 시 설정되며, 암호 서브시스템 전반에서 참조합니다:

/* crypto/fips.c — FIPS 모드 전역 상태 */
#include <linux/kernel.h>
#include <linux/sysctl.h>
#include <linux/export.h>

int fips_enabled;
EXPORT_SYMBOL_GPL(fips_enabled);

/* 부트 파라미터 "fips=1" 처리 */
static int __init fips_enable(char *str)
{
    fips_enabled = 1;
    pr_info("fips mode is enabled\n");
    return 1;
}
__setup("fips=", fips_enable);

/* /proc/sys/crypto/fips_enabled sysctl 등록 */
static struct ctl_table crypto_sysctl_table[] = {
    {
        .procname   = "fips_enabled",
        .data       = &fips_enabled,
        .maxlen     = sizeof(int),
        .mode       = 0444,            /* 읽기 전용 */
        .proc_handler = proc_dointvec,
    },
};

커널 코드 내에서 FIPS 모드를 확인하는 주요 인터페이스:

/* include/linux/fips.h */
#ifdef CONFIG_CRYPTO_FIPS
extern int fips_enabled;

/* FIPS 모드 확인 헬퍼 — 커널 코드에서 광범위하게 사용 */
static inline bool fips_enabled_check(void)
{
    return fips_enabled;
}
#else
#define fips_enabled 0
#endif

/* 사용 예: 비승인 알고리즘 차단 */
if (fips_enabled && !(alg->cra_flags & CRYPTO_ALG_FIPS_INTERNAL)) {
    /* FIPS 비승인 알고리즘 → 등록 거부 */
    return -ENOENT;
}

/* CONFIG_CRYPTO_FIPS 커널 설정 옵션 */
/*
 * Kconfig (crypto/Kconfig):
 * config CRYPTO_FIPS
 *     bool "FIPS 200 compliance"
 *     depends on (CRYPTO_ANSI_CPRNG || CRYPTO_DRBG) && CRYPTO_FIPS_VERSION
 *     help
 *       This option enables the FIPS boot option which is
 *       required if you want the system to operate in a
 *       FIPS 200 compliant manner.
 */

자가 테스트(Self-Test) 메커니즘

FIPS 140-3은 암호 모듈이 전원 투입 자가 테스트(Power-On Self-Test, POST)조건부 자가 테스트(Conditional Self-Test, CST)를 수행할 것을 요구합니다. 리눅스 커널에서는 crypto/testmgr.c가 이 역할을 담당합니다:

/* crypto/testmgr.c — 알고리즘 자가 테스트 핵심 구조 */

/* KAT(Known Answer Test) 테스트 벡터 구조체 */
struct cipher_testvec {
    const char *key;        /* 입력 키 */
    const char *iv;         /* 초기화 벡터 */
    const char *ptext;      /* 평문 (Known Input) */
    const char *ctext;      /* 암호문 (Known Answer) */
    unsigned int klen;      /* 키 길이 */
    unsigned int len;       /* 데이터 길이 */
    bool fips_skip;         /* FIPS 모드에서 건너뛸지 여부 */
};

/* 해시 테스트 벡터 */
struct hash_testvec {
    const char *key;        /* HMAC 키 (해시는 NULL) */
    const char *plaintext;  /* 입력 데이터 */
    const char *digest;     /* 기대 출력 (Known Answer) */
    unsigned int psize;     /* 평문 길이 */
    unsigned int ksize;     /* 키 길이 */
};

/* 알고리즘별 테스트 정의 — testmgr.c의 핵심 테이블 */
static const struct alg_test_desc alg_test_descs[] = {
    {
        .alg   = "cbc(aes)",
        .test  = alg_test_skcipher,
        .fips_allowed = 1,     /* FIPS 승인 알고리즘 */
        .suite = {
            .cipher = __VECS(aes_cbc_tv_template)
        }
    }, {
        .alg   = "ecb(aes)",
        .test  = alg_test_skcipher,
        .fips_allowed = 1,
        .suite = {
            .cipher = __VECS(aes_tv_template)
        }
    }, {
        .alg   = "sha256",
        .test  = alg_test_hash,
        .fips_allowed = 1,
        .suite = {
            .hash = __VECS(sha256_tv_template)
        }
    }, {
        .alg   = "hmac(sha256)",
        .test  = alg_test_hash,
        .fips_allowed = 1,
        .suite = {
            .hash = __VECS(hmac_sha256_tv_template)
        }
    },
    /* ... 수백 개의 알고리즘 테스트 정의 ... */
};

테스트 실행 흐름 — 알고리즘이 등록될 때 자동으로 호출됩니다:

/* crypto/testmgr.c — 자가 테스트 실행 흐름 */

/* 1. 알고리즘 등록 시 testmgr가 호출됨 */
int alg_test(const char *driver, const char *alg,
             u32 type, u32 mask)
{
    const struct alg_test_desc *test;
    int rc;

    /* alg_test_descs[] 테이블에서 알고리즘 검색 */
    test = find_test(alg);
    if (!test) {
        if (fips_enabled) {
            /* FIPS 모드: 테스트 없으면 → 등록 거부 */
            pr_err("alg: no test for %s (%s)\n", alg, driver);
            return -EINVAL;
        }
        /* 일반 모드: 테스트 없으면 → 건너뜀 (경고만) */
        return 0;
    }

    /* 2. FIPS 모드에서 비승인 알고리즘 차단 */
    if (fips_enabled && !test->fips_allowed) {
        pr_info("alg: %s not allowed in fips mode\n", alg);
        return -EINVAL;
    }

    /* 3. KAT(Known Answer Test) 실행 */
    rc = test->test(test, driver, type, mask);
    if (rc) {
        if (fips_enabled)
            panic("alg: self-tests for %s (%s) failed (rc=%d)\n",
                  alg, driver, rc);
        else
            pr_warn("alg: self-tests for %s (%s) failed (rc=%d)\n",
                    alg, driver, rc);
    }

    return rc;
}

FIPS 모드 panic: FIPS 모드에서 필수 암호 알고리즘의 자가 테스트가 실패하면 커널은 panic()을 호출하여 시스템을 즉시 중단합니다. 이는 FIPS 140-3의 "degraded mode 진입 금지" 요구사항을 충족하기 위한 것으로, 손상된 암호 모듈이 보안에 민감한 데이터를 처리하는 것을 원천 차단합니다.

FIPS 자가 테스트 유형

FIPS 140-3이 요구하는 자가 테스트는 크게 두 가지로 분류됩니다:

테스트 유형실행 시점목적커널 구현
POST (Power-On Self-Test) 부팅 시 / 모듈 로드 시 암호 알고리즘의 올바른 동작 확인 testmgr.c의 KAT 벡터 테스트
무결성 테스트 부팅 시 / 모듈 로드 시 암호 모듈 바이너리 변조 감지 HMAC-SHA256 기반 모듈 무결성 검증
CST (Conditional Self-Test) 특정 조건 발생 시 키 쌍 일관성, 난수 연속성 등 DRBG 연속 출력 비교, RSA 키 쌍 검증
KAT (Known Answer Test) 알고리즘 등록 시 알려진 입력→출력 쌍으로 정확성 검증 testmgr.h의 테스트 벡터
PCT (Pairwise Consistency Test) 비대칭 키 생성 시 공개키-개인키 쌍의 일관성 RSA/ECDSA 키 생성 후 서명-검증
/* KAT 실행 예: 대칭 암호(skcipher) 테스트 */
static int test_skcipher_vec(const char *driver,
                              const struct cipher_testvec *vec,
                              struct skcipher_request *req)
{
    /* 1. 키 설정 */
    crypto_skcipher_setkey(tfm, vec->key, vec->klen);

    /* 2. 알려진 평문으로 암호화 수행 */
    sg_init_one(&sg, buf, vec->len);
    memcpy(buf, vec->ptext, vec->len);
    skcipher_request_set_crypt(req, &sg, &sg, vec->len, iv);
    err = crypto_skcipher_encrypt(req);

    /* 3. 결과를 Known Answer와 비교 */
    if (memcmp(buf, vec->ctext, vec->len) != 0) {
        pr_err("encryption test failed for %s\n", driver);
        return -EINVAL;   /* KAT 실패 */
    }

    /* 4. 역방향(복호화) 테스트 */
    memcpy(buf, vec->ctext, vec->len);
    err = crypto_skcipher_decrypt(req);
    if (memcmp(buf, vec->ptext, vec->len) != 0) {
        pr_err("decryption test failed for %s\n", driver);
        return -EINVAL;   /* KAT 실패 */
    }

    return 0;  /* 테스트 통과 */
}

/* 조건부 자가 테스트 예: DRBG 연속 출력 비교 */
static int drbg_healthcheck(struct drbg_state *drbg,
                             unsigned char *buf)
{
    /* FIPS 140-3: 난수 생성기 연속 출력 비교 테스트 (CRNGT)
     * 연속된 두 출력 블록이 동일하면 → 난수 생성기 고장 */
    if (memcmp(buf, drbg->prev_output, drbg->len) == 0) {
        pr_err("drbg: continuous test failed\n");
        if (fips_enabled)
            panic("drbg: continuous random number test failed\n");
        return -EFAULT;
    }
    memcpy(drbg->prev_output, buf, drbg->len);
    return 0;
}

FIPS 승인/비승인 알고리즘

FIPS 모드에서는 NIST가 승인한 알고리즘만 사용할 수 있습니다. testmgr.cfips_allowed 플래그로 구분합니다:

분류FIPS 승인 (사용 가능)FIPS 비승인 (차단)
블록 암호 AES-128/192/256, 3DES (제한적) Blowfish, Twofish, Serpent, Camellia, CAST5/6, DES
운용 모드 ECB, CBC, CTR, GCM, CCM, XTS, KW
해시 SHA-1 (서명 검증만), SHA-224/256/384/512, SHA3-* MD4, MD5, RIPEMD-160
MAC HMAC-SHA-*, CMAC-AES, GMAC HMAC-MD5
비대칭 RSA (≥2048bit), ECDSA (P-256/384/521), EdDSA RSA <2048bit
키 합의 DH (≥2048bit), ECDH (P-256/384/521) DH <2048bit
난수 생성 CTR_DRBG, Hash_DRBG, HMAC_DRBG ANSI X9.31 PRNG (폐기됨)
키 유도 SP 800-108 KDF (HMAC/CMAC 기반)
💡

3DES 주의: NIST SP 800-131A Rev.2에 따라 3DES(Triple DES)는 2023년 이후 암호화에 사용이 금지(disallowed)되었으며, 복호화에만 제한적으로 허용됩니다. 새로운 구현에서는 반드시 AES를 사용하세요.

CRYPTO_ALG_FIPS_INTERNAL 플래그

커널은 CRYPTO_ALG_FIPS_INTERNAL 플래그로 FIPS 인증 경계(Boundary) 내의 알고리즘과 내부 도우미 알고리즘을 구분합니다:

/* include/linux/crypto.h */

/* FIPS 내부 전용 알고리즘 표시 — 사용자 공간에 노출되지 않음 */
#define CRYPTO_ALG_FIPS_INTERNAL    0x00020000

/*
 * 이 플래그가 설정된 알고리즘:
 *   - FIPS 모듈 경계 내부에서만 사용 가능
 *   - AF_ALG 소켓을 통한 사용자 공간 접근 차단
 *   - /proc/crypto에서 "internal : yes"로 표시
 *
 * 예: CTR 모드 DRBG의 내부 AES 인스턴스
 *     → 직접 사용자 접근은 차단, DRBG 내부에서만 사용
 */

/* AF_ALG에서 FIPS_INTERNAL 알고리즘 차단 */
static int alg_accept(struct sock *sk)
{
    struct crypto_alg *alg = ...;

    /* FIPS 모드: internal 플래그 알고리즘은 AF_ALG로 사용 불가 */
    if (alg->cra_flags & CRYPTO_ALG_FIPS_INTERNAL) {
        if (fips_enabled)
            return -ENOENT;
    }
    ...
}

/* 템플릿에서 FIPS_INTERNAL 전파 예 */
static int cbc_create(struct crypto_template *tmpl,
                      struct rtattr **tb)
{
    /* 내부 알고리즘(예: ecb(aes))은 FIPS_INTERNAL로 마킹 */
    inst->alg.base.cra_flags |= (alg->cra_flags & CRYPTO_ALG_FIPS_INTERNAL);
    ...
}

HMAC 무결성 검증

FIPS 140-3은 암호화 모듈의 바이너리 무결성을 부팅 시 검증할 것을 요구합니다. 리눅스에서는 HMAC-SHA256 기반의 무결성 검증을 수행합니다:

/* FIPS 무결성 검증 흐름 (배포판별 구현 상이) */

/*
 * ┌─────────────────────────────────────────────────────────────┐
 * │                  FIPS 모듈 무결성 검증                       │
 * ├─────────────────────────────────────────────────────────────┤
 * │                                                             │
 * │  [빌드 시]                                                  │
 * │   1. 커널 이미지(vmlinuz) 빌드                              │
 * │   2. HMAC-SHA256(vmlinuz) 계산                              │
 * │   3. .vmlinuz.hmac 파일에 저장                              │
 * │                                                             │
 * │  [부팅 시 - fips=1]                                        │
 * │   1. initramfs 내 fips-mode-setup 실행                     │
 * │   2. vmlinuz의 HMAC-SHA256 재계산                           │
 * │   3. .vmlinuz.hmac 파일과 비교                              │
 * │   4. 불일치 → 부팅 중단 (panic)                            │
 * │   5. 일치 → 정상 부팅 계속                                  │
 * │                                                             │
 * │  [모듈 로드 시]                                             │
 * │   1. crypto 관련 .ko 모듈 로드 요청                         │
 * │   2. 모듈의 HMAC-SHA256 재계산                              │
 * │   3. .모듈명.ko.hmac 파일과 비교                            │
 * │   4. 불일치 → 모듈 로드 거부                               │
 * │                                                             │
 * └─────────────────────────────────────────────────────────────┘
 */
# FIPS 무결성 파일 확인 (RHEL/CentOS 예시)
ls -la /boot/.vmlinuz-$(uname -r).hmac
# -r--------. 1 root root 33 Jan 15 12:00 /boot/.vmlinuz-6.x.hmac

# 커널 모듈 HMAC 파일
ls /lib/modules/$(uname -r)/.*.hmac
# .aesni-intel.ko.hmac
# .sha256_ssse3.ko.hmac
# .ghash-clmulni-intel.ko.hmac

# 수동 무결성 검증
sha256hmac /boot/vmlinuz-$(uname -r)
# 출력된 HMAC 값이 .vmlinuz-*.hmac 파일 내용과 일치해야 함

# FIPS 모듈 목록 확인 (dracut 기준)
cat /etc/dracut.conf.d/40-fips.conf
# add_dracutmodules+=" fips "

일반 모드 vs FIPS 모드 상세 비교

구분일반 모드FIPS 모드
자가 테스트 실패경고 로그, 알고리즘 사용 가능panic() — 시스템 중단
테스트 벡터 없음테스트 건너뜀알고리즘 등록 거부
비승인 알고리즘사용 가능등록 거부 또는 CRYPTO_ALG_FIPS_INTERNAL로 제한
무결성 검증없음커널/모듈 HMAC-SHA256 무결성 검증
난수 생성기모든 RNG 사용 가능DRBG(SP 800-90A)만 허용, CRNGT 필수
키 길이 제한없음RSA ≥2048bit, DH ≥2048bit 등 최소 길이 강제
AF_ALG 접근모든 알고리즘FIPS 승인 + 비-internal 알고리즘만
알고리즘 우선순위priority 값 기준FIPS 승인 알고리즘 우선 (비승인 fallback 차단)
/proc/crypto모든 알고리즘 표시FIPS 승인 알고리즘만 외부 노출
커널 설정CONFIG_CRYPTOCONFIG_CRYPTO + CONFIG_CRYPTO_FIPS

주요 배포판의 FIPS 지원

배포판FIPS 인증활성화 방법비고
RHEL 8/9 FIPS 140-2 / 140-3 인증 획득 fips-mode-setup --enable dracut FIPS 모듈, crypto-policies 연동
Ubuntu 20.04+ (Pro) FIPS 140-2 인증 (Pro 전용) ua enable fips 별도 FIPS 커널 패키지 설치
SUSE SLES 15 FIPS 140-2 인증 yast fips 또는 부트 파라미터 SUSE 자체 FIPS 모듈
Oracle Linux 8/9 RHEL 기반 FIPS 인증 fips-mode-setup --enable RHEL 호환 구현
Amazon Linux 2023 FIPS 140-3 인증 진행 fips-mode-setup --enable AWS GovCloud 환경 지원
# ─── RHEL/CentOS: FIPS 모드 활성화 ───
# 1. FIPS 모드 설정 (재부팅 필요)
fips-mode-setup --enable
# Setting system policy to FIPS
# FIPS mode will be enabled.
# Please reboot the system for the setting to take effect.

# 2. 재부팅 후 확인
fips-mode-setup --check
# FIPS mode is enabled.

# 3. crypto-policies 확인 (FIPS 전용 정책)
update-crypto-policies --show
# FIPS

# ─── crypto-policies의 FIPS 정책 효과 ───
# - OpenSSL: FIPS provider만 활성화
# - NSS: FIPS 모드 활성화
# - GnuTLS: FIPS 호환 알고리즘만 허용
# - SSH: FIPS 승인 암호/MAC/키교환만 허용
# - libgcrypt: FIPS 모드 활성화

# ─── Ubuntu Pro: FIPS 활성화 ───
# 1. Ubuntu Advantage 토큰 연결
sudo ua attach <token>
# 2. FIPS 활성화 (별도 커널 설치)
sudo ua enable fips
# 3. 재부팅
sudo reboot

FIPS 모드가 영향을 미치는 커널 서브시스템

FIPS 모드 활성화는 Crypto API뿐 아니라 암호화를 사용하는 모든 커널 서브시스템에 영향을 미칩니다:

서브시스템FIPS 모드 영향구체적 변경
dm-crypt FIPS 승인 암호만 허용 aes-xts-plain64 사용, serpent/twofish 차단
IPsec (XFRM) FIPS 승인 알고리즘만 SA 설정 가능 AES-GCM, AES-CBC + HMAC-SHA256 허용, Blowfish/CAST 차단
IKE (strongSwan/Libreswan) FIPS 정책 연동 DH group 14(2048bit) 이상만 허용
TLS (kTLS) FIPS 승인 cipher suite만 AES-128/256-GCM, AES-256-CCM 허용
네트워크 (TCP-MD5) MD5 기반 인증 차단 FIPS 모드에서 TCP-MD5 옵션 비활성화
디스크 암호화 (LUKS) FIPS 호환 파라미터 강제 PBKDF2-SHA256 + AES-256-XTS, argon2id 차단
난수 (/dev/random) DRBG 기반으로 전환 SP 800-90A DRBG, CRNGT(연속 출력 비교) 활성화
모듈 서명 SHA-256/384/512만 허용 CONFIG_MODULE_SIG_HASH에 SHA-1 사용 불가
커널 키링 FIPS 승인 알고리즘으로 키 연산 RSA ≥2048bit, ECDSA P-256+ 인증서만 허용

CAVP/CMVP 인증 프로세스

FIPS 140-3 인증 획득은 두 단계로 진행됩니다:

/*
 * FIPS 140-3 인증 프로세스 흐름
 *
 * ┌─────────────────────────────────────────────────────────────────┐
 * │                    FIPS 140-3 인증 절차                         │
 * ├─────────────────────────────────────────────────────────────────┤
 * │                                                                 │
 * │  1단계: CAVP (Cryptographic Algorithm Validation Program)       │
 * │  ├─ 개별 알고리즘의 정확성 검증                                  │
 * │  ├─ NIST 제공 테스트 벡터로 KAT 수행                            │
 * │  ├─ 알고리즘별 인증서 발급 (예: AES Certificate #1234)          │
 * │  └─ ACVP (Automated CAVP)로 자동화 가능                        │
 * │                                                                 │
 * │  2단계: CMVP (Cryptographic Module Validation Program)          │
 * │  ├─ 암호 모듈 전체의 보안 요구사항 검증                          │
 * │  ├─ 인가된 시험기관(CST Lab)이 시험 수행                        │
 * │  ├─ 11개 영역 평가:                                             │
 * │  │   ① 암호 모듈 명세      ② 암호 모듈 인터페이스              │
 * │  │   ③ 역할/서비스/인증     ④ 소프트웨어/펌웨어 보안            │
 * │  │   ⑤ 운영 환경            ⑥ 물리적 보안                      │
 * │  │   ⑦ 비침습적 보안        ⑧ 민감 보안 파라미터 관리          │
 * │  │   ⑨ 자가 테스트          ⑩ 수명주기 보증                    │
 * │  │   ⑪ 기타 공격 완화                                          │
 * │  ├─ NIST 검토 후 인증서 발급 (Certificate #xxxx)                │
 * │  └─ 인증 유효기간: 5년 (갱신 필요)                              │
 * │                                                                 │
 * │  소요 기간: 일반적으로 12~24개월                                │
 * │  비용: $50,000 ~ $300,000+ (모듈 복잡도/레벨에 따라)            │
 * │                                                                 │
 * └─────────────────────────────────────────────────────────────────┘
 */
💡

FIPS 인증을 받으려면: (1) NIST CAVP(Cryptographic Algorithm Validation Program) 테스트 벡터를 testmgr에 포함, (2) CRYPTO_ALG_FIPS_INTERNAL을 적절히 설정, (3) 커널 모듈의 HMAC 무결성 검증 통과가 필요합니다. 자체 알고리즘을 FIPS 인증 범위에 포함시키려면 CMVP(Cryptographic Module Validation Program) 프로세스를 거쳐야 합니다.

FIPS 모드 디버깅

# ─── FIPS 모드 상태 종합 확인 ───

# 1. FIPS 모드 활성화 여부
cat /proc/sys/crypto/fips_enabled

# 2. 부팅 시 FIPS 관련 커널 로그
dmesg | grep -i fips
# [    0.000000] command line: ... fips=1
# [    0.123456] fips mode is enabled

# 3. 자가 테스트 결과 확인
dmesg | grep "alg: self-tests"
# alg: self-tests for sha256 (sha256-generic) passed
# alg: self-tests for aes (aes-generic) passed
# alg: self-tests for cbc(aes) (cbc-aes-aesni) passed

# 4. FIPS 승인 알고리즘 목록 확인
grep -A1 "selftest" /proc/crypto | grep -B1 "passed"

# 5. FIPS 모드에서 차단된 알고리즘 확인
dmesg | grep "not allowed in fips"
# alg: md5 not allowed in fips mode

# 6. 무결성 검증 실패 확인
dmesg | grep -i "integrity"
# integrity: HMAC check failed for vmlinuz

# 7. 커널 FIPS 설정 옵션 확인
zgrep CONFIG_CRYPTO_FIPS /proc/config.gz
# CONFIG_CRYPTO_FIPS=y

# 8. 현재 사용 중인 알고리즘의 FIPS 상태
# /proc/crypto에서 fips 관련 필드 확인
awk '/^name/{name=$NF} /^selftest/{print name, $NF}' /proc/crypto

# 9. FIPS 모드에서 OpenSSL 상태 확인 (사용자 공간 연동)
openssl version
openssl list -providers
# fips 프로바이더가 활성화되어 있어야 함

FIPS 모드 트러블슈팅: (1) 부팅 실패 시 fips=1 제거 후 재부팅하여 원인 파악, (2) 모듈 로드 실패 시 .hmac 파일 존재 여부와 커널 버전 일치 확인, (3) 애플리케이션 오류 시 crypto-policies 설정과 라이브러리 FIPS 모드 연동 확인, (4) VM 환경에서는 호스트의 AES-NI/SHA-NI CPU 플래그 패스스루 확인이 필요합니다.

/proc/crypto 상세 해석

/proc/crypto는 현재 등록된 모든 알고리즘의 상세 정보를 보여줍니다:

# /proc/crypto 출력 예시 — AES-NI CBC 구현
name         : cbc(aes)
driver       : cbc-aes-aesni
module       : aesni_intel
priority     : 400
refcnt       : 9
selftest     : passed
internal     : no
type         : skcipher
async        : no
blocksize    : 16
min keysize  : 16
max keysize  : 32
ivsize       : 16
chunksize    : 16
walksize     : 16
필드의미확인 포인트
name정규 알고리즘 이름crypto_alloc_*()에서 사용하는 이름
driver드라이버 고유 이름어떤 구현체인지 식별 (generic, aesni, qat 등)
module소속 커널 모듈lsmod와 대조하여 모듈 의존성 확인
priority우선순위같은 name 중 가장 높은 값이 실제 사용됨
refcnt현재 참조 횟수0이 아니면 사용 중 (모듈 언로드 불가)
selftest자가 테스트 결과passed / unknown / not available
internal내부 전용 여부yes면 AF_ALG에서 사용 불가
async비동기 여부H/W 가속기 구현은 보통 yes
# 실용적인 /proc/crypto 분석 명령

# 같은 알고리즘의 모든 구현체와 priority 비교
awk '/^name/{n=$3} /^driver/{d=$3} /^priority/{print n, d, $3}' /proc/crypto \
  | sort | column -t

# 특정 알고리즘의 실제 사용되는 구현 확인 (priority 최고)
awk '/^name/{n=$3} /^driver/{d=$3} /^priority/{p=$3}
     n=="cbc(aes)"{print p, d}' /proc/crypto | sort -rn | head -1

# 자가 테스트 실패한 알고리즘 찾기
awk '/^name/{n=$3} /^selftest/{if($3!="passed") print n, $3}' /proc/crypto

# 비동기(H/W) 알고리즘만 확인
awk '/^name/{n=$3} /^async/{if($3=="yes") print n}' /proc/crypto

SIMD 컨텍스트 관리

SSE/AVX/NEON 명령어를 사용하는 H/W 가속 구현에서는 SIMD 레지스터 컨텍스트 관리가 필수입니다. 커널에서 SIMD 레지스터를 사용하려면 명시적으로 저장/복원해야 합니다:

#include <asm/fpu/api.h>        /* x86 */
#include <crypto/internal/simd.h>

/* 방법 1: 직접 kernel_fpu_begin/end 사용 */
static int my_aesni_encrypt(struct skcipher_request *req)
{
    /* kernel_fpu_begin()은 현재 태스크의 FPU 상태를 저장하고
     * 커널이 SSE/AVX 레지스터를 사용할 수 있게 함
     * 주의: 이 구간에서는 preemption이 비활성화됨 */
    kernel_fpu_begin();

    /* AES-NI 명령어 사용 (인라인 어셈블리 또는 .S 파일) */
    aesni_cbc_enc(ctx->key_sched, dst, src, len, iv);

    kernel_fpu_end();
    return 0;
}

/* 방법 2: crypto_simd 래퍼 사용 (권장) */
/* softirq/hardirq 컨텍스트에서도 안전하게 동작
 * SIMD를 사용할 수 없는 컨텍스트에서는 자동으로
 * cryptd kthread로 작업을 위임 */
static struct simd_skcipher_alg *simd_alg;

static int __init my_simd_init(void)
{
    int err;

    /* 원본 SIMD 알고리즘 등록 (__cbc-aes-aesni, INTERNAL 플래그) */
    err = crypto_register_skcipher(&my_simd_skcipher);
    if (err)
        return err;

    /* SIMD 래퍼 등록 (cbc-aes-aesni, 외부 공개)
     * → process context: 직접 SIMD 실행
     * → softirq context: cryptd kthread로 위임 */
    simd_alg = simd_skcipher_create_compat(
        my_simd_skcipher.base.cra_name,
        my_simd_skcipher.base.cra_driver_name,
        my_simd_skcipher.base.cra_name);

    return PTR_ERR_OR_ZERO(simd_alg);
}
커널에서 SIMD 사용 시 주의:
  • kernel_fpu_begin()kernel_fpu_end() 사이에서는 sleep 불가 (preemption 비활성화)
  • softirq/hardirq 컨텍스트에서는 kernel_fpu_begin() 호출 불가 — crypto_simd 래퍼를 사용하거나, irq_fpu_usable()로 확인 후 fallback
  • 대량 데이터 처리 시 주기적으로 kernel_fpu_end(); kernel_fpu_begin();으로 preemption 허용 (latency 방지)
  • ARM의 경우 kernel_neon_begin() / kernel_neon_end() 사용

알고리즘 구현 체크리스트

#단계파일핵심 확인사항
1알고리즘 구현crypto/my_alg.c콜백 함수 모두 구현, 에러 처리, 메모리 해제
2헤더 포함crypto/my_alg.c<crypto/internal/hash.h> 등 올바른 헤더
3알고리즘 등록crypto/my_alg.ccrypto_register_*() + module_init/exit
4MODULE_ALIAScrypto/my_alg.cMODULE_ALIAS_CRYPTO("name") 추가
5테스트 벡터crypto/testmgr.h최소 3개 이상의 KAT 벡터 (NIST 표준 권장)
6테스트 등록crypto/testmgr.calg_test_descs[]에 알파벳순 추가
7Kconfigcrypto/Kconfigconfig CRYPTO_MY_ALG + select 의존성
8Makefilecrypto/Makefileobj-$(CONFIG_CRYPTO_MY_ALG) 추가
9셀프 테스트부팅/로드 시dmesg | grep selftest로 passed 확인
10벤치마크런타임tcrypt 모듈로 성능 측정
💡

커널 소스 참고: 새 알고리즘을 구현할 때는 반드시 커널 트리의 유사 구현을 참고하세요. 해시는 crypto/sha256_generic.c, 블록 암호는 crypto/aes_generic.c, CBC 모드는 crypto/cbc.c, AEAD는 crypto/gcm.c, H/W 가속은 arch/x86/crypto/aesni-intel_glue.c가 좋은 참고 자료입니다.