TPM 2.0 (Trusted Platform Module)

Trusted Platform Module(TPM) 2.0은 TCG(Trusted Computing Group)가 표준화한 전용 보안 암호화(Encryption) 코프로세서입니다. 4개의 계층 구조(Platform/Storage/Endorsement/Null), PCR 뱅크 기반 Measured Boot, HMAC/Policy 세션을 통한 Enhanced Authorization, NV 인덱스 영구 저장, CRB/TIS 하드웨어 인터페이스, 그리고 Linux 커널 서브시스템(tpm_chip, tpm_space, tpmrm)을 커널 소스 기반으로 심층 분석합니다. EK/SRK/AK 키 계층, Seal/Unseal, 원격 증명(Quote), LUKS2 봉인, dTPM/fTPM/sTPM 비교, vTPM(swtpm/SVSM), tpm2-tools/tpm2-tss/FAPI 활용까지 포괄합니다.

⚠️

전제 조건: 커널 보안 개요, Secure Boot, 암호화 서브시스템 문서를 먼저 읽으세요. TPM은 이들 기술의 하드웨어 신뢰 기반(Root of Trust)으로 동작하므로, 보안 부팅 체인과 암호화 기본 개념을 이해하면 학습 효율이 크게 높아집니다.

💡

일상 비유: TPM은 은행 금고 시스템과 같습니다. SRK(Storage Root Key)는 마스터 금고 — 내부에 더 작은 금고(하위 키)를 넣을 수 있습니다. EK(Endorsement Key)는 여권 — 이 칩의 신원을 증명하지만 절대 외부로 반출되지 않습니다. PCR은 봉인 테이프 — 한번 붙이면 떼어낼 수 없고, 위에 덧붙이기(extend)만 가능합니다. 누군가 테이프를 교체하면 값이 달라져 즉시 감지됩니다. Seal은 금고에 무언가를 넣되, 봉인 테이프가 특정 패턴일 때만 열리게 하는 것입니다. AK(Attestation Key)는 공증 도장 — PCR 값을 서명하여 외부 검증자에게 "이 시스템은 변조되지 않았다"고 증명합니다.

핵심 요약

  • 전용 보안 코프로세서 — TPM은 메인 CPU와 독립된 하드웨어(또는 펌웨어(Firmware))로, 키 생성·서명·암호화·해시(Hash)를 수행하며 비밀 키가 TPM 외부로 노출되지 않습니다.
  • 4개 계층 구조 — Platform(펌웨어), Storage(OS/사용자), Endorsement(프라이버시/증명), Null(임시) 계층이 각각 독립된 권한과 시드(seed)를 보유합니다.
  • PCR 뱅크 — SHA-1/SHA-256/SHA-384 등 다중 해시 알고리즘별 PCR 뱅크가 부팅 과정(Boot Process)의 각 단계를 측정(extend)하여 Measured Boot 체인을 형성합니다.
  • Enhanced Authorization — HMAC 세션과 Policy 세션을 조합하여 "PCR 값이 X이고 비밀번호가 Y이면 허용" 같은 복합 정책을 구성합니다.
  • Linux 커널 서브시스템drivers/char/tpm/에 위치하며, tpm_chip 구조체(Struct)와 /dev/tpmrm0 리소스 매니저가 멀티프로세스 접근을 관리합니다.
  • 실무 활용 — LUKS2 디스크 암호화 봉인, 원격 증명(Remote Attestation), IMA/EVM 무결성(Integrity) 검증, Secure Boot 정책의 하드웨어 신뢰 기반으로 널리 사용됩니다.

단계별 이해

TPM 2.0의 핵심 개념을 은행 금고 비유로 단계별로 이해해 보겠습니다.

1단계: SRK — 마스터 금고

Storage Root Key(SRK)는 TPM 내부의 최상위 저장 키입니다. SRK는 TPM이 처음 프로비저닝될 때 생성되며, 이 키 아래에 모든 하위 키가 계층적으로 파생됩니다. 은행의 마스터 금고처럼, SRK 자체는 절대 외부로 반출되지 않고 TPM 내부에서만 사용됩니다. 하위 키를 "감싸서(wrap)" 외부에 저장할 수 있지만, 감싼 키를 풀려면 반드시 TPM을 거쳐야 합니다.

2단계: EK — 여권(신원 증명)

Endorsement Key(EK)는 TPM 제조사가 공장에서 주입하는 고유 신원 키입니다. 여권이 개인의 정체를 증명하듯, EK 인증서는 "이 TPM은 진짜 TPM 칩이다"를 제3자에게 증명합니다. EK는 절대로 서명 연산에 사용되지 않으며(restricted decrypt 전용), 오직 신원 확인과 AK 인증 프로토콜에만 참여합니다. 이 설계는 프라이버시 보호를 위한 것입니다 — EK로 직접 서명하면 모든 서명이 동일한 키로 추적되기 때문입니다.

3단계: PCR — 봉인 테이프

Platform Configuration Register(PCR)는 시스템 상태를 기록하는 단방향 레지스터(Register)입니다. 봉인 테이프에 비유하면:

4단계: Seal/Unseal — 조건부 금고

Seal은 비밀 데이터를 TPM에 봉인하되, 특정 PCR 값(봉인 테이프 패턴)이 일치할 때만 열리게 하는 것입니다. 예를 들어 LUKS2 디스크 암호화 키를 TPM에 봉인하면서 "PCR 7(Secure Boot 정책)이 정상이고, PCR 11(커널 해시)이 기대값과 같을 때만 키를 해제하라"고 정책을 설정합니다. 시스템이 변조되면 PCR 값이 달라져 키 해제가 거부됩니다.

5단계: AK — 공증 도장

Attestation Key(AK)는 원격 증명(Remote Attestation)에 사용되는 서명 키입니다. AK는 restricted signing 키로, TPM 내부에서 생성된 데이터(PCR Quote 등)에만 서명할 수 있습니다 — 외부 데이터에 자유롭게 서명하는 것이 아닙니다. 이를 통해 원격 검증자는 "이 Quote는 정말 TPM이 생성한 것이지, 소프트웨어가 위조한 것이 아니다"를 확인할 수 있습니다.

6단계: 세션과 정책 — 복합 자물쇠

TPM 2.0의 Enhanced Authorization은 단순한 비밀번호를 넘어 복합 정책을 지원합니다. "비밀번호가 맞고(PolicyPassword) PCR 7이 특정 값이고(PolicyPCR) 로컬리티가 3 이상이면(PolicyLocality) 허용"처럼 AND/OR 논리로 정책을 조합합니다. HMAC 세션은 비밀번호를 평문으로 전송하지 않고 HMAC으로 증명하며, Policy 세션은 정책 다이제스트를 순차적으로 구축하여 최종 인가를 결정합니다.

ℹ️

TPM 2.0 vs 1.2: TPM 1.2는 SHA-1과 RSA만 지원하고, 단일 Owner 계층만 있었습니다. TPM 2.0은 알고리즘 민첩성(SHA-256, ECC, AES 등), 4개 독립 계층, Enhanced Authorization 정책 엔진(Policy Engine)을 도입하여 유연성과 보안성을 크게 향상시켰습니다.

TPM 아키텍처 개요

TPM(Trusted Platform Module)은 TCG(Trusted Computing Group)가 표준화한 보안 코프로세서 사양입니다. 2014년에 공개된 TPM 2.0(Library Specification)은 TPM 1.2의 근본적인 한계를 해결하며 알고리즘 민첩성, 다중 계층 구조, 복합 인가 정책을 도입했습니다.

TPM 1.2 vs TPM 2.0 비교

항목TPM 1.2TPM 2.0
해시 알고리즘SHA-1 전용SHA-256, SHA-384, SHA-512, SHA-1, SM3 (알고리즘 민첩성)
비대칭 알고리즘RSA 2048 전용RSA 1024/2048/3072/4096, ECC P-256/P-384/BN-P256, SM2
대칭 알고리즘없음AES-128/192/256 (CFB, CTR), HMAC, XOR
계층 구조단일 Owner4개 독립 계층 (Platform, Storage, Endorsement, Null)
키 유형고정 (SRK, AIK 등)유연한 키 속성 (restricted/unrestricted, sign/decrypt, fixedTPM 등)
인가 방식OIAP/OSAP (단순 세션)Enhanced Authorization (PolicyPCR, PolicyOR 등 조합)
NV 저장소단순 바이트 배열타입별 NV (Ordinary, Counter, Bits, Extend, PIN)
PCR 뱅크SHA-1 단일 뱅크 24개알고리즘별 복수 뱅크 (SHA-1/SHA-256 각 24개 등)
플랫폼 독립성PC 중심PC, 모바일, IoT, 자동차, 서버 범용
사양 버전TCG TPM 1.2 rev 116TCG TPM Library (Part 1-4), PTP, PC Client

물리적 인터페이스

인터페이스버스(Bus)속도대표 칩비고
LPCLow Pin Count~33 MHzInfineon SLB 9665레거시 PC, TIS 인터페이스
SPISerial Peripheral Interface~43 MHzInfineon SLB 9670, Nuvoton NPCT75x현재 주류, CRB/TIS 모두 지원
I2CInter-Integrated Circuit~1 MHzInfineon SLB 9673, Nuvoton NPCT7xx임베디드/IoT, 낮은 핀 수
fTPM펌웨어 (TEE 내부)메모리 버스Intel PTT (CSME), AMD PSP fTPM별도 칩 없이 펌웨어로 구현
ℹ️

TCG 사양 구조: TPM 2.0 사양은 4개 파트로 구성됩니다. Part 1 (Architecture): 개념과 설계, Part 2 (Structures): 데이터 타입과 구조체, Part 3 (Commands): 명령어 정의, Part 4 (Supporting Routines): 참조 구현 코드. 이외에 PTP(Platform TPM Profile)가 하드웨어 인터페이스를, PC Client가 PC 플랫폼 바인딩을 정의합니다.

TPM 내부 구성요소

TPM 2.0 Internal Architecture Crypto Engine RSA (1024-4096) ECC (P-256/P-384) AES-128/256 (CFB) HMAC / KDF Hash (SHA-1/256/384) SM2 / SM3 (선택) Random Number Gen DRBG (SP 800-90A) Entropy Source Key Generation KDFa / KDFe Primary Seed → Key Power Detection _TPM_Init Startup(CLEAR/STATE) PCR Banks SHA-1 Bank: PCR[0..23] SHA-256 Bank: PCR[0..23] SHA-384 Bank: PCR[0..23] Extend-only / Reset by hierarchy NV Storage Persistent Objects NV Indices EK Certificate Hierarchy Seeds Execution Engine Command Dispatch Authorization Check Session Management Object Context (load/flush) Dictionary Attack Logic Locality Enforcement Audit Logging I/O Buffer (Command / Response) Command: [tag|size|commandCode|handles|authArea|parameters] Response: [tag|size|responseCode|handles|authArea|parameters] Host Interface: TIS (FIFO) / CRB (Command Response Buffer) — SPI / I2C / LPC / fTPM

TPM 2.0의 내부는 독립적인 기능 블록으로 구성됩니다. 각 블록의 역할을 상세히 살펴보겠습니다.

Crypto Engine

TPM의 핵심 연산 유닛으로, 모든 암호화 연산을 수행합니다. RSA 키 생성은 수백 밀리초에서 수 초가 소요되며, ECC는 상대적으로 빠릅니다. AES는 대칭 암호화에, HMAC과 KDF(Key Derivation Function)는 세션 키 파생과 인가 검증에 사용됩니다. 알고리즘 민첩성(Algorithm Agility) 설계로, TPM 구현체가 지원하는 알고리즘 목록은 TPM2_GetCapability(TPM_ALG_FIRST..TPM_ALG_LAST)로 조회합니다.

Random Number Generator

NIST SP 800-90A 규격의 DRBG(Deterministic Random Bit Generator)를 기반으로 합니다. 하드웨어 엔트로피 소스(noise ring oscillator, thermal noise 등)에서 시드를 추출하고, DRBG가 결정적 확장을 수행합니다. TPM2_GetRandom 명령으로 최대 64바이트의 난수를 요청할 수 있으며, TPM2_StirRandom으로 외부 엔트로피를 주입합니다.

Key Generation

TPM 2.0은 두 가지 KDF를 사용합니다. KDFa는 HMAC 기반 카운터 모드 KDF로, 키 파생·HMAC 세션 키·nonce 생성에 사용됩니다. KDFe는 ECDH 공유 비밀에서 대칭 키를 파생할 때 사용됩니다. Primary Key 생성 시, 계층의 seed와 고정 템플릿을 KDFa에 입력하여 항상 동일한 키를 재생성(re-derivation)할 수 있습니다.

TPM 2.0 명령 구조

모든 TPM 명령은 표준화된 바이트 스트림 형식을 따릅니다:

/* TPM 2.0 Command Header */
typedef struct {
    TPMI_ST_COMMAND_TAG  tag;          /* TPM_ST_SESSIONS or TPM_ST_NO_SESSIONS */
    UINT32               commandSize;  /* 전체 명령 바이트 크기 */
    TPM_CC               commandCode;  /* 명령 코드 (예: TPM_CC_CreatePrimary = 0x00000131) */
} TPM2_COMMAND_HEADER;

/* TPM 2.0 Response Header */
typedef struct {
    TPM_ST               tag;          /* TPM_ST_SESSIONS or TPM_ST_NO_SESSIONS */
    UINT32               responseSize; /* 전체 응답 바이트 크기 */
    TPM_RC               responseCode; /* TPM_RC_SUCCESS = 0x00000000 */
} TPM2_RESPONSE_HEADER;

/* 전체 명령 구조: [Header][Handles][AuthArea][Parameters] */
/* 전체 응답 구조: [Header][Handles][ParameterSize][Parameters][AuthArea] */
💡

Object 슬롯 제한: TPM은 동시에 로드할 수 있는 transient object 수가 제한됩니다 (일반적으로 3~7개). 이 제한을 커널의 tpmrm(Resource Manager)이 자동으로 context save/load하여 해결합니다.

TPM 계층 구조

TPM 2.0 Hierarchy Structure Platform (PH) platformAuth platformPolicy ppSeed (Primary Seed) BIOS/펌웨어 제어 PCR 0-7 리셋 가능 NV platformCreate Endorsement (EH) endorsementAuth endorsementPolicy epSeed (Primary Seed) 프라이버시 민감 EK 생성/인증 TPM2_Clear 유지 Storage (SH) ownerAuth ownerPolicy shSeed (Primary Seed) OS/사용자 제어 SRK, 저장 키 Seal/Unseal 대상 Null (NH) nullAuth (빈 값) nullPolicy (빈 값) nullSeed (매 부팅 변경) 임시 키 전용 부팅 시 리셋 영구 저장 불가 각 계층 공통 속성 authValue (비밀번호) | authPolicy (정책 다이제스트) | proof (내부 검증) | primarySeed (KDFa 시드) TPM2_Clear 영향 범위 Storage Hierarchy: shSeed 재생성, 모든 SH 키/NV 삭제 Endorsement Hierarchy: epSeed 유지, EH 키 보존 (프라이버시 보호) Platform Hierarchy: ppSeed 유지 (펌웨어 관리 영역)

TPM 2.0은 4개의 독립적인 계층(Hierarchy)을 도입했습니다. 각 계층은 고유한 인가 값(authValue), 정책(authPolicy), 증명 값(proof), 기본 시드(primarySeed)를 보유합니다.

Platform Hierarchy (PH)

펌웨어/BIOS가 제어하는 계층입니다. 부팅 초기에 platformAuth를 설정하고, OS에 제어를 넘기기 전에 비활성화하는 것이 일반적입니다. Platform Hierarchy만이 PCR을 리셋하고, TPM2_ChangePPS로 Platform Primary Seed를 변경할 수 있습니다. TPMA_NV_PLATFORMCREATE 속성이 설정된 NV 인덱스는 Platform Hierarchy 권한으로만 생성/삭제됩니다.

Endorsement Hierarchy (EH)

프라이버시와 신원 증명에 특화된 계층입니다. EK(Endorsement Key)가 이 계층에서 생성되며, TPM2_Clear 시에도 epSeed가 유지되어 동일한 EK를 재생성할 수 있습니다. 이는 TPM의 제조사 인증서(EK Certificate)와의 일관성을 보장합니다. Endorsement Hierarchy는 TPM2_ChangeEPS로 의도적으로 시드를 변경할 수 있지만, 이 경우 기존 EK 인증서가 무효화(Invalidation)됩니다.

Storage Hierarchy (SH)

OS와 사용자가 일상적으로 사용하는 주 계층입니다. SRK(Storage Root Key)가 이 계층의 Primary Key로 생성되며, 모든 사용자 키, 봉인 데이터, 어플리케이션 키가 SRK 아래에 계층적으로 위치합니다. TPM2_ClearshSeed가 재생성되어 모든 SH 키와 NV 인덱스가 삭제됩니다.

Null Hierarchy (NH)

부팅할 때마다 새로운 nullSeed가 생성되는 임시 계층입니다. 인가 없이 사용 가능하며(nullAuth는 빈 값), 세션에서 임시 키를 생성하거나 ECDH 키 교환에 활용됩니다. 영구 저장이 불가능하므로 재부팅하면 모든 Null Hierarchy 키가 사라집니다.

속성PlatformEndorsementStorageNull
Primary Seed 핸들0x4000000C0x4000000B0x400000010x40000007
제어 주체BIOS/펌웨어프라이버시 관리자OS/사용자누구나
TPM2_Clear 영향ppSeed 유지epSeed 유지shSeed 재생성해당 없음
영구 객체 저장가능가능가능불가
대표 키Platform 키EKSRK임시 키
PCR 리셋 권한PCR 0-7 가능없음없음없음

키 계층과 키 유형

TPM 2.0 Key Hierarchy EH (epSeed) EK (Endorsement Key) restricted, decrypt fixedTPM, fixedParent AK (Attestation Key) restricted, sign SH (shSeed) SRK (Storage Root Key) restricted, decrypt fixedTPM, fixedParent Storage Key (중간) Signing Key (리프) Sealed Data Object Sub-signing Key Sub-sealed Data Key Attributes (TPMA_OBJECT) restricted: TPM 내부 생성 데이터에만 연산 가능 (Quote, Certify 등) sign vs decrypt: 서명 전용 또는 복호화(wrapping) 전용 fixedTPM: 이 TPM에서만 사용 가능 (마이그레이션 불가) fixedParent: 부모 키 변경 불가 (키 트리 구조 고정) sensitiveDataOrigin: TPM 내부에서 키 생성됨 userWithAuth: authValue로 인가 가능 adminWithPolicy: 관리 작업에 정책 필요 noDA: Dictionary Attack 보호 대상 제외

TPM 2.0의 키는 계층적 트리 구조를 형성합니다. 각 계층의 Primary Seed에서 Primary Key가 파생되고, Primary Key 아래에 자식 키를 생성할 수 있습니다.

EK (Endorsement Key)

EK는 TPM의 신원을 증명하는 유일한 키입니다. 제조사가 공장에서 EK 인증서를 발급하며, TCG EK Credential Profile에 따른 표준 템플릿으로 생성됩니다. EK는 restricted decrypt 속성을 가지므로 서명에는 사용할 수 없고, 오직 TPM2_ActivateCredential 프로토콜에서 AK를 인증하는 데 사용됩니다.

SRK (Storage Root Key)

SRK는 Storage Hierarchy의 Primary Key로, 모든 사용자 키의 최상위 부모 역할을 합니다. restricted decrypt 속성을 가지며, 자식 키를 감싸는(wrap) 용도로 사용됩니다. TCG에서 정의한 표준 SRK 템플릿(RSA-2048 또는 ECC P-256)을 사용하면, 동일한 shSeed에서 항상 동일한 SRK가 재생성됩니다.

AK (Attestation Key)

AK는 원격 증명(Remote Attestation)에 사용되는 restricted signing 키입니다. restricted 속성으로 인해 TPM 내부에서 생성된 데이터(Quote, Certify 결과 등)에만 서명할 수 있으며, 임의의 외부 데이터에 서명할 수 없습니다. 이 제한이 Quote의 무결성을 보장합니다.

키 생성 코드

/* SRK 생성을 위한 Primary Key 템플릿 (TCG EK Credential Profile) */
TPM2B_PUBLIC srk_template = {
    .publicArea = {
        .type           = TPM2_ALG_RSA,
        .nameAlg        = TPM2_ALG_SHA256,
        .objectAttributes = (
            TPMA_OBJECT_RESTRICTED     |
            TPMA_OBJECT_DECRYPT        |
            TPMA_OBJECT_FIXEDTPM       |
            TPMA_OBJECT_FIXEDPARENT    |
            TPMA_OBJECT_SENSITIVEDATAORIGIN |
            TPMA_OBJECT_USERWITHAUTH   |
            TPMA_OBJECT_NODA
        ),
        .parameters.rsaDetail = {
            .symmetric = {
                .algorithm = TPM2_ALG_AES,
                .keyBits   = { .aes = 128 },
                .mode      = { .aes = TPM2_ALG_CFB },
            },
            .scheme   = { .scheme = TPM2_ALG_NULL },
            .keyBits  = 2048,
            .exponent = 0,  /* 기본값 65537 */
        },
    },
};

/* TPM2_CreatePrimary 호출 */
TSS2_RC rc = Esys_CreatePrimary(
    esys_ctx,
    ESYS_TR_RH_OWNER,       /* Storage Hierarchy */
    ESYS_TR_PASSWORD,        /* ownerAuth 세션 */
    ESYS_TR_NONE, ESYS_TR_NONE,
    &in_sensitive,
    &srk_template,
    &outside_info,
    &creation_pcr,
    &srk_handle,             /* 생성된 SRK 핸들 */
    &out_public,
    &creation_data,
    &creation_hash,
    &creation_ticket
);
키 유형계층restrictedsign/decrypt용도
EKEndorsementYesdecryptTPM 신원 증명, AK 인증
SRKStorageYesdecrypt자식 키 래핑, 키 트리 루트
AKEndorsement/StorageYessignQuote, Certify (원격 증명)
Storage KeyStorageYesdecrypt중간 래핑 키
Signing KeyStorageNosign범용 서명 (코드 서명 등)
Sealed DataStorage--비밀 데이터 봉인

PCR 상세

Platform Configuration Register(PCR)는 TPM의 핵심 보안 메커니즘으로, 시스템 상태를 암호학적으로 기록합니다. PCR은 extend-only 레지스터로, 현재 값에 새 데이터를 해시하여 덧붙이는 것만 가능합니다.

PCR Extend 연산

PCR Extend는 다음과 같은 단방향 연산입니다:

PCR_new = Hash(PCR_old || data)

예시 (SHA-256, PCR 초기값 = 0x0000...0000):
PCR_0  = SHA-256(0x0000...0000 || measurement_1)
       = 0x3a7f...8b2e
PCR_0  = SHA-256(0x3a7f...8b2e || measurement_2)
       = 0x91c4...d017
...

이 연산의 핵심 속성은 비가역성입니다. PCR 값을 이전 상태로 되돌리는 것이 계산적으로 불가능하므로, 측정 체인의 무결성이 보장됩니다. PCR은 TPM2_Startup(CLEAR) 시 0으로 초기화되며, TPM2_Startup(STATE)(절전 복귀)에서는 이전 값을 유지합니다.

PCR 뱅크

TPM 2.0의 알고리즘 민첩성에 따라, 각 해시 알고리즘별로 독립적인 PCR 뱅크가 존재합니다. TPM2_PCR_Extend 명령은 지정된 뱅크의 PCR만 갱신하며, TPM2_PCR_Event는 모든 활성 뱅크를 동시에 갱신합니다.

뱅크다이제스트 크기상태비고
SHA-120 바이트레거시 호환NIST 권장 중단, 그러나 하위 호환성 유지
SHA-25632 바이트기본 뱅크현재 주류, FIPS 140-3 준수
SHA-38448 바이트고보안정부/군사 요구사항
SHA-51264 바이트선택적일부 TPM에서 지원
SM3-25632 바이트중국 표준중국 시장 규격 준수

TCG PC Client PCR 할당 표준

PCR측정 대상Extend 주체설명
0SRTM, BIOS, Host PlatformBIOS/UEFICore Root of Trust for Measurement 코드
1Host Platform ConfigurationBIOS/UEFIBIOS 설정, EFI 변수
2Option ROM CodeBIOS/UEFI확장 ROM 실행 코드 (네트워크, 스토리지)
3Option ROM ConfigurationBIOS/UEFI확장 ROM 설정 데이터
4IPL Code (MBR/부트로더)BIOS/UEFI부트 매니저, GRUB, systemd-boot 코드
5IPL ConfigurationBIOS/UEFI부트 매니저 설정, GPT 테이블
6State TransitionsBIOS/UEFIWake 이벤트, 전원 상태 전환
7Secure Boot PolicyBIOS/UEFISecureBoot 변수: PK, KEK, db, dbx, MokList
8-15OS 정의부트로더/OSGRUB: 커널/initrd(8,9), IMA(10), systemd-boot(11,12)
16Debug누구나테스트용, 리셋 가능 (Platform Hierarchy)
17-22DRTMDRTM 에이전트Intel TXT / AMD SKINIT 전용
23Application Support어플리케이션사용자 어플리케이션 용도

Locality 개념

PCR 접근은 Locality(지역성)에 의해 제어됩니다. Locality는 TPM에 명령을 보내는 소프트웨어의 신뢰 수준을 나타냅니다:

Locality접근 주체PCR Extend 범위PCR Reset 범위
0일반 OS/사용자 공간PCR 0-15, 23PCR 16, 23
1OS 특권 모드PCR 0-15, 20, 23PCR 16, 20, 23
2시스템 소프트웨어 (GRUB 등)PCR 0-15, 20, 23PCR 16, 20, 23
3DRTM (TXT ACM)PCR 0-23PCR 16-23
4Intel TXT 환경PCR 17-18PCR 17-18

tpm2-tools PCR 조작

# PCR 값 읽기 (SHA-256 뱅크, 모든 PCR)
tpm2_pcrread sha256

# 특정 PCR만 읽기 (SHA-256 뱅크, PCR 0, 7, 11)
tpm2_pcrread sha256:0,7,11

# PCR Extend: PCR 23에 파일 해시를 기록
tpm2_pcrextend 23:sha256=$(sha256sum /boot/vmlinuz | cut -d' ' -f1)

# PCR Event: 파일 내용을 모든 활성 뱅크의 PCR 23에 동시 기록
tpm2_pcrevent 23 /boot/vmlinuz

# PCR Reset: PCR 16 초기화 (디버그용)
tpm2_pcrreset 16

# PCR 할당 정보 조회
tpm2_getcap pcrs
⚠️

PCR Fragility: PCR에 직접 바인딩된 Seal 정책은 커널, initrd, 부트로더 업데이트 시 PCR 값이 변경되어 키 해제가 실패합니다. 이를 PCR Fragility 문제라 하며, systemd-cryptenroll의 signed PCR policy(--tpm2-public-key-pcrs)가 이 문제를 해결합니다. 자세한 내용은 TPM 기반 디스크 암호화 섹션을 참고하세요.

TPM 2.0 세션

TPM 2.0 Session Lifecycle StartAuthSession sessionType: HMAC/Policy/Trial tpmKey (salted), bind entity → sessionHandle 반환 명령에서 사용 authArea에 sessionHandle 포함 HMAC 계산 / Policy 누적 continueSession 플래그 FlushContext 세션 자원 해제 또는 continueSession=0이면 명령 완료 후 자동 해제 HMAC Session authValue를 평문 전송 없이 HMAC으로 증명 명령/응답 암호화 가능 Policy Session Policy 명령으로 policyDigest 누적 최종 digest가 authPolicy와 일치 시 허용 복합 조건 (AND/OR) 지원 Trial Policy Session 정책 다이제스트를 계산만 함 TPM이 검증하지 않음 객체 생성 시 authPolicy 설정용 Session Attributes (TPMA_SESSION) continueSession | auditExclusive | auditReset | decrypt (명령 파라미터 암호화) | encrypt (응답 파라미터 암호화) | audit Salted: tpmKey 공개키로 salt 암호화 → 세션 키 강화 | Bound: 특정 entity에 바인딩 → 해당 entity만 인가 가능

TPM 2.0의 세션은 명령 인가와 파라미터 보호의 핵심 메커니즘입니다. 모든 인가가 필요한 명령은 세션을 통해 수행되며, 세션은 최대 3개까지 동시에 하나의 명령에 첨부할 수 있습니다.

HMAC 세션

HMAC 세션은 authValue(비밀번호)를 평문으로 전송하지 않고 HMAC으로 증명하는 방식입니다. 세션 키는 KDFa(sessionKey = KDFa(hashAlg, sessionSecret, "ATH", nonceTPM, nonceCaller, bits))로 파생되며, 이 키로 명령 파라미터의 HMAC을 계산합니다. decrypt/encrypt 속성을 설정하면 명령 파라미터와 응답 파라미터를 각각 AES-CFB로 암호화하여 버스 스니핑 공격을 방어합니다.

Policy 세션

Policy 세션은 Enhanced Authorization의 핵심입니다. 세션을 시작한 후 여러 Policy 명령(TPM2_PolicyPCR, TPM2_PolicyPassword 등)을 순차적으로 호출하면, 각 명령이 세션의 policyDigest를 갱신합니다. 최종 policyDigest가 대상 객체의 authPolicy와 일치하면 인가가 승인됩니다.

Trial Policy 세션

Trial 세션은 TPM이 실제로 정책을 검증하지 않고 policyDigest 값만 계산합니다. 이 값을 객체 생성 시 authPolicy로 설정하는 데 사용됩니다. 즉, "이 정책을 만족해야 이 키를 사용할 수 있다"는 조건을 객체에 바인딩하는 과정입니다.

# HMAC 세션 시작 (salted, unbound)
tpm2_startauthsession --policy-session -S session.ctx

# Policy 세션으로 PCR 정책 설정
tpm2_policypcr -S session.ctx -l sha256:0,7

# Trial 세션으로 policyDigest 계산 (객체 생성 전)
tpm2_startauthsession --trial-session -S trial.ctx
tpm2_policypcr -S trial.ctx -l sha256:0,7
tpm2_policygetdigest -S trial.ctx -o policy.digest
tpm2_flushcontext trial.ctx

Enhanced Authorization 정책

Enhanced Authorization Policy Tree PolicyOR Branch A PolicyPCR(sha256:7) AND PolicyPassword Branch B PolicySecret(admin_key) Policy 명령 목록 상태 기반: PolicyPCR — 특정 PCR 값 요구 (Measured Boot 바인딩) PolicyLocality — 특정 Locality에서만 허용 (Locality 3 = DRTM) PolicyNV — NV 인덱스 값 비교 (카운터, 비트 플래그) PolicyCounterTimer — TPMS_TIME_INFO 기반 시간 조건 인증 기반: PolicyPassword — authValue 비밀번호 요구 PolicyAuthValue — HMAC에 authValue 포함 PolicySecret — 다른 객체의 authValue로 인가 PolicySigned — 외부 키의 서명으로 인가 논리 조합: PolicyOR — 여러 브랜치 중 하나 충족 (OR 논리) 순차 호출 — 여러 Policy 순서대로 호출 (AND 논리) 제한: PolicyCommandCode — 특정 명령에만 허용 PolicyCpHash — 특정 명령 파라미터에만 허용 PolicyNameHash — 특정 객체 이름에만 허용 PolicyDuplicationSelect — 복제 대상 제한 고급: PolicyAuthorize — 외부 서명으로 정책 갱신 가능 PolicyNvWritten — NV가 기록된 적 있는지 검사 PolicyTemplate — 생성될 객체 템플릿 제한

Enhanced Authorization(EA)은 TPM 2.0의 가장 강력한 기능 중 하나로, 단순 비밀번호를 넘어 다양한 조건을 논리적으로 조합하여 인가 정책을 구성합니다.

정책 구성 원리

정책은 policyDigest라는 해시 값으로 표현됩니다. 각 Policy 명령은 현재 세션의 policyDigest를 갱신합니다:

policyDigest = Hash(policyDigest_old || TPM_CC_PolicyPCR || pcrs || pcrDigest)
policyDigest = Hash(policyDigest_old || TPM_CC_PolicyPassword)
...
최종 policyDigest == 객체의 authPolicy → 인가 승인

여러 Policy 명령을 순차적으로 호출하면 AND 논리가 됩니다 (모든 조건 충족 필요). PolicyOR는 여러 브랜치의 OR 논리를 제공하여, 브랜치 중 하나만 충족하면 됩니다.

복합 정책 예시: PCR + 비밀번호 OR 관리자 키

# 1단계: Trial 세션으로 Branch A의 digest 계산
tpm2_startauthsession --trial-session -S trial_a.ctx
tpm2_policypcr -S trial_a.ctx -l sha256:7
tpm2_policypassword -S trial_a.ctx
tpm2_policygetdigest -S trial_a.ctx -o branch_a.digest
tpm2_flushcontext trial_a.ctx

# 2단계: Trial 세션으로 Branch B의 digest 계산
tpm2_startauthsession --trial-session -S trial_b.ctx
tpm2_policysecret -S trial_b.ctx -c admin_key.ctx
tpm2_policygetdigest -S trial_b.ctx -o branch_b.digest
tpm2_flushcontext trial_b.ctx

# 3단계: Trial 세션으로 PolicyOR의 최종 digest 계산
tpm2_startauthsession --trial-session -S trial_or.ctx
tpm2_policyor -S trial_or.ctx -l sha256:branch_a.digest,branch_b.digest
tpm2_policygetdigest -S trial_or.ctx -o final_policy.digest
tpm2_flushcontext trial_or.ctx

# 4단계: 최종 정책으로 키 생성
tpm2_create -C srk.ctx -L final_policy.digest \
  -u key.pub -r key.priv -a "sign|fixedtpm|fixedparent|sensitivedataorigin"

# 5단계: 사용 시 Branch A로 인가 (PCR + 비밀번호)
tpm2_startauthsession --policy-session -S session.ctx
tpm2_policypcr -S session.ctx -l sha256:7
tpm2_policypassword -S session.ctx
tpm2_policyor -S session.ctx -l sha256:branch_a.digest,branch_b.digest
tpm2_sign -c key.ctx -p session:session.ctx+mypassword -o sig.out msg.dat
💡

PolicyAuthorize — 정책의 정책: PolicyAuthorize는 외부 서명 키가 새로운 정책을 서명하면, 객체를 재생성하지 않고도 정책을 갱신할 수 있습니다. 이는 PCR 값이 커널 업데이트 등으로 변경될 때 특히 유용하며, systemd-cryptenroll의 signed PCR policy가 이 메커니즘을 활용합니다.

NV 인덱스

TPM의 NV(Non-Volatile) 저장소는 재부팅 후에도 유지되는 영구 데이터를 저장합니다. NV 인덱스는 단순한 바이트 배열이 아니라, 타입별로 특화된 동작을 지원합니다.

NV 인덱스 타입

타입동작용도
Ordinary임의 읽기/쓰기인증서 저장, 구성 데이터
Counter단조 증가 전용 (감소 불가)롤백(Rollback) 방지, 부팅 횟수 기록
Bits개별 비트 설정 전용 (해제 불가)일회성 플래그, 기능 활성화
ExtendPCR과 동일한 extend 연산감사 로그, 이벤트 누적 해시
PIN Pass실패 카운터 (성공 시 리셋)비밀번호 시도 제한
PIN Fail실패 카운터 (리셋 불가)영구 잠금(Lock) 임계값

NV 속성 (TPMA_NV)

속성비트설명
PPWRITE0Platform Hierarchy로 쓰기 가능
OWNERWRITE1Owner(Storage) Hierarchy로 쓰기 가능
AUTHWRITE2NV 인덱스 자체 authValue로 쓰기 가능
POLICYWRITE3NV 인덱스 authPolicy로 쓰기 가능
PPREAD16Platform Hierarchy로 읽기 가능
OWNERREAD17Owner Hierarchy로 읽기 가능
AUTHREAD18NV 인덱스 자체 authValue로 읽기 가능
POLICYREAD19NV 인덱스 authPolicy로 읽기 가능
WRITTEN29한 번 이상 기록됨 (읽기 전용(Read-Only))
WRITEDEFINE13Write Lock 후 TPM2_Clear까지 쓰기 잠금
WRITE_STCLEAR14Write Lock 후 재부팅까지 쓰기 잠금
GLOBALLOCK5TPM2_NV_GlobalWriteLock으로 일괄 잠금
READ_STCLEAR12Read Lock 후 재부팅까지 읽기 잠금
PLATFORMCREATE30Platform Hierarchy가 생성 (Clear에 영향 안 받음)

NV 인덱스 핸들 범위

범위용도예시
0x01000000 ~ 0x013FFFFFOwner 정의사용자 데이터
0x01400000 ~ 0x017FFFFFOwner 정의 (TPM_NT 활용)카운터, extend 등
0x01800000 ~ 0x01BFFFFFPlatform 정의펌웨어 구성
0x01C00000 ~ 0x01FFFFFFTCG/제조사 정의EK 인증서 (0x01C00002)
# NV 인덱스 정의: 32바이트, Owner 읽기/쓰기, 인증 필요
tpm2_nvdefine 0x01000001 -s 32 -a "ownerread|ownerwrite|authread|authwrite" -p mysecret

# NV 인덱스에 데이터 쓰기
echo -n "Hello TPM NV Storage!" | tpm2_nvwrite 0x01000001 -i - -P mysecret

# NV 인덱스에서 데이터 읽기
tpm2_nvread 0x01000001 -P mysecret

# 카운터형 NV 인덱스 정의 및 증가
tpm2_nvdefine 0x01500001 -s 8 -a "ownerread|ownerwrite|nt=counter"
tpm2_nvincrement 0x01500001

# NV 인덱스 삭제
tpm2_nvundefine 0x01000001

# 모든 NV 인덱스 목록 조회
tpm2_getcap handles-nv-index
ℹ️

EK 인증서 NV 인덱스: 대부분의 TPM은 NV 인덱스 0x01C00002(RSA EK 인증서)와 0x01C0000A(ECC EK 인증서)에 제조사가 사전 프로비저닝한 EK 인증서를 저장합니다. tpm2_getekcertificate 또는 tpm2_nvread 0x01C00002로 추출할 수 있습니다.

TPM 2.0 핵심 명령어

TPM 2.0은 약 130여 개의 명령을 정의합니다. 여기서는 실무에서 가장 빈번하게 사용되는 핵심 명령을 범주별로 정리합니다.

시작/종료

명령코드설명
TPM2_Startup0x0144TPM 초기화. CLEAR(리셋) 또는 STATE(절전 복귀)
TPM2_Shutdown0x0145TPM 종료 준비. STATE(절전) 또는 CLEAR(재부팅)
TPM2_SelfTest0x0143내부 자가 테스트 실행

키 관리

명령코드설명
TPM2_CreatePrimary0x0131계층의 Primary Seed에서 Primary Key 생성 (EK, SRK 등)
TPM2_Create0x0153부모 키 아래에 자식 키/봉인 데이터 생성
TPM2_Load0x0157생성된 키를 TPM 메모리에 로드 (transient handle 획득)
TPM2_LoadExternal0x0167외부 키를 TPM에 로드 (서명 검증(Signature Verification) 등)
TPM2_EvictControl0x0120transient 키를 persistent 핸들에 영구 저장
TPM2_FlushContext0x0165transient 객체/세션 메모리 해제
TPM2_ContextSave0x0162객체 상태를 외부로 내보내기 (컨텍스트 스왑(Swap))
TPM2_ContextLoad0x0161내보낸 객체 상태를 복원

암호 연산

명령코드설명
TPM2_Sign0x015DTPM 키로 외부 데이터에 서명
TPM2_VerifySignature0x0177로드된 키로 서명 검증
TPM2_RSA_Encrypt0x0174RSA 공개키 암호화 (OAEP, PKCS1_v1_5)
TPM2_RSA_Decrypt0x0159RSA 개인키 복호화(Decryption)
TPM2_ECDH_KeyGen0x0163ECDH 키 쌍 생성, Z 포인트 반환
TPM2_ECDH_ZGen0x0154ECDH 공유 비밀 Z 계산
TPM2_Hash0x017BTPM 내부에서 해시 연산
TPM2_HMAC0x0155TPM 키로 HMAC 연산

봉인/해제 및 증명

명령코드설명
TPM2_Create (seal)0x0153sensitive 영역에 비밀 데이터를 포함하여 봉인 객체 생성
TPM2_Unseal0x015E봉인 객체에서 비밀 데이터 추출 (인가 필요)
TPM2_Quote0x0158PCR 값에 AK로 서명 (원격 증명용)
TPM2_Certify0x0148객체의 공개 영역에 서명 (키 증명)
TPM2_MakeCredential0x0147EK로 credential 래핑 (AK 인증 프로토콜)
TPM2_ActivateCredential0x0147래핑된 credential 풀기 (AK가 이 EK의 TPM에 있음을 증명)

PCR / 난수 / NV / 기능 조회

명령코드설명
TPM2_PCR_Read0x017EPCR 값 읽기
TPM2_PCR_Extend0x0182PCR에 다이제스트 extend
TPM2_PCR_Event0x013CPCR에 이벤트 데이터 해시+extend (모든 뱅크)
TPM2_PCR_Reset0x013DPCR 초기화 (허용된 PCR만)
TPM2_GetRandom0x017B하드웨어 난수 바이트 획득
TPM2_StirRandom0x0146외부 엔트로피를 RNG에 주입
TPM2_NV_DefineSpace0x012ANV 인덱스 정의
TPM2_NV_Write0x0137NV 인덱스에 쓰기
TPM2_NV_Read0x014ENV 인덱스에서 읽기
TPM2_GetCapability0x017ATPM 기능/속성 조회
TPM2_TestParms0x018A알고리즘/파라미터 지원 여부 확인

CreatePrimary → Create → Load → Sign 흐름

# 1. SRK 생성 (Storage Hierarchy Primary Key)
tpm2_createprimary -C o -G rsa2048 -g sha256 -c srk.ctx

# 2. SRK 아래에 서명 키 생성
tpm2_create -C srk.ctx -G rsa2048 -g sha256 \
  -u sign_key.pub -r sign_key.priv \
  -a "sign|fixedtpm|fixedparent|sensitivedataorigin|userwithauth" \
  -p keypass123

# 3. 서명 키를 TPM에 로드
tpm2_load -C srk.ctx -u sign_key.pub -r sign_key.priv -c sign_key.ctx

# 4. 서명 수행
echo "Hello TPM" > message.dat
tpm2_sign -c sign_key.ctx -g sha256 -o signature.dat -p keypass123 message.dat

# 5. 서명 검증
tpm2_verifysignature -c sign_key.ctx -g sha256 -s signature.dat -m message.dat

# 6. 키를 persistent 핸들에 영구 저장 (선택)
tpm2_evictcontrol -C o -c sign_key.ctx 0x81010001

# 이후부터는 0x81010001 핸들로 직접 사용 가능
tpm2_sign -c 0x81010001 -g sha256 -o sig2.dat -p keypass123 message.dat

리눅스 커널 TPM 서브시스템

Linux TPM Software Stack Userspace tpm2-tools Application (FAPI) systemd-cryptenroll Keylime Agent libtss2 (FAPI/ESAPI/SAPI) libtss2-tcti-device /dev/tpmrm0 Kernel Space (drivers/char/tpm/) tpm_dev /dev/tpm0 (raw) tpmrm_dev /dev/tpmrm0 (resource mgr) tpm_space context_buf / session_buf vtpm_proxy vTPM testing struct tpm_chip ops (tpm_class_ops) | dev | timeout_a/b/c/d | flags | tpm_mutex | banks | locality users (kref) | devs (list) | nr_allocated_banks | allocated_banks[] struct tpm_class_ops send() | recv() | status() | cancel() | req_complete_mask | req_canceled | request_locality() | relinquish_locality() Hardware: tpm_tis (TIS/SPI) | tpm_crb (CRB) | tpm_ftpm_tee (fTPM) | tpm_tis_i2c (I2C)

Linux 커널의 TPM 서브시스템은 drivers/char/tpm/에 위치하며, 하드웨어 추상화 레이어를 통해 다양한 TPM 칩과 인터페이스를 지원합니다.

핵심 자료 구조

/* drivers/char/tpm/tpm.h */
struct tpm_chip {
    struct device          dev;
    struct device          devs;           /* /dev/tpm0 */
    struct cdev            cdev;
    struct cdev            cdevs;
    struct rw_semaphore    ops_sem;        /* ops 접근 보호 */
    const struct tpm_class_ops *ops;       /* HW-specific 연산 */
    struct tpm_space       work_space;     /* tpmrm용 작업 공간 */
    unsigned long          timeout_a;      /* TPM_TIMEOUT_A (ms) */
    unsigned long          timeout_b;
    unsigned long          timeout_c;
    unsigned long          timeout_d;
    unsigned long          duration[4];    /* 명령 유형별 최대 실행 시간 */
    unsigned int           flags;          /* TPM_CHIP_FLAG_* */
    int                    dev_num;        /* tpm0, tpm1, ... */
    struct mutex           tpm_mutex;      /* 직렬화 */
    struct tpm_bank_info   *allocated_banks;
    int                    nr_allocated_banks;
    int                    locality;
};

/* 하드웨어별 연산 콜백 */
struct tpm_class_ops {
    unsigned int flags;
    int  (*recv)(struct tpm_chip *chip, u8 *buf, size_t len);
    int  (*send)(struct tpm_chip *chip, u8 *buf, size_t len);
    void (*cancel)(struct tpm_chip *chip);
    u8   (*status)(struct tpm_chip *chip);
    bool (*update_timeouts)(struct tpm_chip *chip, unsigned long *timeout_cap);
    int  (*request_locality)(struct tpm_chip *chip, int loc);
    int  (*relinquish_locality)(struct tpm_chip *chip, int loc);
    u8   req_complete_mask;
    u8   req_complete_val;
    u8   req_canceled;
};

/dev/tpm0 vs /dev/tpmrm0

장치리소스 관리멀티프로세스용도
/dev/tpm0없음 (raw 접근)하나의 프로세스(Process)만 독점레거시, tpm2-abrmd 백엔드
/dev/tpmrm0커널 내장 Resource Manager여러 프로세스 동시 접근기본 권장, context 자동 관리

/dev/tpmrm0는 커널 6.x에서 기본 접근 경로입니다. 각 프로세스가 open()하면 독립적인 tpm_space가 할당되어, transient 객체와 세션의 context save/load를 커널이 자동으로 관리합니다. 이를 통해 프로세스 A의 키 핸들이 프로세스 B에 의해 flush되는 문제가 방지됩니다.

tpm_transmit 흐름

/* drivers/char/tpm/tpm-interface.c */
ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
                     u8 *buf, size_t bufsiz, unsigned int flags)
{
    int rc;

    /* 1. 직렬화: 한 번에 하나의 명령만 */
    rc = tpm_try_get_ops(chip);  /* down_read(&chip->ops_sem) + chip->locality request */

    /* 2. tpm_space: 이전 세션 context 복원 */
    if (space)
        rc = tpm2_prepare_space(chip, space, buf, bufsiz);

    /* 3. 명령 전송 */
    rc = chip->ops->send(chip, buf, count);

    /* 4. 완료 대기 (polling 또는 IRQ) */
    tpm_cmd_ready(chip, status);

    /* 5. 응답 수신 */
    len = chip->ops->recv(chip, buf, bufsiz);

    /* 6. tpm_space: 세션 context 저장 */
    if (space)
        rc = tpm2_commit_space(chip, space, buf, &len);

    /* 7. 직렬화 해제 */
    tpm_put_ops(chip);  /* relinquish_locality + up_read */

    return len;
}
⚠️

커널 내부 TPM 사용: 커널 자체도 TPM을 사용합니다 — IMA의 PCR extend, trusted key 타입의 seal/unseal, 하드웨어 RNG 등. 이들은 tpm_chip_find_get()으로 칩을 찾고 tpm_send()/tpm2_get_random() 등의 내부 API를 호출합니다. 자세한 내용은 키링(Keyring) 문서의 trusted 키 타입을 참고하세요.

sysfs 인터페이스

커널은 TPM 칩 정보를 sysfs를 통해 사용자 공간에 노출합니다. 주요 디렉토리는 /sys/class/tpm/tpm0//sys/kernel/security/tpm0/입니다:

sysfs 경로내용설명
/sys/class/tpm/tpm0/tpm_version_major2TPM 스펙 주 버전 (1 또는 2)
/sys/class/tpm/tpm0/device/descriptionTPM 2.0 DeviceTPM 장치 설명 문자열
/sys/class/tpm/tpm0/caps제조사, 버전TPM 1.2 호환용 기능 정보
/sys/class/tpm/tpm0/durationsshort/med/long ms명령 유형별 최대 실행 시간
/sys/class/tpm/tpm0/timeoutsA/B/C/D ms통신 타임아웃 값 4종
/sys/class/tpm/tpm0/pcrsPCR 값 목록TPM 1.2 전용, 2.0은 tpm2_pcrread 사용
# TPM 버전 확인
cat /sys/class/tpm/tpm0/tpm_version_major
# 출력: 2

# TPM 디바이스 설명
cat /sys/class/tpm/tpm0/device/description
# 출력: TPM 2.0 Device

# 타임아웃/duration 값 확인 (ms 단위)
cat /sys/class/tpm/tpm0/timeouts
# 출력: 750 2000 200 30 [original]

cat /sys/class/tpm/tpm0/durations
# 출력: 200000 400000 2000000 [original]

# securityfs를 통한 이벤트 로그 접근
ls -la /sys/kernel/security/tpm0/
# binary_bios_measurements  — 바이너리 TCG 이벤트 로그
# ascii_bios_measurements   — 텍스트 형식 이벤트 로그 (레거시)

TPM 이벤트 로그 파싱

TPM Event Log Parsing Flow Firmware (UEFI/BIOS) 부팅 각 단계를 측정 PCR extend + Event Log 기록 → ACPI/EFI/DT로 로그 주소 전달 Kernel (tpm_bios_log) tpm_read_log_efi() tpm_read_log_acpi() tpm_read_log_of() securityfs /sys/kernel/security/tpm0/ binary_bios_measurements ascii_bios_measurements TCG_PCR_EVENT2 Structure (Crypto Agile) PCRIndex u32 (0-23) EventType u32 (EV_*) Count u32 Digests[Count] {AlgID(u16) + Digest(가변)} × Count EventSize u32 EventData u8[EventSize] SHA-1 뱅크: AlgID=0x0004, DigestSize=20 바이트 SHA-256 뱅크: AlgID=0x000B, DigestSize=32 바이트 SHA-384 뱅크: AlgID=0x000C, DigestSize=48 바이트 첫 번째 이벤트는 항상 TCG_EfiSpecIDEvent (Spec ID Event03 — Crypto Agile 로그 시작 표시)

커널은 부팅 과정에서 ACPI, EFI, 또는 Device Tree 경로를 통해 펌웨어가 작성한 이벤트 로그를 읽어 securityfs에 노출합니다. 이벤트 로그 소스를 탐색하는 우선순위(Priority)는 다음과 같습니다:

  1. tpm_read_log_efi() — EFI 변수 EventLogAddr/EventLogSize에서 로그 주소와 크기를 읽음 (UEFI 시스템 우선)
  2. tpm_read_log_acpi() — ACPI TCPA/TPM2 테이블의 로그 영역에서 읽음
  3. tpm_read_log_of() — Device Tree의 linux,sml-base/linux,sml-size 프로퍼티에서 읽음 (ARM/PowerPC)
/* drivers/char/tpm/eventlog/common.c — TCG Crypto Agile 이벤트 구조 */
struct tcg_pcr_event2_head {
    u32  pcr_idx;        /* PCR 인덱스 (0-23) */
    u32  event_type;     /* EV_* 상수 */
    u32  count;          /* 해시 알고리즘 개수 */
    /* 이후 count × { alg_id(u16) + digest(가변) } */
    /* 마지막: event_size(u32) + event_data(가변) */
};

/* SHA-256 Crypto Agile 로그 예시 (count=2인 경우) */
/* pcr_idx=7 | event_type=EV_EFI_VARIABLE_DRIVER_CONFIG
 * | count=2
 * | alg_id=0x0004 (SHA-1)   | digest[20]
 * | alg_id=0x000B (SHA-256) | digest[32]
 * | event_size | event_data (EFI 변수 이름 + 값)
 */

주요 이벤트 타입

이벤트 타입PCR설명
EV_S_CRTM_VERSION0x000000080CRTM 버전 (펌웨어 초기 코드)
EV_POST_CODE0x000000010-1POST 코드/BIOS 모듈
EV_SEPARATOR0x000000040-7부팅 단계 경계 (Pre-OS → OS 전환)
EV_EFI_VARIABLE_DRIVER_CONFIG0x800000011,3,5,7EFI 변수 (SecureBoot, PK, KEK, db, dbx)
EV_EFI_BOOT_SERVICES_APPLICATION0x800000034EFI 부트 서비스 애플리케이션 (shim, GRUB)
EV_EFI_ACTION0x800000071,2,3,4,5,6EFI 액션 이벤트 문자열
EV_EFI_VARIABLE_AUTHORITY0x800000E07Secure Boot 검증에 사용된 인증서/해시
EV_EFI_GPT_EVENT0x800000065GPT 파티션 테이블 해시
EV_IPL0x0000000D4,8,9부트로더/커널/initrd 측정
EV_EFI_HCRTM_EVENT0x800000100호스트 플랫폼 CRTM

TPM 칩 드라이버 등록(Driver Registration) 흐름

TPM 하드웨어 드라이버(tpm_tis, tpm_crb 등)가 프로브(Probe)되면 다음과 같은 등록 흐름을 거칩니다:

/* TPM 칩 등록 흐름 (drivers/char/tpm/tpm-chip.c) */

/* 1단계: 칩 구조체 할당 */
chip = tpmm_chip_alloc(dev, ops);
/*   - devm_kzalloc으로 tpm_chip 할당
 *   - dev_num 할당 (ida_alloc)
 *   - mutex, ops_sem 초기화
 *   - cdev 초기화 */

/* 2단계: TPM 2.0 프로브 */
rc = tpm2_probe(chip);
/*   - TPM2_GetCapability(TPM_PT_TOTAL_COMMANDS) 전송
 *   - 응답 성공 → TPM_CHIP_FLAG_TPM2 설정
 *   - 응답 실패 → TPM 1.2로 취급 */

/* 3단계: 자동 시작 */
rc = tpm2_auto_startup(chip);
/*   - tpm2_startup(TPM2_SU_CLEAR) — TPM 초기화
 *   - tpm2_do_selftest() — 자체 테스트
 *   - tpm2_get_pcr_allocation() — 활성 PCR 뱅크 조회
 *   - tpm_get_timeouts() — 타임아웃 값 획득 */

/* 4단계: 칩 등록 */
rc = tpm_chip_register(chip);
/*   - tpm_add_char_device() — /dev/tpm0 + /dev/tpmrm0 생성
 *   - tpm_add_hwrng() — hwrng 프레임워크에 TPM RNG 등록
 *   - tpm_bios_log_setup() — securityfs 이벤트 로그 노출
 *   - tpm_add_ppi() — Physical Presence Interface sysfs */

커널 내부 TPM API

커널의 다른 서브시스템이 TPM 기능을 사용할 때 호출하는 주요 내부 API입니다:

함수용도호출자
tpm_chip_find_get()기본 TPM 칩 참조 획득IMA, trusted keys, hwrng
tpm_pcr_read()PCR 값 읽기IMA (boot_aggregate 계산)
tpm_pcr_extend()PCR extend 연산IMA (파일 측정), 커널 부팅
tpm_get_random()TPM RNG에서 난수 획득hwrng 프레임워크
tpm_send()임의 TPM 명령 전송trusted keys (seal/unseal)
tpm_is_tpm2()TPM 2.0 여부 확인여러 서브시스템
tpm_chip_put()TPM 칩 참조 해제사용 완료 후
/* IMA에서의 TPM 사용 예시 (security/integrity/ima/ima_init.c) */
static int ima_init_tpm(void)
{
    /* 기본 TPM 칩 찾기 */
    ima_tpm_chip = tpm_default_chip();
    if (!ima_tpm_chip)
        pr_info("No TPM chip found, activating TPM-bypass!\n");
    return 0;
}

/* boot_aggregate 계산 — PCR 0-7 읽어서 해시 */
static int ima_calc_boot_aggregate(struct ima_digest_data *hash)
{
    struct tpm_digest d;
    int i, rc;

    for (i = 0; i <= 7; i++) {
        rc = tpm_pcr_read(ima_tpm_chip, i, &d);
        /* PCR 값을 연결하여 해시 업데이트 */
        crypto_shash_update(shash, d.digest, d.digest_size);
    }
    crypto_shash_final(shash, hash->digest);
    return 0;
}
💡

devm 관리: tpmm_chip_alloc()devm_ 접두사 함수를 사용하여, 드라이버 언바인딩 시 tpm_chip 구조체와 관련 리소스가 자동으로 정리됩니다. 이는 에러 경로에서의 메모리 누수를 방지합니다.

CRB vs TIS 인터페이스

TIS (FIFO) vs CRB Register Layout TIS (TPM Interface Specification) 0x0000 TPM_ACCESS (Locality 제어) 0x0008 TPM_INT_ENABLE (인터럽트 활성화) 0x000C TPM_INT_VECTOR (인터럽트 벡터) 0x0010 TPM_INT_STATUS (인터럽트 상태) 0x0014 TPM_INTF_CAPABILITY (인터페이스 기능) 0x0018 TPM_STS (상태/명령 제어) 0x0024 TPM_DATA_FIFO (데이터 입출력) 0x0030 TPM_INTERFACE_ID (인터페이스 식별) 0x0080 TPM_XDATA_FIFO (확장 FIFO) 전송 방식: 바이트 단위 FIFO 읽기/쓰기 Locality × 0x1000 오프셋 (Loc0=0x0000, Loc1=0x1000...) STS 레지스터 폴링으로 상태 전환 관리 흐름: requestLocality → write FIFO → poll STS → commandReady → read FIFO → releaseLocality 드라이버: tpm_tis_core.c, tpm_tis_spi_main.c CRB (Command Response Buffer) 0x0000 CRB_LOC_STATE (Locality 상태) 0x0008 CRB_LOC_CTRL (Locality 제어) 0x000C CRB_LOC_STS (Locality 상태) 0x0040 CRB_CTRL_REQ (제어 요청) 0x0044 CRB_CTRL_STS (제어 상태) 0x0048 CRB_CTRL_CANCEL (명령 취소) 0x004C CRB_CTRL_START (명령 시작) 0x0058 CRB_CTRL_CMD_SIZE (명령 버퍼 크기) 0x005C CRB_CTRL_CMD_ADDR (명령 버퍼 주소) 0x0064 CRB_CTRL_RSP_SIZE (응답 버퍼 크기) 0x0068 CRB_CTRL_RSP_ADDR (응답 버퍼 주소) 0x0080 CRB_DATA_BUFFER (공유 명령/응답) 전송 방식: 메모리 매핑 버퍼 직접 접근 FIFO 불필요, DMA 가능, fTPM 기본 인터페이스 드라이버: tpm_crb.c

TPM과 호스트 사이의 통신 인터페이스는 TIS(FIFO 기반)와 CRB(Command Response Buffer, 메모리 매핑(Mapping) 기반) 두 가지가 있습니다.

ACPI TPM2 테이블

시스템 펌웨어는 ACPI TPM2 테이블을 통해 TPM의 인터페이스 유형과 주소를 커널에 알려줍니다. StartMethod 필드가 인터페이스 유형을 결정합니다:

StartMethod인터페이스비고
ACPI Start2TISACPI _DSM 메서드 호출
CRB7CRBfTPM 기본, 최신 플랫폼
CRB with ACPI Start8CRBCRB + ACPI 초기화
TIS 1.36TIS레거시 TIS
비교 항목TIS (FIFO)CRB
데이터 전송바이트 단위 FIFO R/W메모리 매핑 버퍼(Buffer) 직접 접근
프로토콜 오버헤드(Overhead)높음 (STS 폴링(Polling), 바이트 루프)낮음 (단일 START 트리거)
DMA 지원불가가능 (CMD/RSP 주소 분리)
명령 버퍼FIFO 공유 (명령/응답 교대)명령/응답 버퍼 분리 가능
fTPM 호환에뮬레이션 필요기본 지원
Locality0x1000 × N 오프셋(Offset)LOC_CTRL 레지스터
대표 용도dTPM (SPI/LPC), 레거시fTPM (Intel PTT, AMD PSP)
# ACPI TPM2 테이블 확인
sudo cat /sys/firmware/acpi/tables/TPM2 | hexdump -C | head -5

# dmesg에서 TPM 인터페이스 확인
dmesg | grep -i tpm
# 예: tpm_crb MSFT0101:00: [Firmware Bug]: ACPI table has 0 start method
# 예: tpm_tis 00:05: 2.0 TPM (device-id 0x1B, rev-id 16)

# TPM 칩 정보 확인
cat /sys/class/tpm/tpm0/device/description
cat /sys/class/tpm/tpm0/tpm_version_major

TIS 상세 프로토콜

TIS FIFO Command Transmission Protocol 1. Request TPM_ACCESS requestUse=1 2. cmdReady TPM_STS commandReady=1 3. Write FIFO TPM_DATA_FIFO 바이트 단위 쓰기 4. tpmGo TPM_STS tpmGo=1 5. Poll STS dataAvail 대기 (또는 IRQ) 6. Read FIFO TPM_DATA_FIFO 응답 바이트 읽기 TPM_STS Register Bits Bit 0: reserved | Bit 1: responseRetry | Bit 2: selfTestDone | Bit 3: expect (더 많은 데이터 기대) Bit 4: dataAvail (응답 준비) | Bit 5: tpmGo (실행 트리거) | Bit 6: commandReady | Bit 7: stsValid TPM_ACCESS Register Bits Bit 0: tpmEstablishment | Bit 1: requestUse (locality 요청) | Bit 2: pendingRequest Bit 3: seize (강제 탈취) | Bit 4: beenSeized | Bit 5: activeLocality | Bit 7: tpmRegValidSts

TIS(FIFO) 인터페이스는 바이트 단위 FIFO를 통해 TPM과 통신합니다. 각 locality는 0x1000 바이트 오프셋으로 분리되며, 하나의 명령 전송은 다음 단계를 거칩니다:

  1. Locality 요청: TPM_ACCESS 레지스터에 requestUse 비트를 설정하고, activeLocality 비트가 설정될 때까지 폴링
  2. 명령 준비: TPM_STScommandReady 비트 설정 → TPM이 명령 수신 준비 상태로 전환
  3. 명령 쓰기: TPM_DATA_FIFO에 명령 바이트를 순차적으로 쓰기, 각 쓰기 후 TPM_STS.expect 비트로 추가 데이터 필요 여부 확인
  4. 실행 트리거: TPM_STStpmGo 비트 설정 → TPM이 명령 처리 시작
  5. 완료 대기: TPM_STS.dataAvail 비트 폴링 (또는 인터럽트(Interrupt) 대기)
  6. 응답 읽기: TPM_DATA_FIFO에서 응답 바이트를 순차적으로 읽기
  7. Locality 반환: TPM_ACCESSactiveLocality 비트 설정하여 반환

TIS Locality 관리

Locality주소 범위용도사용 주체
Locality 00xFED40000SRTM (Static Root of Trust)BIOS/UEFI, 일반 OS
Locality 10xFED41000예약 (TCG 정의)환경별 정의
Locality 20xFED42000DRTM (Dynamic Root of Trust)Intel TXT ACM, AMD SKINIT
Locality 30xFED43000Maintenance / AUX펌웨어 보조
Locality 40xFED44000특수 (Intel TXT 전용)Intel TXT SINIT ACM

SPI TPM 전송

현대 dTPM은 대부분 SPI 버스를 사용합니다. SPI TPM은 표준 TIS 레지스터 레이아웃을 SPI 프레임으로 래핑합니다:

/* SPI TPM 프레임 포맷 (TCG PC Client Platform TPM Profile Spec) */
/* Byte 0: R/W (0=Write, 1=Read) | Transfer size (6 bits) */
/* Byte 1-3: 24-bit TIS 레지스터 주소 */
/* Wait State: TPM이 준비되지 않으면 MISO=0 반환 (wait polling) */
/*            MISO=1이면 데이터 전송 시작 */
/* Data: 실제 레지스터 데이터 (1-64 바이트) */

/* drivers/char/tpm/tpm_tis_spi_main.c — SPI wait-state 처리 */
static int tpm_tis_spi_flow(struct tpm_tis_spi_phy *phy,
                            u32 addr, u16 len, u8 *buf,
                            u8 direction)
{
    /* 1. 4바이트 헤더 전송 (방향 + 크기 + 주소) */
    /* 2. wait-state 폴링: TPM이 MISO에 0x01을 반환할 때까지 */
    /*    최대 timeout 동안 SPI 클럭 계속 공급 */
    /* 3. 데이터 전송/수신 */
}
ℹ️

SPI clock stretching: SPI TPM에서 wait-state 폴링은 TPM이 내부 연산을 완료할 때까지 SPI 클럭을 계속 공급해야 합니다. 이 과정에서 SPI 버스가 점유되므로, 같은 SPI 버스에 연결된 다른 디바이스(SPI 플래시 등)의 접근이 지연(Latency)될 수 있습니다. 일부 SoC에서는 이것이 fTPM 지연 문제의 원인이 됩니다.

CRB 상세 프로토콜

CRB State Machine Idle tpmIdle=1 cmdReady 명령 수신 가능 Executing CRB_START=1 Complete CRB_START=0 cmdReady START=1 START→0 goIdle (CTRL_REQ.goIdle=1) Cancel (CTRL_CANCEL=1)

CRB(Command Response Buffer)는 메모리 매핑 버퍼를 사용하여 FIFO 오버헤드를 제거합니다. CRB의 명령 전송 흐름은 다음과 같습니다:

  1. goIdle → cmdReady 전환: CRB_CTRL_REQcmdReady 비트를 설정하고, CRB_CTRL_STS에서 tpmIdle 비트가 클리어될 때까지 대기
  2. 명령 쓰기: CRB_CTRL_CMD_ADDR이 가리키는 메모리 버퍼에 명령 바이트를 직접 복사 (memcpy 한 번)
  3. 실행 트리거: CRB_CTRL_STARTStart 비트를 설정
  4. 완료 대기: CRB_CTRL_STARTStart 비트가 클리어될 때까지 폴링
  5. 응답 읽기: CRB_CTRL_RSP_ADDR이 가리키는 메모리 버퍼에서 응답을 직접 읽기
  6. goIdle: CRB_CTRL_REQgoIdle 비트를 설정하여 TPM을 유휴 상태(Idle State)로 전환
/* drivers/char/tpm/tpm_crb.c — CRB 명령 전송 */
static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
{
    struct crb_priv *priv = dev_get_drvdata(&chip->dev);

    /* goIdle → cmdReady 상태 전환 */
    crb_cmd_ready(chip);

    /* 명령 버퍼에 직접 복사 (FIFO 없이 단일 memcpy) */
    memcpy_toio(priv->cmd, buf, len);

    /* CRB_START 비트 설정 — TPM 명령 실행 시작 */
    iowrite32(CRB_START_INVOKE,
              &priv->regs_t->ctrl_start);

    return 0;
}

ACPI TPM2 테이블 상세

/* ACPI TPM2 테이블 구조 (TCG ACPI Specification) */
struct acpi_table_tpm2 {
    struct acpi_table_header header;  /* 표준 ACPI 헤더 */
    u16  platform_class;   /* 0=PC Client, 1=Server */
    u16  reserved;
    u64  control_address;  /* CRB 제어 영역 주소 또는 TIS 베이스 */
    u32  start_method;     /* StartMethod 값 (아래 표 참조) */
    /* start_method별 Platform-Specific Parameters (최대 12바이트) */
    u8   start_method_params[12];
    /* 선택적: Log Area Minimum Length + Log Area Start Address */
};

/* 커널의 ACPI TPM2 테이블 파싱 (drivers/char/tpm/tpm_crb.c) */
static int crb_acpi_add(struct acpi_device *device)
{
    struct acpi_table_tpm2 *buf;
    acpi_status st;

    st = acpi_get_table(ACPI_SIG_TPM2, 1,
                        (struct acpi_table_header **)&buf);
    /* buf->start_method에 따라:
     * 7 (CRB) → crb_map_io() 호출
     * 8 (CRB with ACPI Start) → ACPI _DSM 메서드도 호출
     * 6 (TIS) → tpm_tis 드라이버로 위임 */
}
StartMethod 값이름동작 방식대표 플랫폼
0Reserved사용 금지-
2ACPI StartACPI _DSM 메서드로 명령 전송레거시 dTPM
6TIS 1.3메모리 매핑 TIS FIFO (0xFED40000)dTPM (LPC/SPI)
7CRBCRB 레지스터 직접 접근Intel PTT, AMD fTPM
8CRB with ACPI StartCRB + ACPI _DSM 초기화일부 ARM 서버
9CRB with SMCCRB + ARM SMC 호출ARM TrustZone fTPM
11CRB with ARM FF-ACRB + Firmware Framework for ArmARM FF-A fTPM
💡

인터럽트 지원: TIS는 TPM_INT_ENABLE/TPM_INT_STATUS/TPM_INT_VECTOR 레지스터를 통해 인터럽트를 지원하지만, 많은 실무 환경에서 인터럽트가 불안정하여 기본적으로 폴링 모드로 동작합니다. 커널 파라미터 tpm_tis.interrupts=0이 기본값이며, 인터럽트를 활성화하려면 tpm_tis.interrupts=1로 설정합니다. CRB는 인터럽트 대신 CRB_CTRL_START 비트 폴링만 사용합니다.

TPM 드라이버 내부 구현

TPM 드라이버의 내부 구현을 커널 소스 기반으로 분석합니다. 명령 전송, 리소스 관리, 전원 관리(Power Management)의 주요 경로를 추적합니다.

tpm_transmit() 상세 흐름

모든 TPM 명령은 tpm_transmit()를 통해 직렬화(Serialization)됩니다. 이 함수는 다음 단계를 수행합니다:

  1. Ops 획득: tpm_try_get_ops()chip->ops_sem 읽기 잠금 획득, locality 요청
  2. Space 준비: tpm2_prepare_space() — tpmrm의 경우, 이전 세션/객체 context를 TPM에 복원
  3. 명령 전송: chip->ops->send() — 하드웨어별 전송 (TIS FIFO 쓰기 또는 CRB 버퍼 복사)
  4. 완료 대기: tpm_cmd_ready() — STS 레지스터 폴링 또는 IRQ 대기
  5. 응답 수신: chip->ops->recv() — 하드웨어별 수신
  6. Space 커밋: tpm2_commit_space() — 세션/객체 context를 space 버퍼에 저장
  7. Ops 해제: tpm_put_ops() — locality 반환, 읽기 잠금 해제

Timeout 관리

TPM은 4가지 타임아웃 값을 자체 보고합니다:

타임아웃용도일반적인 값
TIMEOUT_A명령 수신 확인750 ms
TIMEOUT_B명령 처리 대기2000 ms
TIMEOUT_C긴 명령 (키 생성)200 ms (기본), 실제 수 초
TIMEOUT_Dlocality 획득30 ms

명령 유형별 duration은 TPM2_GetCapability(TPM_CAP_TPM_PROPERTIES, TPM_PT_MAX_COMMAND_SIZE)TPM2_SelfTest 결과로 결정됩니다. RSA-2048 키 생성(TPM2_CreatePrimary)은 dTPM에서 수 초가 소요될 수 있습니다.

tpm_space 리소스 관리

/* drivers/char/tpm/tpm.h */
struct tpm_space {
    u32  context_tbl[3];       /* transient object 핸들 (최대 3개) */
    u8   *context_buf;           /* ContextSave 결과 저장 */
    u32  session_tbl[64];      /* 세션 핸들 테이블 */
    u8   *session_buf;           /* 세션 ContextSave 결과 */
    u32  buf_size;               /* 버퍼 크기 */
};

/* tpmrm open 시 호출 — 프로세스별 독립 공간 할당 */
int tpm2_init_space(struct tpm_space *space, unsigned int buf_size)
{
    space->context_buf = kzalloc(buf_size, GFP_KERNEL);
    space->session_buf = kzalloc(buf_size, GFP_KERNEL);
    return 0;
}

/dev/tpmrm0open()하면 tpm2_init_space()가 호출되어 해당 file descriptor에 전용 tpm_space가 할당됩니다. 명령 전송 전에 tpm2_prepare_space()가 이전에 저장된 context를 TPM에 복원하고, 응답 수신 후 tpm2_commit_space()가 현재 context를 다시 저장합니다. 이 과정이 프로세스 간 핸들 충돌을 방지합니다.

전원 관리

/* 절전(suspend) 시 */
int tpm_pm_suspend(struct device *dev)
{
    struct tpm_chip *chip = dev_get_drvdata(dev);

    /* TPM2_Shutdown(STATE) — 상태 보존 모드로 종료 */
    tpm2_shutdown(chip, TPM2_SU_STATE);
    return 0;
}

/* 복귀(resume) 시 */
int tpm_pm_resume(struct device *dev)
{
    struct tpm_chip *chip = dev_get_drvdata(dev);

    /* TPM2_Startup(STATE) — 보존된 상태로 재개 */
    tpm2_startup(chip, TPM2_SU_STATE);
    return 0;
}
ℹ️

tpm_chip_register 흐름: TPM 드라이버 프로브 시 tpmm_chip_alloc()tpm_chip_register()가 호출됩니다. 이 과정에서 tpm2_probe()로 TPM 2.0 여부를 확인하고, tpm2_auto_startup()으로 Startup/SelfTest를 실행하며, tpm_add_char_device()/dev/tpm0/dev/tpmrm0를 등록합니다.

dTPM vs fTPM vs sTPM 비교

TPM Implementation Comparison dTPM (Discrete) 독립 칩 (SPI/I2C/LPC) 보안 경계: 전용 실리콘 공격면: 물리적 프로브, 버스 성능: 느림 (~수 ms/op) 인증: CC EAL4+, FIPS L2-3 비용: BOM 추가 ($1-5) 리셋: 물리적 접근 필요 대표: Infineon SLB 9670 Nuvoton NPCT75x STMicro ST33TPHF2x 용도: 서버, HSM, 금융 fTPM (Firmware) TEE 내부 실행 보안 경계: TEE 격리 공격면: TEE 취약점, 사이드채널 성능: 중간 (메모리 버스) 인증: FIPS 140-2 L1-2 비용: 추가 비용 없음 리셋: 펌웨어 업데이트 대표: Intel PTT (CSME) AMD PSP fTPM OP-TEE fTPM (ARM) 용도: 소비자 노트북, IoT sTPM (Software) 순수 소프트웨어 에뮬레이션 보안 경계: 프로세스 격리 공격면: 전체 OS 공격면 성능: 가장 빠름 인증: 인증 불가 비용: 무료 리셋: 상태 파일 삭제 대표: swtpm (libtpms) IBM TPM Simulator vtpm_proxy (커널) 용도: 개발, 테스트, CI/CD

fTPM 취약점(Vulnerability) 사례

취약점년도영향설명
AMD fTPM 타이밍 공격2023AMD Zen 2/3fTPM의 ECDSA 서명 타이밍에서 비밀 키 추출 가능 (faulTPM)
Intel CSME 취약점 (CVE-2019-0090)2019Intel 2016 이전 칩셋CSME ROM 취약점으로 PTT fTPM 키 추출 가능
AMD PSP 버그 (CVE-2021-26311)2021AMD EPYCPSP 메모리 손상으로 fTPM 상태 변조 가능
TPM.FAIL (CVE-2019-11090)2019Intel PTT, STMicro dTPMECDSA nonce 타이밍 공격으로 키 복구
⚠️

AMD fTPM 랜덤 지연 현상: AMD Zen 3 플랫폼에서 fTPM이 간헐적으로 수백 밀리초의 지연(stutter)을 유발하는 현상이 보고되었습니다. 이는 fTPM이 SPI 플래시에 NV 데이터를 쓸 때 발생하며, 오디오 끊김이나 마우스 지연으로 나타납니다. BIOS에서 fTPM을 dTPM으로 전환하면 해결되지만, dTPM이 없는 시스템에서는 tpm_tis.interrupts=0 커널 파라미터로 완화할 수 있습니다.

ℹ️

ARM TrustZone fTPM: ARM 기반 플랫폼에서는 OP-TEE(Open Portable Trusted Execution Environment) 위에 fTPM을 구현합니다. 커널의 CONFIG_TCG_FTPM_TEE 드라이버가 TEE Client API를 통해 OP-TEE fTPM TA(Trusted Application)와 통신합니다. 자세한 내용은 ARM TrustZone & OP-TEE 문서를 참고하세요.

fTPM 보안 분석 상세

취약점/이슈CVE/ID년도영향 플랫폼공격 유형완화/수정
faulTPM-2023AMD Zen 2/3전압 결함 주입 (voltage glitching)으로 fTPM 키 추출물리적 접근 필요, 펌웨어 업데이트로 일부 완화
TPM-FAIL ECDSACVE-2019-110902019Intel PTT, STMicro ST33ECDSA nonce 생성의 타이밍 편차에서 비밀키 복구 (격자 공격)TPM 펌웨어 업데이트, 상수 시간 구현 적용
Intel CSME ROMCVE-2019-00902019Intel 10세대 이전 칩셋CSME ROM 취약점으로 PTT fTPM 루트 키 추출칩셋 교체 필요 (ROM 패치(Patch) 불가)
Intel SA-00391CVE-2020-87052020Intel 다수 플랫폼CSME/SPS/TXE 메모리 손상으로 특권 상승CSME 펌웨어 업데이트
AMD fTPM stutterSB-10292022AMD Zen 3 데스크톱보안이 아닌 성능 이슈 — fTPM NV 쓰기 시 수백 ms 지연dTPM 전환, BIOS 업데이트, 또는 tpm_tis.interrupts=0
AMD PSP SEVCVE-2021-263112021AMD EPYCPSP 메모리 손상으로 fTPM 상태 변조 가능PSP 펌웨어 업데이트
⚠️

fTPM 사이드 채널 완화: fTPM은 메인 CPU와 메모리를 공유하는 경우(Intel PTT는 CSME 별도 코어이지만, AMD PSP도 전용 코어), 캐시(Cache) 타이밍 공격에 노출될 수 있습니다. 완화 방법: (1) TPM 펌웨어의 상수 시간(constant-time) 암호 연산 구현, (2) Intel CSME의 캐시 파티셔닝, (3) AMD PSP의 메모리 격리(Isolation). 높은 보안이 요구되는 환경에서는 CC EAL4+ 인증을 받은 dTPM을 권장합니다.

dTPM 물리 보안

dTPM Physical Attack Vectors Host CPU SPI Controller 메모리, OS SPI Bus (MOSI/MISO/CLK/CS) dTPM Chip 보안 실리콘 키, PCR, NV SPI 스니핑 (TPM Genie) FPGA 인터포저 (중간자 공격) 전력/EM 사이드채널 방어 메커니즘 파라미터 암호화 세션 HMAC 인증 물리적 탬퍼 검출 액티브 실드 글리치 센서 TPM 2.0 파라미터 암호화 (Encrypt Session): SPI 버스의 명령/응답 데이터를 AES 암호화하여 스니핑 무력화 CC EAL4+ 인증 칩: 전력 분석, 글리칭, 디캡 공격에 대한 물리적 방어 포함

dTPM은 별도의 물리적 칩이므로, 호스트 CPU와의 통신 버스(SPI/LPC/I2C)를 물리적으로 도청(sniffing)하거나 중간자 공격(interposer)을 수행할 수 있습니다:

공격 방법난이도필요 장비대응
SPI 버스 스니핑 (TPM Genie)중간로직 분석기, SPI 프로브버스 암호화 (TPM 2.0 파라미터 암호화 세션)
LPC 버스 인터포저중간FPGA 보드, LPC 어댑터SPI TPM으로 전환, 물리적 접근 통제
전원 분석 (SPA/DPA)높음오실로스코프, 전류 프로브전력 필터링, 마스킹 구현 (CC 인증 칩)
글리칭 (전압/클럭)높음ChipWhisperer, 전압 인젝터글리치 검출기, 이상 전압 리셋 (CC 인증 칩)
디캡 (실리콘 개봉)매우 높음화학 에칭, SEM/FIB액티브 실드, 메모리 암호화 (EAL5+ 칩)

dTPM 칩 제조사 비교

제조사칩 시리즈인터페이스인증특징
InfineonSLB 9670/9672SPI, I2CCC EAL4+, FIPS 140-2 L2가장 널리 사용, 서버/데스크톱 주류
NuvotonNPCT750/755SPI, I2CCC EAL4+, FIPS 140-2 L2Chromebook에서 많이 사용, Google 협력
STMicroelectronicsST33TPHF2ESPISPI, I2CCC EAL4+, FIPS 140-2 L2자동차/산업용 온도 범위 지원
MicrochipATECC608B-TNGTPMI2CFIPS 140-2 L3IoT/임베디드 특화, 저전력
GoogleCr50/Ti50 (Titan C)SPI, I2CFIPS 140-2 L1Chromebook 전용, Google 커스텀 펌웨어

실무 선택 가이드

기준dTPMfTPMsTPM
보안 요구 수준높음~최고 (금융, 정부, 규제)중간 (일반 기업, 소비자)없음 (개발/테스트 전용)
비용칩당 $1-5 + BOM추가 비용 없음무료
물리적 공격 저항높음 (전용 보안 실리콘)중간 (TEE 의존)없음
소프트웨어 공격 저항높음 (격리된 실행 환경)중간 (CPU 취약점 연쇄 가능)없음 (동일 OS 내)
성능느림 (버스 지연)중간빠름
교체 유연성하드웨어 교체 필요펌웨어 업데이트소프트웨어 업데이트
인증 획득 가능CC EAL4+, FIPS L2-3FIPS L1-2불가
💡

Multi-TPM 시나리오: 일부 서버 플랫폼은 dTPM과 fTPM을 동시에 장착합니다. 이 경우 BIOS에서 어떤 TPM을 기본 장치로 노출할지 선택합니다. Linux 커널은 /dev/tpm0으로 첫 번째 검출된 TPM을, 두 번째를 /dev/tpm1으로 등록합니다. 단, 대부분의 소프트웨어(systemd-cryptenroll, Keylime 등)는 tpm0만 사용하므로, BIOS에서 주 TPM을 올바르게 설정해야 합니다.

Measured Boot와 TPM

Measured Boot Chain SRTM (Static Root of Trust for Measurement) CRTM/BIOS ROM 변경 불가 (RoT) → PCR 0 UEFI 펌웨어 설정 → PCR 1 OpROM → PCR 2,3 부트로더 GRUB/sd-boot → PCR 4,5 커널/initrd 커널 → PCR 8,9 cmdline → PCR 8 IMA (커널 내부) 파일 실행/읽기 → PCR 10 PCR 7: Secure Boot 정책 (PK, KEK, db, dbx, MokList, SbatLevel) — 정책 변경 시 PCR 7 값 변동 DRTM (Dynamic Root of Trust for Measurement) Intel TXT GETSEC[SENTER] 명령 ACM → Measured Launch AMD SKINIT SKINIT 명령 Secure Loader Block DRTM PCR 할당 PCR 17: DRTM 코드 PCR 18-22: DRTM 정책/상태 TCG Crypto Agile Event Log TCG_PCR_EVENT2: { PCRIndex | EventType | Digests[SHA-1, SHA-256, ...] | EventSize | Event[] } EventType: EV_S_CRTM_VERSION | EV_POST_CODE | EV_EFI_VARIABLE_DRIVER_CONFIG | EV_EFI_BOOT_SERVICES_APPLICATION EV_EFI_ACTION | EV_SEPARATOR | EV_IPL | EV_EFI_VARIABLE_AUTHORITY | EV_EFI_GPT_EVENT PCR Replay: 이벤트 로그에서 PCR 값을 재계산하여 TPM의 PCR 값과 비교 → 무결성 검증

Measured Boot는 시스템 부팅 과정의 각 단계를 TPM PCR에 측정하여, 나중에 부팅 과정이 변조되지 않았음을 검증할 수 있게 합니다.

SRTM (Static Root of Trust for Measurement)

SRTM은 CPU 리셋부터 시작하는 측정 체인입니다. CRTM(Core Root of Trust for Measurement) — 일반적으로 BIOS/UEFI ROM의 최초 실행 코드 — 이 신뢰의 출발점이며, 이후 각 단계가 다음 단계를 PCR에 기록합니다. SRTM의 한계는 CRTM 자체의 무결성이 전제 조건이라는 점입니다.

DRTM (Dynamic Root of Trust for Measurement)

DRTM은 런타임에 새로운 신뢰 기반을 설정합니다. Intel TXT의 GETSEC[SENTER] 또는 AMD의 SKINIT 명령이 CPU 하드웨어 레벨에서 모든 코어를 정지시키고, 서명된 ACM(Authenticated Code Module)을 실행하여 깨끗한 측정 환경을 만듭니다. DRTM은 PCR 17-22를 사용하며, SRTM과 독립적으로 동작합니다.

이벤트 로그 읽기

# 바이너리 이벤트 로그 위치 (securityfs)
ls -la /sys/kernel/security/tpm0/binary_bios_measurements

# tpm2-tools로 이벤트 로그 파싱
tpm2_eventlog /sys/kernel/security/tpm0/binary_bios_measurements

# 특정 PCR에 대한 이벤트만 필터
tpm2_eventlog /sys/kernel/security/tpm0/binary_bios_measurements \
  | grep -A5 "PCRIndex: 7"

# PCR Replay 검증: 이벤트 로그에서 재계산한 PCR과 실제 PCR 비교
tpm2_eventlog --pcrs=sha256 /sys/kernel/security/tpm0/binary_bios_measurements
tpm2_pcrread sha256:0,1,2,3,4,5,6,7
💡

systemd-boot PCR 정책: systemd-boot(sd-boot)는 UKI(Unified Kernel Image)와 함께 다음 PCR을 사용합니다: PCR 11(UKI 해시), PCR 12(커널 명령줄), PCR 15(시스템 확장 이미지). systemd-measure 도구로 부팅 전에 예상 PCR 값을 계산하고 서명할 수 있어, PCR fragility 문제를 해결합니다. 자세한 내용은 TPM 기반 디스크 암호화 섹션을 참고하세요.

ℹ️

IMA와 PCR 10: Linux 커널의 IMA(Integrity Measurement Architecture)는 실행되거나 읽히는 파일을 PCR 10에 측정합니다. IMA 이벤트 로그는 /sys/kernel/security/ima/ascii_runtime_measurements에서 확인할 수 있으며, 원격 증명에서 OS 런타임 무결성을 검증하는 핵심 요소입니다. 자세한 내용은 IMA/EVM 무결성 검증 문서를 참고하세요.

DRTM 상세 분석

DRTM(Dynamic Root of Trust for Measurement)은 이미 실행 중인 시스템에서 새로운 신뢰 기반을 동적으로 설정하는 메커니즘입니다. SRTM과 달리 부팅 초기 코드의 무결성에 의존하지 않으므로, 후기 부팅(late launch) 보안을 제공합니다.

Intel TXT (Trusted Execution Technology)

Intel TXT DRTM Flow GETSEC[SENTER] 모든 AP 정지 인터럽트 비활성화 SINIT ACM 로드 서명 검증 (Intel 키) CPU 마이크로코드 검증 PCR 17-22 리셋 Locality 4에서 실행 DRTM 전용 PCR 초기화 Measured Launch SINIT ACM → PCR 17 extend MLE (tboot) → PCR 18 extend DRTM PCR 할당 상세 PCR 17: DRTM MLE (Measured Launch Environment) — SINIT ACM 자체 + tboot 코드 PCR 18: MLE 정책 — tboot 설정, 모듈 목록, 커널/initrd 해시 PCR 19-22: 추가 정책/상태 — 플랫폼별 정의 (대부분 미사용) tboot (Trusted Boot) — Open Source DRTM Launcher GRUB → tboot (GETSEC[SENTER]) → SINIT ACM 실행 → PCR 17-18 측정 → Linux 커널 부팅 → tboot 복귀 (셧다운 시)

Intel TXT의 GETSEC[SENTER] 명령은 다음 단계를 CPU 하드웨어 레벨에서 강제합니다:

  1. 환경 정화: 모든 AP(Application Processor)를 WAIT-FOR-SIPI 상태로 강제 전환, 인터럽트 비활성화, DMA 차단
  2. SINIT ACM 검증: Intel이 서명한 Authenticated Code Module의 서명을 CPU 내장 키로 검증
  3. PCR 17-22 리셋: Locality 4에서만 가능한 DRTM PCR을 하드웨어적으로 초기화
  4. Measured Launch: SINIT ACM과 MLE(Measured Launch Environment, 예: tboot)를 PCR 17-18에 측정
  5. 제어 전달: 측정이 완료된 MLE에 제어를 넘기고, MLE가 OS 커널을 시작

AMD SKINIT

AMD의 DRTM은 SKINIT 명령을 사용하며, Intel TXT보다 단순한 구조입니다:

AMD SKINIT 흐름:
1. SKINIT 명령 실행 (Ring 0에서)
2. CPU가 인터럽트/DMA 차단
3. Secure Loader Block (SLB, 최대 64KB) 해시 → PCR 17 extend
4. SLB 실행 — SLB가 커널 측정 후 부팅
5. DEV (Device Exclusion Vector)로 DMA 보호 설정
ℹ️

Linux 커널 DRTM 지원 상태: tboot는 GRUB multiboot 프로토콜을 통해 Intel TXT DRTM을 구현합니다. AMD SKINIT은 커널에서 CONFIG_SECURE_LAUNCH로 지원이 진행 중이며, "Secure Launch" 패치셋이 활발히 개발되고 있습니다. ARM 플랫폼에서는 DRTM 표준이 아직 초기 단계입니다.

TCG Crypto Agile Event Log 상세

TCG Crypto Agile 로그 형식(TCG_PCR_EVENT2)은 다중 해시 알고리즘을 지원하며, TPM 2.0의 표준 이벤트 로그 형식입니다:

/* TCG PC Client Platform Firmware Profile Specification */
struct tcg_pcr_event2 {
    u32  pcr_index;          /* PCR 인덱스 (0-23) */
    u32  event_type;         /* EV_* 이벤트 타입 */
    u32  digest_count;       /* 다이제스트 개수 (뱅크 수) */
    struct {
        u16  alg_id;         /* TPM_ALG_SHA1=0x0004, SHA256=0x000B */
        u8   digest[];       /* 알고리즘별 다이제스트 길이 */
    } digests[digest_count];
    u32  event_size;         /* 이벤트 데이터 길이 */
    u8   event[];            /* 이벤트 설명 데이터 */
};

/* 이벤트 로그의 첫 번째 항목은 항상 TCG_EfiSpecIDEvent (레거시 호환) */
struct tcg_efi_specid_event_head {
    u8   signature[16];    /* "Spec ID Event03" */
    u32  platform_class;    /* 0=PC Client */
    u8   spec_version_minor;
    u8   spec_version_major;
    u8   spec_errata;
    u8   uintn_size;        /* 1=u32, 2=u64 */
    u32  num_algs;          /* 지원 알고리즘 수 */
    /* 이후: alg_id(u16) + digest_size(u16) × num_algs */
};

PCR Replay 검증 알고리즘

이벤트 로그에서 PCR 값을 재계산하여 TPM의 실제 PCR 값과 비교하는 과정을 PCR Replay라 합니다. 이것이 원격 증명의 핵심입니다:

PCR Replay 의사코드:

pcr_expected[0..23] = {0x00...00}  // 모든 PCR을 0으로 초기화

for each event in event_log:
    pcr_idx = event.pcr_index
    digest  = event.digests[SHA-256]

    // PCR extend 연산 재현
    pcr_expected[pcr_idx] = SHA-256(pcr_expected[pcr_idx] || digest)

// 검증: 재계산된 PCR과 TPM Quote의 PCR 비교
for each pcr_idx in quote_pcr_selection:
    assert pcr_expected[pcr_idx] == quote_pcr_digest[pcr_idx]
    // 불일치 → 이벤트 로그가 변조되었거나 누락된 이벤트 존재
# PCR Replay를 tpm2-tools로 수행
tpm2_eventlog --pcrs=sha256 /sys/kernel/security/tpm0/binary_bios_measurements

# 출력 예시:
# sha256:
#   0 : 0x3A2B1C... (계산값)
#   7 : 0x5D4E2F... (계산값)

# 실제 TPM PCR 값과 비교
tpm2_pcrread sha256:0,1,2,3,4,5,6,7

# 이벤트 로그 상세 파싱 — 개별 이벤트 확인
tpm2_eventlog /sys/kernel/security/tpm0/binary_bios_measurements 2>/dev/null | \
  grep -B2 -A5 "EventType"

# EFI 변수 관련 이벤트만 필터 (Secure Boot 정책)
tpm2_eventlog /sys/kernel/security/tpm0/binary_bios_measurements | \
  grep -A10 "EV_EFI_VARIABLE"

IMA와 TPM 연동

IMA(Integrity Measurement Architecture)는 커널 레벨에서 파일 실행/접근을 PCR 10에 측정하여, BIOS 이벤트 로그 이후의 OS 런타임 무결성을 보장합니다:

/* security/integrity/ima/ima_queue.c — IMA PCR extend */
static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
{
    int result;

    /* CONFIG_IMA_MEASURE_PCR_IDX (기본 10)에 extend */
    result = tpm_pcr_extend(ima_tpm_chip, pcr, digests_arg);
    return result;
}

/* IMA 템플릿 엔트리 구조 */
struct ima_template_entry {
    int             pcr;              /* PCR 인덱스 (기본 10) */
    struct tpm_digest *digests;      /* 뱅크별 다이제스트 */
    struct ima_template_desc *template_desc;
    u32             template_data_len;
    struct ima_field_data *template_data;
};

boot_aggregate 계산

IMA 이벤트 로그의 첫 번째 항목은 항상 boot_aggregate이며, BIOS PCR 값(PCR 0-7)의 해시로 계산됩니다. 이를 통해 BIOS 측정과 IMA 측정이 연결됩니다:

boot_aggregate = SHA-256(PCR[0] || PCR[1] || ... || PCR[7])

IMA 이벤트 로그 첫 줄 (ascii_runtime_measurements):
10  ima-ng sha256: boot_aggregate
# IMA 이벤트 로그 첫 줄 확인 — boot_aggregate
head -1 /sys/kernel/security/ima/ascii_runtime_measurements
# 출력: 10 abc123... ima-ng sha256:def456... boot_aggregate

# boot_aggregate 수동 검증
# 1. PCR 0-7 값 읽기
tpm2_pcrread sha256:0,1,2,3,4,5,6,7 -o pcr_values.bin
# 2. 연결된 PCR 값의 SHA-256 해시 계산
sha256sum pcr_values.bin
# 3. IMA 로그의 boot_aggregate와 비교
💡

BIOS 이벤트 로그 vs IMA 이벤트 로그: BIOS 이벤트 로그(binary_bios_measurements)는 PCR 0-9에 대한 펌웨어/부트로더 측정을 기록합니다. IMA 이벤트 로그(ascii_runtime_measurements)는 PCR 10에 대한 OS 런타임 측정을 기록합니다. 원격 증명에서는 두 로그를 모두 수집하여 전체 부팅 체인과 런타임 무결성을 검증합니다. 자세한 IMA 정책과 설정은 IMA/EVM 무결성 검증 문서를 참고하세요.

PCR fragility 문제와 해결

PCR fragility(취약성)는 TPM 기반 봉인의 가장 큰 실무 문제입니다. 커널 업데이트, BIOS 업데이트, Secure Boot 정책 변경 등이 PCR 값을 변경하여 봉인된 비밀에 접근할 수 없게 만듭니다:

PCR Fragility Problem & Solutions PCR Fragility (문제) 시나리오 1: 커널 업데이트 → PCR 8/9/11 변경 → Unseal 실패 → 부팅 불가 시나리오 2: BIOS/펌웨어 업데이트 → PCR 0/1/2 변경 → Unseal 실패 시나리오 3: Secure Boot dbx 업데이트 → PCR 7 변경 → LUKS 봉인 키 해제 불가 시나리오 4: GRUB 설정 변경 → PCR 4/5/8 변경 → 재봉인 필요 해결 전략 1. Signed PCR Policy (PolicyAuthorize) 서명 키로 새 PCR 값을 사전 인증 → 자동 갱신 2. PCR 선택 최소화 PCR 7(Secure Boot 정책)만 바인딩 — 커널 업데이트 무관 3. PolicyOR (복수 정책) 현재 PCR OR 다음 PCR OR 복구 비밀번호 4. systemd-measure sign 커널 빌드 시 예상 PCR 값을 미리 서명
PCR변경 원인빈도봉인 바인딩 권장 여부
PCR 0BIOS/펌웨어 업데이트낮음 (연 1-2회)주의 필요 — 펌웨어 업데이트 전 재봉인
PCR 1BIOS 설정 변경낮음주의 필요
PCR 4부트로더(GRUB/sd-boot) 변경중간비권장 — 커널 업데이트마다 변경
PCR 7Secure Boot 정책 (PK/KEK/db/dbx)낮음권장 — 가장 안정적인 PCR
PCR 8커널 명령줄, GRUB 설정높음비권장 — 빈번한 변경
PCR 9initrd 내용높음비권장 — 커널 업데이트마다 변경
PCR 11UKI(Unified Kernel Image) 해시중간Signed Policy와 함께 사용
# === Signed PCR Policy를 사용한 자동 갱신 워크플로 ===

# 1. 서명 키 쌍 (오프라인에 안전하게 보관)
openssl genpkey -algorithm ec -pkeyopt ec_paramgen_curve:P-256 \
  -out pcr-sign-key.pem
openssl pkey -in pcr-sign-key.pem -pubout -out pcr-sign-pubkey.pem

# 2. 커널 패키지 빌드/설치 시 예상 PCR 값 서명
# → 패키지 관리자의 post-install hook에 통합
systemd-measure sign \
  --linux=/boot/vmlinuz-6.8.0 \
  --initrd=/boot/initrd.img-6.8.0 \
  --osrel=/etc/os-release \
  --cmdline=/proc/cmdline \
  --private-key=pcr-sign-key.pem \
  --public-key=pcr-sign-pubkey.pem \
  --phase=enter-initrd:leave-initrd:sysinit:ready \
  > /boot/loader/entries/6.8.0-pcrsig.json

# 3. TPM 봉인은 공개키로만 등록 (최초 1회)
systemd-cryptenroll --tpm2-device=auto \
  --tpm2-public-key=pcr-sign-pubkey.pem \
  --tpm2-public-key-pcrs=11 \
  /dev/sda3

# → 이후 커널 업데이트마다 서명만 갱신하면 자동으로 동작
# → TPM 봉인 객체 재생성 불필요!

원격 증명 (Remote Attestation)

Remote Attestation Protocol Attester (대상 시스템) TPM (AK, EK) Event Log Verifier (검증자) EK CA 인증서, Golden PCR Values 1. nonce 2. TPM2_Quote(AK, PCR selection, nonce) TPM이 (PCR digest + nonce)에 AK로 서명 3. Quote + EventLog + EK Cert 4. 검증 단계 a) AK 서명 검증 (Quote 무결성) b) nonce 확인 (replay 방지) c) Event Log → PCR replay → Quote PCR 비교 d) 개별 이벤트를 Golden Values와 비교 e) EK 인증서 체인 검증 (TPM 진위 확인) 5. 증명 결과 (Pass/Fail)

원격 증명(Remote Attestation)은 TPM의 가장 중요한 응용 중 하나로, 원격 시스템의 소프트웨어 상태가 변조되지 않았음을 제3자(Verifier)에게 암호학적으로 증명합니다.

AK 인증: MakeCredential / ActivateCredential

원격 증명에서 검증자는 Attester의 AK가 진짜 TPM 내부에 존재하는지 확인해야 합니다. 이를 위해 EK를 활용하는 MakeCredential/ActivateCredential 프로토콜을 사용합니다:

  1. Attester가 AK 공개키와 EK 공개키(또는 EK 인증서)를 Verifier에게 전송
  2. Verifier가 TPM2_MakeCredential(EK_pub, AK_name, secret)으로 credential 블롭 생성
  3. Attester가 TPM2_ActivateCredential(EK, AK, credential_blob)으로 secret 복구
  4. secret을 복구할 수 있다는 것은 AK가 EK와 같은 TPM에 있음을 증명
# 1. AK 생성
tpm2_createak -C ek.ctx -c ak.ctx -u ak.pub -n ak.name -G rsa2048

# 2. Quote 생성 (PCR 0,7,11에 대해 nonce와 함께)
tpm2_quote -c ak.ctx -l sha256:0,7,11 \
  -q $(openssl rand -hex 32) \
  -m quote.msg -s quote.sig -o quote.pcrs -g sha256

# 3. Quote 검증 (검증자 측)
tpm2_checkquote -u ak.pub -m quote.msg -s quote.sig \
  -f quote.pcrs -g sha256 -q 

# MakeCredential / ActivateCredential 프로토콜
# (검증자 측) credential 래핑
echo "challenge-secret" > secret.data
tpm2_makecredential -e ek.pub -s secret.data -n ak.name -o credential.blob

# (Attester 측) credential 풀기
tpm2_activatecredential -c ak.ctx -C ek.ctx \
  -i credential.blob -o recovered_secret.data

Keylime 원격 증명 프레임워크

Keylime은 CNCF 프로젝트로, 대규모 시스템의 TPM 기반 원격 증명을 자동화합니다:

컴포넌트역할위치
RegistrarAgent 등록, EK/AK 인증중앙 서버
Verifier주기적 Quote 검증, 정책 평가중앙 서버
AgentTPM Quote 생성, IMA 로그 전송대상 시스템
Tenant정책 설정, Agent 등록 요청 CLI관리자 워크스테이션
# Keylime Agent 등록
keylime_tenant -c add -t  --uuid  \
  --allowlist /path/to/allowlist.txt \
  --exclude /path/to/exclude.txt

# 증명 상태 확인
keylime_tenant -c status -t  --uuid 
ℹ️

기밀 컴퓨팅(Confidential Computing)과 증명: AMD SEV-SNP, Intel TDX 등의 기밀 컴퓨팅 환경에서도 증명(Attestation)은 핵심 요소입니다. 이 경우 CPU가 생성하는 증명 보고서가 TPM Quote와 유사한 역할을 합니다. 자세한 내용은 기밀 컴퓨팅 문서를 참고하세요.

MakeCredential/ActivateCredential 프로토콜 상세

이 프로토콜은 AK(Attestation Key)가 EK(Endorsement Key)와 동일한 TPM에 존재함을 증명하는 핵심 메커니즘입니다. 암호학적으로 다음과 같이 동작합니다:

MakeCredential / ActivateCredential Protocol Attester (대상 시스템) TPM: EK(비밀), AK(비밀), EK_pub, AK_pub, AK_name Verifier (Privacy CA) EK CA Root Certificate (TPM 제조사 CA) 1. EK_cert + AK_pub + AK_name 2. Verifier 처리 a) EK_cert 체인 검증 (CA 확인) b) MakeCredential(EK_pub, AK_name, secret) 3. credential_blob (EK로 암호화됨) 4. TPM2_ActivateCredential(EK, AK, blob) TPM 내부: EK로 복호화 → AK_name 검증 → secret 반환 5. recovered secret → 일치 확인 AK가 이 TPM에 존재함이 증명됨

MakeCredential의 내부 암호학적 동작은 다음과 같습니다:

  1. Verifier가 랜덤 secret을 생성하고, TPM2_MakeCredential(EK_pub, AK_name, secret)을 호출
  2. 내부적으로: secret을 AK_name의 해시로 바인딩(KDF)한 후, EK 공개키로 RSA-OAEP 암호화 → credential_blob 생성
  3. AttesterTPM2_ActivateCredential(EK, AK, credential_blob)을 TPM에서 실행
  4. TPM 내부에서: EK 비밀키로 복호화 → AK_name을 현재 TPM에 로드된 AK의 name과 비교 → 일치하면 secret 반환
  5. 이 과정이 성공하면, AK가 EK와 동일한 물리적 TPM에 바운딩되어 있음이 암호학적으로 증명됨
ℹ️

EK 인증서 체인 검증: EK 인증서는 TPM 제조사(Infineon, Nuvoton, STMicro 등)가 서명합니다. 검증자는 제조사의 CA 인증서로 EK 인증서의 진위를 확인합니다. EK 인증서는 TPM의 NV 인덱스 0x01C00002(RSA) 또는 0x01C0000A(ECC)에 저장되어 있거나, 제조사의 온라인 서비스(예: ekop.intel.com)에서 다운로드할 수 있습니다.

Privacy CA vs DAA

방식프라이버시복잡도설명
Privacy CA중간 (CA가 모든 AK 매핑을 알 수 있음)낮음신뢰할 수 있는 CA가 AK 인증서를 발급. EK→AK 링크를 CA가 보유. 구현이 단순하여 실무에서 가장 많이 사용
DAA (Direct Anonymous Attestation)높음 (익명성 보장)높음그룹 서명 기반으로, 검증자가 어떤 TPM인지 식별 불가능. TPM2_Commit/TPM2_Sign으로 구현. FIDO 키와 유사한 프라이버시 모델
ECDAA높음중간ECC 기반 DAA. TPM 2.0 스펙에 포함 (TPM_ALG_ECDAA). 성능이 RSA DAA보다 우수하지만 구현체가 제한적

Keylime 운영

Keylime Architecture Tenant (CLI) 정책 설정 Agent 등록/삭제 Registrar EK/AK 인증 Agent UUID 등록 Verifier 주기적 Quote 검증 IMA 로그 분석 Agent (대상 서버) TPM Quote 생성, IMA 로그 전송 revocation action 실행 등록 Quote 요청 Revocation Actions 실패 시: 네트워크 차단, 키 삭제, 알림 전송 Fail PostgreSQL/SQLite Agent 상태 정책, 허용 목록

Keylime의 주요 운영 기능을 상세히 살펴봅니다:

Keylime Allowlist/Excludelist 관리

# === Keylime 설치 ===
sudo dnf install keylime keylime-verifier keylime-registrar keylime-agent

# === Allowlist 생성 (IMA 정책용) ===
# 현재 시스템의 파일 해시를 allowlist로 생성
keylime_create_allowlist -o /etc/keylime/allowlist.txt \
  -a sha256 -e /boot -e /proc -e /sys

# Excludelist: 변동이 잦은 파일 제외
cat > /etc/keylime/excludelist.txt << 'EXCLUDE'
/var/log/.*
/tmp/.*
/run/.*
/sys/.*
EXCLUDE

# === Agent 등록 및 모니터링 ===
# Agent에 allowlist + IMA 정책 적용
keylime_tenant -c add \
  -t 192.168.1.100 \
  --uuid d432fbb3-d2f1-4a97-9ef7-75bd81c00000 \
  --allowlist /etc/keylime/allowlist.txt \
  --exclude /etc/keylime/excludelist.txt \
  --ima-sign-verification-keys /etc/keys/ima-pubkey.pem

# Agent 상태 확인
keylime_tenant -c status \
  -t 192.168.1.100 \
  --uuid d432fbb3-d2f1-4a97-9ef7-75bd81c00000

# Agent 삭제 (모니터링 중지)
keylime_tenant -c delete \
  -t 192.168.1.100 \
  --uuid d432fbb3-d2f1-4a97-9ef7-75bd81c00000

# === Revocation 액션 설정 ===
# /etc/keylime/keylime.conf
# [agent]
# revocation_actions = local_action_rm_ssh_key,
#                     local_action_block_network
# revocation_notification_ip = 10.0.0.1
# revocation_notification_port = 8992
💡

Keylime Runtime Policy: Keylime v7.x부터는 JSON 기반 Runtime Policy로 allowlist/excludelist를 대체합니다. 정책 파일에서 파일별 해시, IMA 서명 검증, 커스텀 검증 스크립트를 선언적으로 구성할 수 있으며, keylime_create_policy 도구로 기존 시스템에서 정책을 자동 생성할 수 있습니다.

키 봉인/해제 (Seal/Unseal)

Seal/Unseal은 TPM의 가장 실용적인 기능 중 하나로, 비밀 데이터를 TPM에 봉인하되 특정 조건(PCR 값, 비밀번호 등)이 충족될 때만 해제할 수 있게 합니다.

봉인 흐름 (Seal)

  1. SRK 생성/로드: TPM2_CreatePrimary(SH)로 Storage Root Key를 생성하거나, persistent handle에서 로드
  2. 정책 다이제스트 계산: Trial 세션으로 원하는 정책(예: PolicyPCR + PolicyPassword)의 다이제스트를 계산
  3. 봉인 객체 생성: TPM2_Create(parent=SRK, sensitive=비밀데이터, authPolicy=정책다이제스트)
  4. 결과 저장: 생성된 publicprivate 블롭을 파일시스템(Filesystem)에 저장 (SRK로 래핑되어 있으므로 안전)

해제 흐름 (Unseal)

  1. SRK 재생성: 동일한 SRK 템플릿으로 TPM2_CreatePrimary(SH) — 같은 shSeed에서 동일 SRK 파생
  2. 봉인 객체 로드: TPM2_Load(SRK, public_blob, private_blob)
  3. 정책 세션 시작: TPM2_StartAuthSession(PolicySession)
  4. 정책 만족: TPM2_PolicyPCR, TPM2_PolicyPassword 등 순서대로 호출
  5. 데이터 해제: TPM2_Unseal(봉인객체, PolicySession) → 비밀 데이터 반환
# === Seal: 비밀 데이터를 PCR 7에 바인딩하여 봉인 ===

# 1. SRK 생성
tpm2_createprimary -C o -G rsa2048 -g sha256 -c srk.ctx

# 2. 정책 다이제스트 계산 (Trial 세션)
tpm2_startauthsession --trial-session -S trial.ctx
tpm2_policypcr -S trial.ctx -l sha256:7
tpm2_policygetdigest -S trial.ctx -o pcr7_policy.digest
tpm2_flushcontext trial.ctx

# 3. 비밀 데이터 봉인
echo "my-super-secret-key-material" > secret.dat
tpm2_create -C srk.ctx -L pcr7_policy.digest \
  -i secret.dat -u sealed.pub -r sealed.priv

# === Unseal: PCR 7이 변하지 않았을 때만 성공 ===

# 4. 봉인 객체 로드
tpm2_load -C srk.ctx -u sealed.pub -r sealed.priv -c sealed.ctx

# 5. 정책 세션으로 Unseal
tpm2_startauthsession --policy-session -S session.ctx
tpm2_policypcr -S session.ctx -l sha256:7
tpm2_unseal -c sealed.ctx -p session:session.ctx -o recovered_secret.dat

# 복구된 비밀 확인
cat recovered_secret.dat
# 출력: my-super-secret-key-material

커널 trusted 키 타입

Linux 커널은 trusted 키 타입을 통해 TPM Seal/Unseal을 커널 키링 인프라에 통합합니다:

# TPM으로 봉인된 trusted 키 생성 (32바이트, PCR 7에 바인딩)
keyctl add trusted tpm-master "new 32 pcrinfo=0:sha256:7" @s

# 키 내용 조회 (봉인된 블롭 형태)
keyctl pipe  > sealed_blob.bin

# 재부팅 후 봉인 블롭에서 키 복원
keyctl add trusted tpm-master "load $(cat sealed_blob.bin | base64)" @s

# trusted 키를 마스터로 encrypted 키 생성 (dm-crypt 등에 사용)
keyctl add encrypted dm-key "new ecb(aes) 64 trusted:tpm-master" @s
💡

trusted vs encrypted 키: trusted 키는 TPM으로 봉인되어 TPM 없이는 절대 평문을 얻을 수 없습니다. encrypted 키는 마스터 키(trusted 또는 user 키)로 AES 암호화되며, 마스터 키를 알면 소프트웨어로 복호화 가능합니다. 최상의 보안을 위해 trusted 키를 마스터로, encrypted 키를 실제 암호화에 사용합니다. 자세한 내용은 키링 서비스 문서를 참고하세요.

복합 정책 봉인 예제

실무에서는 PCR 바인딩만으로는 부족하여, 비밀번호와 PCR을 조합하는 복합 정책(PolicyOR)을 사용합니다. 이를 통해 "PCR 7이 특정 값이고 비밀번호도 맞을 때" 또는 "관리자 비밀번호만으로도 해제 가능"한 유연한 정책을 구성할 수 있습니다:

# === 복합 정책: PolicyPCR + PolicyPassword (AND 조합) ===

# 1. SRK 생성
tpm2_createprimary -C o -c srk.ctx

# 2. 복합 정책 다이제스트 계산 (Trial 세션)
tpm2_startauthsession --trial-session -S trial.ctx

# PolicyPCR: PCR 7이 현재 값일 때만
tpm2_policypcr -S trial.ctx -l sha256:7

# PolicyAuthValue: 추가로 비밀번호도 필요
tpm2_policyauthvalue -S trial.ctx

# 정책 다이제스트 추출
tpm2_policygetdigest -S trial.ctx -o compound_policy.dgst
tpm2_flushcontext trial.ctx

# 3. 비밀 데이터 봉인 (정책 + 비밀번호 양쪽 모두 필요)
echo "top-secret-data" > secret.dat
tpm2_create -C srk.ctx -G keyedhash \
  -L compound_policy.dgst \
  -p "mysealpassword" \
  -i secret.dat \
  -u compound_sealed.pub -r compound_sealed.priv \
  -a "fixedtpm|fixedparent"

# 4. Unseal (PCR + 비밀번호 양쪽 모두 제공)
tpm2_load -C srk.ctx -u compound_sealed.pub \
  -r compound_sealed.priv -c compound_sealed.ctx

tpm2_startauthsession --policy-session -S session.ctx
tpm2_policypcr -S session.ctx -l sha256:7
tpm2_policyauthvalue -S session.ctx

tpm2_unseal -c compound_sealed.ctx \
  -p "session:session.ctx+mysealpassword" \
  -o recovered.dat

cat recovered.dat
# 출력: top-secret-data
# === PolicyOR: 복수 정책 중 하나만 만족하면 OK ===

# 정책 A: PCR 7 바인딩 (일반 부팅)
tpm2_startauthsession --trial-session -S trial_a.ctx
tpm2_policypcr -S trial_a.ctx -l sha256:7
tpm2_policygetdigest -S trial_a.ctx -o policy_a.dgst
tpm2_flushcontext trial_a.ctx

# 정책 B: 관리자 비밀번호만으로 해제 (복구용)
tpm2_startauthsession --trial-session -S trial_b.ctx
tpm2_policyauthvalue -S trial_b.ctx
tpm2_policygetdigest -S trial_b.ctx -o policy_b.dgst
tpm2_flushcontext trial_b.ctx

# PolicyOR: A 또는 B 중 하나만 만족하면 OK
tpm2_startauthsession --trial-session -S trial_or.ctx
tpm2_policyor -S trial_or.ctx \
  -l sha256:policy_a.dgst,policy_b.dgst
tpm2_policygetdigest -S trial_or.ctx -o policyor.dgst
tpm2_flushcontext trial_or.ctx

# 봉인 (PolicyOR 정책 적용)
tpm2_create -C srk.ctx -G keyedhash \
  -L policyor.dgst -p "admin-recovery-pw" \
  -i secret.dat -u or_sealed.pub -r or_sealed.priv \
  -a "fixedtpm|fixedparent"

TPM 기반 디스크 암호화 실무

LUKS2 + TPM Sealing Strategies Direct PCR Binding (취약) systemd-cryptenroll \ --tpm2-pcrs=7+11 /dev/sda3 PCR 7: Secure Boot 정책 (db/dbx 변경 시 깨짐) PCR 11: UKI 해시 (커널 업데이트 시 깨짐) 문제: 커널/dbx 업데이트 → PCR 변경 → 잠금! 해결: 업데이트 전에 PIN/복구 키로 수동 재등록 적합: 변경이 드문 고정 환경 (IoT, 키오스크) 장점: 구현 단순, 추가 키 관리 불필요 단점: PCR fragility, 업데이트마다 수동 재등록 Signed PCR Policy (권장) systemd-cryptenroll \ --tpm2-public-key-pcrs=11 \ --tpm2-public-key=key.pem /dev/sda3 PolicyAuthorize: 서명 키가 새 PCR 값을 서명 → 객체 재생성 없이 정책 갱신 가능 커널 업데이트 → systemd-measure sign → 자동 갱신 서명 키만 보호하면 됨 (별도 HSM/오프라인) 적합: 일반 서버/데스크톱 (자동 업데이트) 장점: 업데이트에 강건, 자동화 가능 단점: 서명 키 관리 필요, 설정 복잡

systemd-cryptenroll 실무

# === 방법 1: Direct PCR Binding ===

# LUKS2 파티션에 TPM2 키슬롯 추가 (PCR 7 바인딩)
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sda3

# PCR 7+11 바인딩 + PIN 추가 (2FA)
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7+11 \
  --tpm2-with-pin=yes /dev/sda3

# /etc/crypttab 설정
# root  /dev/sda3  -  tpm2-device=auto,tpm2-pcrs=7

# === 방법 2: Signed PCR Policy (권장) ===

# 서명 키 쌍 생성
openssl genpkey -algorithm RSA -out pcr-sign-key.pem -pkeyopt rsa_keygen_bits:2048
openssl pkey -in pcr-sign-key.pem -pubout -out pcr-sign-pubkey.pem

# 현재 부팅 예상 PCR 값 서명 (systemd-measure)
sudo systemd-measure sign \
  --linux=/boot/vmlinuz \
  --initrd=/boot/initrd.img \
  --private-key=pcr-sign-key.pem \
  --public-key=pcr-sign-pubkey.pem \
  --phase=enter-initrd:leave-initrd:sysinit:ready \
  > pcr-signature.json

# Signed Policy로 TPM 등록
sudo systemd-cryptenroll --tpm2-device=auto \
  --tpm2-public-key=pcr-sign-pubkey.pem \
  --tpm2-public-key-pcrs=11 \
  --tpm2-signature=pcr-signature.json \
  /dev/sda3

# === Clevis + Tang (Network-Bound Disk Encryption) ===

# Tang 서버 설정
sudo systemctl enable tangd.socket --now

# Clevis로 LUKS에 Tang 바인딩 추가
sudo clevis luks bind -d /dev/sda3 tang '{"url":"http://tang.local:7500"}'

# TPM + Tang 결합 (SSS: Shamir Secret Sharing)
sudo clevis luks bind -d /dev/sda3 sss '{"t":1,"pins":{"tpm2":{"pcr_ids":"7"},"tang":{"url":"http://tang.local:7500"}}}'
⚠️

복구 전략 필수: TPM 봉인을 유일한 잠금 해제 수단으로 사용하면 안 됩니다. TPM이 고장나거나 교체되면 데이터에 접근할 수 없게 됩니다. 반드시 복구 키슬롯(패스프레이즈 또는 복구 키)을 함께 유지하세요: systemd-cryptenroll --recovery-key /dev/sda3

LUKS2 키슬롯 관리

LUKS2는 최대 32개의 키슬롯을 지원하며, TPM 봉인과 기존 패스프레이즈를 병행 관리할 수 있습니다:

# === LUKS2 키슬롯 상태 확인 ===
sudo cryptsetup luksDump /dev/sda3 | grep -A5 "Keyslots:"
# 0: luks2 (패스프레이즈)
# 1: luks2 (TPM2, systemd-cryptenroll)
# 2: luks2 (복구 키)

# === 키슬롯 상세 정보 ===
sudo systemd-cryptenroll /dev/sda3
# SLOT TYPE
# 0    password
# 1    tpm2
# 2    recovery

# === TPM 키슬롯 교체 (PCR 값 변경 후) ===
# 기존 TPM 키슬롯 제거
sudo systemd-cryptenroll --wipe-slot=tpm2 /dev/sda3

# 새 PCR 값으로 재등록
sudo systemd-cryptenroll --tpm2-device=auto \
  --tpm2-pcrs=7 /dev/sda3

# === 복구 키 생성 (반드시 안전하게 백업!) ===
sudo systemd-cryptenroll --recovery-key /dev/sda3
# 출력: pufkqn-th84jv-x37qkm-...  ← 이 키를 오프라인에 보관

# === FIDO2 보안 키 + TPM 결합 ===
sudo systemd-cryptenroll --fido2-device=auto \
  --fido2-with-user-verification=yes /dev/sda3

Clevis + Tang (Network-Bound) 상세

Clevis/Tang은 네트워크 기반 디스크 암호화 해제(NBDE)를 제공합니다. 서버가 특정 네트워크에 접속되어 있을 때만 자동으로 디스크를 해제하므로, 물리적 도난 시 데이터를 보호합니다:

컴포넌트역할프로토콜
Tang 서버ECMR(Elliptic Curve McCallum-Relyea) 키 교환HTTP REST, 상태 없음(stateless)
Clevis 클라이언트LUKS 키슬롯에 Tang 바인딩 추가Shamir Secret Sharing 지원
clevis-dracut/initrd부팅 시 Tang 서버와 통신하여 자동 해제initrd 내 네트워크 초기화 필요
# === Tang 서버 설정 ===
sudo dnf install tang
sudo systemctl enable tangd.socket --now
# 기본 포트: 80 (systemd socket activation)

# Tang 서버 키 확인
tang-show-keys 80

# === Clevis 클라이언트 설정 ===
sudo dnf install clevis clevis-luks clevis-dracut

# LUKS에 Tang 바인딩 추가
sudo clevis luks bind -d /dev/sda3 tang \
  '{"url":"http://tang.internal:80"}'

# === TPM + Tang 결합 (Shamir Secret Sharing) ===
# t=1: TPM 또는 Tang 둘 중 하나만 있으면 해제
sudo clevis luks bind -d /dev/sda3 sss '{
  "t": 1,
  "pins": {
    "tpm2": {"pcr_ids": "7"},
    "tang": {"url": "http://tang.internal:80"}
  }
}'

# t=2: TPM과 Tang 모두 필요 (AND 조건)
sudo clevis luks bind -d /dev/sda3 sss '{
  "t": 2,
  "pins": {
    "tpm2": {"pcr_ids": "7"},
    "tang": {"url": "http://tang.internal:80"}
  }
}'

# initrd 재생성 (부팅 시 자동 해제 지원)
sudo dracut -fv --regenerate-all
ℹ️

Tang vs Escrow 서버: Tang은 상태 없는(stateless) 서버로, 어떤 클라이언트의 키도 저장하지 않습니다. 이는 Tang 서버가 탈취되어도 암호화 키를 복구할 수 없음을 의미합니다. 반면 일반 키 에스크로 서버는 키를 저장하므로 서버 보안이 곧 데이터 보안입니다. Tang은 ECMR(McCallum-Relyea) 프로토콜을 사용하여, 서버와 클라이언트가 각각 비밀 없이 Diffie-Hellman 유사 키 교환을 수행합니다.

TPM과 커널 난수 생성기

TPM은 내장된 하드웨어 난수 생성기를 통해 Linux 커널의 엔트로피 풀에 기여합니다. CONFIG_HW_RANDOM_TPM이 활성화되면 TPM이 hwrng 프레임워크에 등록됩니다.

커널 통합

/* drivers/char/tpm/tpm-chip.c */
static int tpm_hwrng_read(struct hwrng *rng, void *data,
                          size_t max, bool wait)
{
    struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng);

    /* TPM2_GetRandom 호출 — 최대 64바이트 */
    return tpm_get_random(chip, data, min_t(size_t, max, 64));
}

/* hwrng 프레임워크에 등록 */
static int tpm_add_hwrng(struct tpm_chip *chip)
{
    chip->hwrng.name = dev_name(&chip->dev);
    chip->hwrng.read = tpm_hwrng_read;
    chip->hwrng.quality = 1000;  /* 높은 품질 — 1024 중 1000 */
    return hwrng_register(&chip->hwrng);
}

성능 특성

특성dTPM (SPI)fTPM비고
처리량(Throughput)~50 KB/s~500 KB/sTPM 버스 속도에 제한
요청당 최대64 바이트64 바이트TPM2_GetRandom 제한
지연~5-20 ms~1-5 ms명령 왕복 시간
hwrng quality100010001024 중 1000 (높음)
# hwrng 상태 확인
cat /sys/class/misc/hw_random/rng_current
# 출력: tpm-rng-0 또는 tpm0

# 사용 가능한 hwrng 목록
cat /sys/class/misc/hw_random/rng_available

# TPM에서 직접 난수 획득
tpm2_getrandom 32 --hex

# 외부 엔트로피를 TPM RNG에 주입
tpm2_stirrandom < /dev/urandom
ℹ️

hwrng 프레임워크와 TPM: 여러 hwrng 소스가 있을 때 quality 값이 가장 높은 소소가 기본 선택됩니다. TPM은 quality=1000으로 등록되어, 다른 hwrng 소스보다 높은 우선순위를 갖는 경우가 많습니다. 그러나 TPM의 처리량 한계로 인해 대량 난수 생성에는 CPU의 RDRAND/RDSEED나 전용 TRNG가 더 적합합니다. 자세한 내용은 HWRNG/TRNG 문서를 참고하세요.

TPM RNG 엔트로피 품질 분석

TPM의 내장 RNG는 물리적 노이즈 소스(열 노이즈, 메타스테이블 래치 등)를 사용하여 진정한 무작위 수를 생성합니다. 하지만 fTPM의 경우 소프트웨어 DRBG(Deterministic Random Bit Generator)를 사용할 수 있으므로 주의가 필요합니다:

특성dTPM (하드웨어 TRNG)fTPM (DRBG 기반)sTPM (OpenSSL PRNG)
엔트로피 소스물리적 노이즈 (열, 샷)TEE 내 DRBG + 하드웨어 시드호스트 OS PRNG
NIST SP 800-90B 준수예 (인증 시)일부 (TEE 구현에 의존)아니오
예측 저항 (Prediction Resistance)지원 (TRNG 자체)구현에 따라 다름미지원
재시드 빈도매 요청 (연속 엔트로피)주기적 (DRBG 리시드 간격)호스트 커널 의존
부팅 초기 엔트로피즉시 가용TEE 초기화 후 가용호스트 엔트로피 풀에 의존
# TPM RNG 품질 테스트 (간이)
# 1. TPM에서 대량 난수 수집
for i in $(seq 1 1000); do
    tpm2_getrandom 64 >> /tmp/tpm_random.bin
done

# 2. ent 명령으로 엔트로피 분석
ent /tmp/tpm_random.bin
# 엔트로피 = 7.999xxx bits/byte (이상적: 8.0)
# 카이제곱: 50% 부근이 이상적

# 3. rng-tools로 FIPS 140-2 테스트
rngtest < /tmp/tpm_random.bin
# rngtest: FIPS 140-2 successes: 998
# rngtest: FIPS 140-2 failures: 2  (2% 이하 허용)

# 4. TPM RNG를 기본 hwrng로 강제 설정
echo tpm-rng-0 | sudo tee /sys/class/misc/hw_random/rng_current

# 5. rngd로 TPM 엔트로피를 커널 풀에 자동 주입
sudo rngd -r /dev/hwrng -f -t 10

vTPM (Virtual TPM)

vTPM Architecture Path 1: swtpm + QEMU Guest VM /dev/tpm0 → tpm_tis 드라이버 → Guest kernel TPM subsystem QEMU -device tpm-tis,tpmdev=tpm0 -tpmdev emulator,id=tpm0,chardev=chr0 Unix socket swtpm (libtpms) 소프트웨어 TPM 에뮬레이터 — NV 상태를 파일로 영구 저장 State: /var/lib/swtpm-localca/ (EK cert, NV data) Path 2: SVSM vTPM (기밀 VM) Guest VM (VMPL 2-3) /dev/tpm0 → Guest kernel → SVSM 프로토콜 VMGEXIT SVSM (VMPL 0) vTPM Service — 하이퍼바이저로부터 격리됨 SEV-SNP 메모리 보호 (VMPL 0 특권) SEV-SNP 하드웨어 AMD PSP → 증명 보고서 (VMPL 0 measurement) 보안: 하이퍼바이저도 vTPM 상태에 접근 불가

swtpm 설정

# swtpm 설치 (Fedora/RHEL)
sudo dnf install swtpm swtpm-tools

# TPM 상태 디렉토리 생성 및 초기화
mkdir -p /tmp/mytpm
swtpm_setup --tpmstate /tmp/mytpm --tpm2 \
  --create-ek-cert --create-platform-cert \
  --allow-signing --overwrite

# swtpm 실행 (Unix socket 모드)
swtpm socket --tpmstate dir=/tmp/mytpm \
  --tpm2 --ctrl type=unixio,path=/tmp/mytpm/swtpm-sock \
  --log level=20 &

# QEMU에서 swtpm 연결
qemu-system-x86_64 \
  -machine q35,accel=kvm \
  -chardev socket,id=chrtpm,path=/tmp/mytpm/swtpm-sock \
  -tpmdev emulator,id=tpm0,chardev=chrtpm \
  -device tpm-tis,tpmdev=tpm0 \
  -drive file=disk.qcow2,if=virtio \
  ...

libvirt XML 설정

<!-- libvirt domain XML에 TPM 추가 -->
<devices>
  <tpm model="tpm-tis">
    <backend type="emulator" version="2.0">
      <active_pcr_banks>
        <sha256/>
        <sha384/>
      </active_pcr_banks>
    </backend>
  </tpm>
</devices>

vtpm_proxy 커널 모듈(Kernel Module)

CONFIG_TCG_VTPM_PROXY를 활성화하면 커널 내부에서 가상 TPM 장치를 생성할 수 있습니다. 주로 테스트와 개발 용도로 사용됩니다:

/* vtpm_proxy 사용 예시 */
int fd = open("/dev/vtpmx", O_RDWR);
struct vtpm_proxy_new_dev new_dev = {
    .flags = VTPM_PROXY_FLAG_TPM2,
};
ioctl(fd, VTPM_PROXY_IOC_NEW_DEV, &new_dev);
/* new_dev.major, new_dev.minor — 새 /dev/tpmN 장치 번호
 * new_dev.tpm_num — 할당된 TPM 번호
 * fd를 통해 TPM 명령/응답을 사용자 공간에서 처리 */
ℹ️

vTPM 라이브 마이그레이션: vTPM 상태는 보안에 민감하므로 라이브 마이그레이션이 복잡합니다. swtpm은 상태를 직렬화하여 마이그레이션을 지원하지만, 마이그레이션 중 TPM 상태가 노출될 수 있습니다. 기밀 컴퓨팅 환경의 SVSM vTPM은 마이그레이션 시 증명 체인이 끊어지는 문제가 있으며, 현재 활발히 연구 중입니다. 자세한 내용은 기밀 컴퓨팅 문서를 참고하세요.

swtpm 내부 아키텍처

swtpm은 libtpms 라이브러리를 기반으로 TPM 2.0 명령을 완전히 소프트웨어로 구현합니다. 내부 구조는 다음과 같습니다:

컴포넌트역할설명
libtpmsTPM 상태 머신TPM 2.0 명령 프로세서, 암호 연산, PCR/NV/키 관리의 완전한 소프트웨어 구현. IBM의 ms-tpm-20-ref 기반
swtpm프로세스 래퍼libtpms를 데몬으로 래핑, socket/chardev/cuse 인터페이스 제공. QEMU/libvirt와 연동
swtpm_setup초기 프로비저닝EK 인증서 생성, 초기 상태 설정. swtpm_localca를 사용하여 자체 서명 EK 인증서 발급
swtpm_localca로컬 CAEK/플랫폼 인증서를 발급하는 로컬 인증 기관

swtpm 상태 파일

# swtpm 상태 디렉토리 구조
ls -la /var/lib/swtpm-localca/
# issuercert.pem   — 로컬 CA 인증서
# signkey.pem      — 로컬 CA 서명 키
# certserial       — 인증서 일련번호 카운터

# VM별 TPM 상태 디렉토리
ls -la /tmp/mytpm/
# tpm2-00.permall  — 영구 상태 (NV, persistent 키, 계층 시드)
# tpm2-00.volatilestate — 휘발성 상태 (세션, PCR 값)
# swtpm-sock       — Unix 소켓 (QEMU 연결용)

# swtpm 상태 백업 및 복원 (마이그레이션)
# 1. VM 일시 정지
# 2. swtpm에 SIGSTOP → volatilestate 파일 생성
# 3. tpm2-00.permall + tpm2-00.volatilestate 복사
# 4. 대상 호스트에서 swtpm 재시작 (동일 상태 로드)

# swtpm 고급 옵션
swtpm socket --tpmstate dir=/var/lib/swtpm/vm1 \
  --tpm2 \
  --ctrl type=unixio,path=/var/run/swtpm-vm1.sock \
  --flags not-need-init,startup-clear \
  --seccomp action=log \
  --pid file=/var/run/swtpm-vm1.pid \
  --log level=5,file=/var/log/swtpm-vm1.log &

SVSM vTPM

SVSM(Secure VM Service Module)은 AMD SEV-SNP의 VMPL(Virtual Machine Privilege Level) 아키텍처를 활용하여, 하이퍼바이저(Hypervisor)로부터 격리된 vTPM을 구현합니다:

SVSM vTPM: VMPL Architecture VMPL 0 (최고 특권) SVSM vTPM Service ~30KB Rust TCB VTPM 상태 (암호화됨) SVSM 프로토콜 핸들러 메모리: VMPL 0만 접근 가능 VMPL 1 (예약/미사용) VMPL 2-3 (Guest VM) Guest Linux Kernel /dev/tpm0 → tpm_crb 드라이버 → SVSM 프로토콜 Guest는 vTPM이 하드웨어 TPM처럼 보임 VMGEXIT 호출 TPM 명령/응답 버퍼 Guest 애플리케이션 (Keylime, systemd-cryptenroll) 메모리: VMPL 0의 vTPM 상태에 접근 불가 VMGEXIT

coconut-svsm 프로젝트의 vTPM 구현 특징:

ℹ️

SVSM vTPM 사용 사례: 클라우드 환경에서 vTPM은 일반적으로 하이퍼바이저가 제공하므로, 하이퍼바이저 관리자가 vTPM 상태를 열람할 수 있습니다. SVSM vTPM은 이 문제를 해결하여, 기밀 VM의 TPM 상태를 CSP(Cloud Service Provider)로부터 완전히 보호합니다. AWS Nitro Enclaves, Azure Confidential VMs, Google Confidential Space 등에서 유사한 개념이 적용되고 있습니다. 자세한 내용은 기밀 컴퓨팅의 SVSM 섹션을 참고하세요.

vtpm_proxy 커널 모듈 상세

CONFIG_TCG_VTPM_PROXY를 활성화하면 사용자 공간에서 TPM 명령을 처리하는 가상 TPM 장치를 생성할 수 있습니다. 이는 TPM 드라이버 개발, CI/CD 파이프라인(Pipeline), 그리고 swtpm 없이도 간단한 TPM 테스트를 수행할 때 유용합니다:

/* vtpm_proxy 사용 — 완전한 예제 */
#include <linux/vtpm_proxy.h>
#include <sys/ioctl.h>
#include <fcntl.h>

int main(void) {
    int fd, vtpm_fd;
    struct vtpm_proxy_new_dev new_dev = {
        .flags = VTPM_PROXY_FLAG_TPM2,  /* TPM 2.0 모드 */
    };

    /* 1. /dev/vtpmx 열기 */
    fd = open("/dev/vtpmx", O_RDWR);

    /* 2. 새 vTPM 장치 생성 */
    ioctl(fd, VTPM_PROXY_IOC_NEW_DEV, &new_dev);
    /* → /dev/tpmN 과 /dev/tpmrmN 이 자동 생성됨
     * new_dev.major: 장치 주 번호
     * new_dev.minor: 장치 부 번호
     * new_dev.tpm_num: 할당된 TPM 번호 (N) */

    vtpm_fd = new_dev.fd;  /* TPM 명령을 수신할 fd */

    /* 3. 이벤트 루프: TPM 명령 수신 → 처리 → 응답 전송 */
    while (1) {
        u8 cmd[4096], rsp[4096];
        ssize_t cmd_len, rsp_len;

        /* read()로 TPM 명령 수신 (blocking) */
        cmd_len = read(vtpm_fd, cmd, sizeof(cmd));

        /* TPM 명령 처리 (libtpms 또는 커스텀 로직) */
        rsp_len = process_tpm_command(cmd, cmd_len, rsp);

        /* write()로 TPM 응답 전송 */
        write(vtpm_fd, rsp, rsp_len);
    }
    return 0;
}
# vtpm_proxy 모듈 로드
sudo modprobe tpm_vtpm_proxy

# /dev/vtpmx 확인
ls -la /dev/vtpmx
# crw------- 1 root root 10, 123 ... /dev/vtpmx

# swtpm을 vtpm_proxy 백엔드로 사용 (테스트 용도)
swtpm chardev --vtpm-proxy \
  --tpmstate dir=/tmp/vtpm-test \
  --tpm2 \
  --flags startup-clear
# → /dev/tpmN 자동 생성, tpm2-tools로 직접 접근 가능

# CI/CD에서 TPM 기능 테스트
export TPM2TOOLS_TCTI=device:/dev/tpm1
tpm2_startup -c
tpm2_pcrread sha256:0
💡

vtpm_proxy vs swtpm: vtpm_proxy는 커널 수준의 가상 TPM 장치를 제공하여, 사용자 공간 프로그램이 /dev/tpmN과 동일한 인터페이스로 접근할 수 있습니다. swtpm은 QEMU의 chardev 백엔드로 동작하여 VM에 vTPM을 제공합니다. 커널 TPM 서브시스템 개발이나 TPM 의존 소프트웨어의 CI 테스트에는 vtpm_proxy + swtpm 조합이 가장 편리합니다.

TPM 프로비저닝과 오너십

TPM 프로비저닝은 TPM을 처음 사용하기 위해 계층별 인가 값을 설정하고, EK 인증서를 확인하며, 기본 키 구조를 생성하는 과정입니다.

인가 값 설정

# 계층별 인가 값(비밀번호) 설정
# Owner (Storage) Hierarchy
tpm2_changeauth -c o new_owner_password

# Endorsement Hierarchy
tpm2_changeauth -c e new_endorsement_password

# Lockout (Dictionary Attack 복구)
tpm2_changeauth -c l new_lockout_password

# Platform Hierarchy (일반적으로 BIOS가 설정)
tpm2_changeauth -c p new_platform_password

# 인가 값 변경 (기존 비밀번호 필요)
tpm2_changeauth -c o -p old_owner_password new_owner_password

EK 인증서 관리

# EK 인증서 NV 인덱스에서 추출
tpm2_nvread 0x01C00002 -o ek_cert_rsa.der
openssl x509 -inform DER -in ek_cert_rsa.der -text -noout

# ECC EK 인증서
tpm2_nvread 0x01C0000A -o ek_cert_ecc.der

# EK 공개키 직접 생성 (인증서 없는 경우)
tpm2_createek -c ek.ctx -G rsa2048 -u ek.pub

# 온라인 EK 인증서 다운로드 (제조사 서버)
tpm2_getekcertificate -o ek_cert.pem -u ek.pub \
  https://ekop.intel.com/ekcertservice/

Dictionary Attack 보호

TPM은 사전 공격(Dictionary Attack)을 방어하기 위해 인가 실패 횟수를 추적하고, 임계값 초과 시 잠금(lockout)합니다:

# DA 파라미터 설정
tpm2_dictionarylockout --setup-parameters \
  --max-tries=5 \
  --recovery-time=600 \
  --lockout-recovery-time=86400

# DA 잠금 해제 (lockout 인가 필요)
tpm2_dictionarylockout --clear-lockout -p lockout_password

# 현재 DA 상태 확인
tpm2_getcap properties-variable | grep -E "lockout|DA"

TPM2_Clear

# TPM 초기화 (Storage Hierarchy 전체 삭제!)
# 주의: 모든 SH 키, NV 인덱스, 인가 값이 삭제됩니다
tpm2_clear -c p  # Platform 인가로 Clear

# Clear 후 영향:
# - shSeed 재생성 → 모든 SH Primary Key 변경
# - ownerAuth, endorsementAuth, lockoutAuth → 빈 값으로 리셋
# - 모든 persistent 객체 삭제 (SH)
# - 모든 NV 인덱스 삭제 (PLATFORMCREATE 제외)
# - epSeed 유지 → EK는 동일하게 재생성 가능
⚠️

TPM2_Clear의 파괴적 영향: TPM2_Clear는 Storage Hierarchy의 모든 데이터를 삭제합니다. LUKS2 TPM 봉인 키, trusted 키, 어플리케이션 키가 모두 무효화되어 접근 불가능해집니다. Clear를 실행하기 전에 반드시 모든 TPM 의존 서비스의 복구 경로(복구 키, 패스프레이즈 등)를 확인하세요.

NV 인덱스 고급 활용

TPM NV(Non-Volatile) 저장 영역은 단순한 데이터 저장 외에도 카운터, 비트 필드, 확장(extend) 인덱스 등 다양한 특수 유형을 지원합니다:

NV 유형속성동작용도
Ordinarynt=0일반 읽기/쓰기인증서, 설정 데이터 저장
Counternt=counter단조 증가만 가능 (롤백 불가)부팅 카운터, 안티롤백
Bit Fieldnt=bits비트 OR만 가능 (한번 설정하면 해제 불가)일회성 플래그, 기능 락
Extendnt=extendPCR처럼 extend만 가능커스텀 측정, 로그 무결성
PIN Passnt=pinfail카운터 + DA 보호 연동PIN 실패 횟수 추적
# === NV 인덱스 유형별 사용 예제 ===

# 1. Ordinary: EK 인증서 저장 (기본 NV)
tpm2_nvdefine 0x01800001 -C o -s 2048 \
  -a "ownerread|ownerwrite|authread|authwrite"
echo "my-application-config-data" | \
  tpm2_nvwrite 0x01800001 -C o -i -
tpm2_nvread 0x01800001 -C o

# 2. Counter: 부팅 카운터 (안티롤백)
tpm2_nvdefine 0x01800002 -C o -s 8 \
  -a "ownerread|ownerwrite|nt=counter"
tpm2_nvincrement 0x01800002 -C o
tpm2_nvread 0x01800002 -C o -s 8 | xxd

# 3. Bit Field: 일회성 프로비저닝 플래그
tpm2_nvdefine 0x01800003 -C o -s 8 \
  -a "ownerread|ownerwrite|nt=bits"
# 비트 0 설정 (한번 설정하면 해제 불가)
tpm2_nvsetbits 0x01800003 -C o -i 0x01
# 비트 1도 설정
tpm2_nvsetbits 0x01800003 -C o -i 0x02
# 현재 값: 0x03 (비트 0과 1이 설정됨)

# 4. Extend: PCR과 유사한 해시 체인
tpm2_nvdefine 0x01800004 -C o -s 32 \
  -a "ownerread|ownerwrite|nt=extend"
echo "event1" | sha256sum | awk '{print $1}' | xxd -r -p | \
  tpm2_nvextend 0x01800004 -C o -i -
echo "event2" | sha256sum | awk '{print $1}' | xxd -r -p | \
  tpm2_nvextend 0x01800004 -C o -i -

# 5. 쓰기 잠금 (Write Once — 재부팅까지 잠금)
tpm2_nvdefine 0x01800005 -C o -s 64 \
  -a "ownerread|ownerwrite|write_stclear"
echo "immutable-after-write" | \
  tpm2_nvwrite 0x01800005 -C o -i -
tpm2_nvwritelock 0x01800005 -C o
# 이후 쓰기 시도 → TPM_RC_NV_LOCKED
# 재부팅하면 잠금 해제

# NV 인덱스 목록 조회
tpm2_getcap handles-nv-index
# NV 인덱스 정보 조회
tpm2_nvreadpublic 0x01800001

TCG 표준 NV 인덱스

NV 인덱스 범위소유자용도
0x01C00002TCG (Endorsement)RSA 2048 EK 인증서
0x01C0000ATCG (Endorsement)ECC P-256 EK 인증서
0x01C00003TCG (Endorsement)RSA EK nonce
0x01C00004TCG (Endorsement)RSA EK 템플릿
0x01400001TCG (Platform)PPI (Physical Presence Interface) 요청
0x01800000-0x01BFFFFFOwner사용자 정의 NV 인덱스
0x01C00000-0x01FFFFFFEndorsementTCG 정의 NV 인덱스

프로비저닝 자동화

대규모 배포 환경에서 TPM 프로비저닝을 자동화하는 방법:

#!/bin/bash
# TPM 프로비저닝 자동화 스크립트

set -euo pipefail

# 1. TPM 기본 상태 확인
if ! tpm2_getcap properties-fixed > /dev/null 2>&1; then
    echo "ERROR: TPM 장치에 접근할 수 없습니다"
    exit 1
fi

# 2. TPM 버전 확인
TPM_VERSION=$(cat /sys/class/tpm/tpm0/tpm_version_major)
if [ "$TPM_VERSION" != "2" ]; then
    echo "ERROR: TPM 2.0이 필요합니다 (현재: TPM $TPM_VERSION)"
    exit 1
fi

# 3. EK 생성 및 인증서 확인
tpm2_createek -c /tmp/ek.ctx -G rsa2048 -u /tmp/ek.pub
if tpm2_nvread 0x01C00002 -o /tmp/ek_cert.der 2>/dev/null; then
    echo "EK 인증서 존재 — 제조사 CA 검증 가능"
    openssl x509 -inform DER -in /tmp/ek_cert.der \
      -subject -issuer -noout
else
    echo "WARNING: EK 인증서 없음 — 자체 생성된 EK"
fi

# 4. Owner/Endorsement 인가 값 설정 (빈 값이면)
if tpm2_changeauth -c o "$(openssl rand -hex 16)" 2>/dev/null; then
    echo "Owner 인가 값 설정 완료"
else
    echo "Owner 인가 값이 이미 설정되어 있음"
fi

# 5. SRK 영구 핸들 등록
tpm2_createprimary -C o -G rsa2048 -g sha256 -c /tmp/srk.ctx
tpm2_evictcontrol -C o -c /tmp/srk.ctx 0x81000001 || \
    echo "SRK가 이미 0x81000001에 존재함"

# 6. AK 생성 및 영구 핸들 등록
tpm2_createak -C /tmp/ek.ctx -c /tmp/ak.ctx \
  -u /tmp/ak.pub -n /tmp/ak.name -G rsa2048
tpm2_evictcontrol -C o -c /tmp/ak.ctx 0x81010001 || \
    echo "AK가 이미 0x81010001에 존재함"

# 7. DA 보호 파라미터 설정
tpm2_dictionarylockout --setup-parameters \
  --max-tries=10 \
  --recovery-time=300 \
  --lockout-recovery-time=3600

echo "TPM 프로비저닝 완료"
echo "  EK pub: /tmp/ek.pub"
echo "  AK pub: /tmp/ak.pub"
echo "  SRK handle: 0x81000001"
echo "  AK handle: 0x81010001"
⚠️

프로비저닝 보안 고려사항: 프로비저닝 스크립트에서 설정한 인가 값(비밀번호)은 반드시 안전하게 저장해야 합니다. 인가 값을 분실하면 해당 계층의 객체에 접근할 수 없으며, TPM2_Clear만이 복구 방법입니다. 서버 환경에서는 인가 값을 HashiCorp Vault나 AWS Secrets Manager 같은 비밀 관리 시스템에 저장하고, 프로비저닝 이후 로컬 복사본을 삭제하는 것을 권장합니다.

TPM 리소스 한계

TPM은 제한된 하드웨어 리소스를 가지므로, 동시에 로드 가능한 객체와 세션 수에 주의해야 합니다:

리소스일반적인 한계확인 방법비고
Transient 객체 슬롯3개TPM_PT_HR_TRANSIENT_MIN초과 시 TPM_RC_OBJECT_MEMORY
활성 세션 슬롯64개TPM_PT_HR_LOADED_MINHMAC + Policy 세션 합산
NV 영역 크기~8-16 KBTPM_PT_NV_BUFFER_MAXNV 인덱스 정의 + 데이터 합산
Persistent 객체~7개TPM_PT_HR_PERSISTENT_MINevictcontrol로 영구 저장된 키
PCR 뱅크SHA-1 + SHA-256 (+ SHA-384)tpm2_pcrreadBIOS에서 활성 뱅크 설정
# TPM 리소스 한계 확인
tpm2_getcap properties-fixed | grep -E "TRANSIENT|LOADED|PERSISTENT|NV_BUFFER"
# TPM_PT_HR_TRANSIENT_MIN: 0x3        ← 3개 transient 슬롯
# TPM_PT_HR_PERSISTENT_MIN: 0x7       ← 7개 persistent 핸들
# TPM_PT_NV_BUFFER_MAX: 0x800         ← 단일 NV read/write 최대 2048 바이트

# 현재 사용 중인 핸들 확인
tpm2_getcap handles-transient
tpm2_getcap handles-persistent
tpm2_getcap handles-loaded-session

# transient 슬롯 정리
tpm2_flushcontext -t  # 모든 transient 객체 flush
tpm2_flushcontext -s  # 모든 세션 flush

tpm2-tools 종합 가이드

tpm2-tss Software Stack Application (tpm2-tools, custom app) FAPI (Feature API) JSON 기반, 고수준 추상화 ESAPI (Enhanced System API) 세션/암호 자동 관리 SAPI (System API) 1:1 TPM 명령 매핑 TCTI (TPM Command Transmission Interface) device | mssim | tabrmd | swtpm | cmd tpm2-abrmd (선택) D-Bus 기반 Resource Manager /dev/tpmrm0 (권장) 커널 내장 Resource Manager swtpm (에뮬레이터) 개발/테스트용 TPM Hardware / Firmware

tpm2-tss 계층별 비교

계층추상화 수준세션 관리암호 연산용도
FAPI최고 (JSON, key path)자동자동어플리케이션 통합, 프로비저닝
ESAPI높음 (ESYS_TR 핸들)자동자동tpm2-tools, 일반 개발
SAPI낮음 (1:1 명령 매핑)수동수동성능 최적화, 커스텀 프로토콜

TCTI 전송 인터페이스

TCTI대상환경 변수
device/dev/tpmrm0TPM2TOOLS_TCTI=device:/dev/tpmrm0
tabrmdtpm2-abrmd (D-Bus)TPM2TOOLS_TCTI=tabrmd:bus_name=com.intel.tss2.Tabrmd
mssimMS TPM SimulatorTPM2TOOLS_TCTI=mssim:host=localhost,port=2321
swtpmswtpm Unix socketTPM2TOOLS_TCTI=swtpm:path=/tmp/swtpm-sock

실전 레시피 모음

# === RSA 키 생성 및 서명 ===
tpm2_createprimary -C o -c srk.ctx
tpm2_create -C srk.ctx -G rsa2048:rsassa-sha256 -u rsa.pub -r rsa.priv \
  -a "sign|fixedtpm|fixedparent|sensitivedataorigin|userwithauth"
tpm2_load -C srk.ctx -u rsa.pub -r rsa.priv -c rsa.ctx
echo "sign this" | tpm2_sign -c rsa.ctx -g sha256 -o rsa_sig.dat

# === ECC 키 생성 및 서명 ===
tpm2_create -C srk.ctx -G ecc256:ecdsa-sha256 -u ecc.pub -r ecc.priv \
  -a "sign|fixedtpm|fixedparent|sensitivedataorigin|userwithauth"
tpm2_load -C srk.ctx -u ecc.pub -r ecc.priv -c ecc.ctx
echo "sign this" | tpm2_sign -c ecc.ctx -g sha256 -o ecc_sig.dat

# === RSA 암호화/복호화 ===
tpm2_create -C srk.ctx -G rsa2048:oaep-sha256 -u enc.pub -r enc.priv \
  -a "decrypt|fixedtpm|fixedparent|sensitivedataorigin|userwithauth"
tpm2_load -C srk.ctx -u enc.pub -r enc.priv -c enc.ctx
echo "plaintext" | tpm2_rsaencrypt -c enc.ctx -o cipher.dat
tpm2_rsadecrypt -c enc.ctx -o plain.dat cipher.dat

# === NV 카운터 ===
tpm2_nvdefine 0x01500001 -s 8 -a "ownerread|ownerwrite|nt=counter"
tpm2_nvincrement 0x01500001
tpm2_nvread 0x01500001 | xxd

# === 난수 생성 ===
tpm2_getrandom 32 --hex
tpm2_getrandom 1024 -o random.bin

# === TPM 기능 조회 ===
tpm2_getcap algorithms
tpm2_getcap commands
tpm2_getcap properties-fixed
tpm2_getcap handles-persistent
tpm2_getcap handles-transient

실전 레시피 추가

# === PCR 정책으로 봉인된 ECC 키 생성 + 서명 검증 ===

# 1. SRK 생성
tpm2_createprimary -C o -c srk.ctx

# 2. PCR 정책 다이제스트 계산 (PCR 0+7)
tpm2_startauthsession --trial-session -S trial.ctx
tpm2_policypcr -S trial.ctx -l sha256:0,7
tpm2_policygetdigest -S trial.ctx -o pcr_policy.dgst
tpm2_flushcontext trial.ctx

# 3. ECC P-256 서명 키 생성 (PCR 정책 바인딩)
tpm2_create -C srk.ctx -G ecc256:ecdsa-sha256 \
  -u ecc_sign.pub -r ecc_sign.priv \
  -L pcr_policy.dgst \
  -a "sign|fixedtpm|fixedparent|sensitivedataorigin"

# 4. 키 로드
tpm2_load -C srk.ctx -u ecc_sign.pub -r ecc_sign.priv -c ecc_sign.ctx

# 5. PCR 정책 세션으로 서명
echo "Hello TPM" > message.txt
sha256sum message.txt | awk '{print $1}' | xxd -r -p > msg_digest.bin

tpm2_startauthsession --policy-session -S session.ctx
tpm2_policypcr -S session.ctx -l sha256:0,7
tpm2_sign -c ecc_sign.ctx -g sha256 \
  -o ecc_sig.dat -p session:session.ctx msg_digest.bin

# 6. 서명 검증 (공개키 추출 + OpenSSL 검증)
tpm2_readpublic -c ecc_sign.ctx -f pem -o ecc_pubkey.pem
openssl dgst -sha256 -verify ecc_pubkey.pem \
  -signature ecc_sig.dat message.txt
# === NV 카운터 생성 + 증가 + 읽기 ===

# 단조 증가 카운터 (Monotonic Counter) 정의
tpm2_nvdefine 0x01500010 -C o -s 8 \
  -a "ownerread|ownerwrite|nt=counter"

# 카운터 증가 (총 3회)
tpm2_nvincrement 0x01500010 -C o
tpm2_nvincrement 0x01500010 -C o
tpm2_nvincrement 0x01500010 -C o

# 카운터 값 읽기
tpm2_nvread 0x01500010 -C o -s 8 | xxd
# 출력: 00000000: 0000 0000 0000 0003  ........

# NV 인덱스 삭제
tpm2_nvundefine 0x01500010 -C o
# === 키 인증 (TPM2_Certify) ===
# AK가 특정 키가 이 TPM에 존재함을 인증

# 1. AK 생성
tpm2_createek -c ek.ctx
tpm2_createak -C ek.ctx -c ak.ctx -u ak.pub -n ak.name

# 2. 대상 키 생성
tpm2_createprimary -C o -c srk.ctx
tpm2_create -C srk.ctx -G rsa2048:rsassa-sha256 \
  -u target.pub -r target.priv -c target.ctx

# 3. AK로 대상 키 인증 (Certify)
tpm2_certify -c target.ctx -C ak.ctx \
  -g sha256 -o certify.out -s certify.sig

# 4. 인증 검증
tpm2_verifysignature -c ak.ctx -g sha256 \
  -m certify.out -s certify.sig
# === HMAC 세션 사용 ===
# 명령 파라미터의 무결성을 HMAC으로 보호

# 비밀번호로 보호된 키 생성
tpm2_createprimary -C o -c srk.ctx
tpm2_create -C srk.ctx -G rsa2048:rsassa-sha256 \
  -u hmac_key.pub -r hmac_key.priv -p "myKeyAuth" \
  -a "sign|fixedtpm|fixedparent|sensitivedataorigin|userwithauth"
tpm2_load -C srk.ctx -u hmac_key.pub -r hmac_key.priv -c hmac_key.ctx

# HMAC 세션 시작 (명령 파라미터 보호)
tpm2_startauthsession --hmac-session -S hmac_session.ctx

# HMAC 세션 + 비밀번호로 서명
echo "data" | tpm2_sign -c hmac_key.ctx -g sha256 \
  -o sig.dat -p "session:hmac_session.ctx+myKeyAuth"

tpm2_flushcontext hmac_session.ctx
# === EK 인증서 추출 + 검증 ===

# RSA EK 인증서 NV에서 추출
tpm2_nvread 0x01C00002 -o ek_rsa_cert.der

# 인증서 내용 확인
openssl x509 -inform DER -in ek_rsa_cert.der -text -noout | head -20
# Subject: ...Infineon/Nuvoton/STMicro...
# Issuer: TPM 제조사 CA

# 인증서 체인 검증 (제조사 CA 인증서 필요)
# Infineon 예시:
wget -q https://pki.infineon.com/OptigaRsaMfrCA049/OptigaRsaMfrCA049.crt
openssl verify -CAfile OptigaRsaMfrCA049.crt ek_rsa_cert.der

# ECC EK 인증서 (별도 NV 인덱스)
tpm2_nvread 0x01C0000A -o ek_ecc_cert.der 2>/dev/null || \
  echo "ECC EK 인증서 없음 (이 TPM은 RSA EK만 지원)"

# 온라인 EK 인증서 다운로드 (Intel PTT)
tpm2_createek -c ek.ctx -G rsa2048 -u ek.pub
tpm2_getekcertificate -o ek_cert_online.pem -u ek.pub \
  https://ekop.intel.com/ekcertservice/

tpm2-abrmd 상세

tpm2-abrmd(Access Broker and Resource Manager Daemon)는 D-Bus 기반의 사용자 공간 TPM 리소스 관리자입니다. 커널의 /dev/tpmrm0이 도입되기 전에 멀티프로세스 TPM 접근을 관리하기 위해 개발되었습니다:

비교 항목/dev/tpmrm0 (커널 RM)tpm2-abrmd
위치커널 내부사용자 공간 데몬
IPC 메커니즘파일 디스크립터(File Descriptor)D-Bus (system bus)
TCTI 이름device:/dev/tpmrm0tabrmd:bus_name=com.intel.tss2.Tabrmd
세션 관리fd별 자동 context save/loadD-Bus 연결별 context save/load
추가 설정불필요systemd 서비스, D-Bus 정책 필요
권장 환경일반 용도 (기본 권장)레거시, 커스텀 접근 제어(Access Control) 필요 시
# tpm2-abrmd 설치 및 설정
sudo dnf install tpm2-abrmd
sudo systemctl enable tpm2-abrmd --now

# D-Bus 서비스 확인
dbus-send --system --print-reply \
  --dest=com.intel.tss2.Tabrmd \
  /com/intel/tss2/Tabrmd \
  org.freedesktop.DBus.Introspectable.Introspect

# tpm2-tools에서 abrmd TCTI 사용
export TPM2TOOLS_TCTI="tabrmd:bus_name=com.intel.tss2.Tabrmd"
tpm2_pcrread sha256:0

# abrmd 상태 확인
systemctl status tpm2-abrmd
journalctl -u tpm2-abrmd -f

FAPI 활용

FAPI(Feature API)는 tpm2-tss의 최상위 추상화 계층으로, JSON 기반 프로파일과 키 경로(key path) 개념을 통해 TPM을 훨씬 간단하게 사용할 수 있습니다.

FAPI 구성

// /etc/tpm2-tss/fapi-config.json
{
    "profile_name": "P_ECCP256",
    "profile_dir": "/etc/tpm2-tss/fapi-profiles/",
    "user_dir": "~/.local/share/tpm2-tss/user/keystore",
    "system_dir": "/var/lib/tpm2-tss/system/keystore",
    "tcti": "device:/dev/tpmrm0",
    "log_dir": "/var/log/tpm2-tss",
    "ek_cert_less": "yes"
}

FAPI 키 경로 체계

경로설명
/P_ECCP256프로파일 루트 (ECC P-256 프로파일)
/P_ECCP256/HSStorage Hierarchy
/P_ECCP256/HS/SRKStorage Root Key
/P_ECCP256/HS/SRK/mykey사용자 정의 자식 키
/P_ECCP256/HEEndorsement Hierarchy
/P_ECCP256/HE/EKEndorsement Key
/P_ECCP256/HNNull Hierarchy
/ext/mykey외부 키 (TPM 외부)

FAPI C 프로그래밍

#include <tss2/tss2_fapi.h>

int main(void) {
    FAPI_CONTEXT *ctx = NULL;
    TSS2_RC      rc;
    uint8_t      *signature = NULL;
    size_t       sig_size;
    char         *sig_json = NULL;
    char         *cert = NULL;

    /* 1. FAPI 컨텍스트 초기화 */
    rc = Fapi_Initialize(&ctx, NULL);

    /* 2. TPM 프로비저닝 (최초 1회) */
    rc = Fapi_Provision(ctx, NULL, NULL, NULL);

    /* 3. 서명 키 생성 */
    rc = Fapi_CreateKey(ctx,
        "/P_ECCP256/HS/SRK/mySignKey",  /* 키 경로 */
        "sign,noDa",                     /* 속성 */
        NULL,                            /* 정책 경로 */
        NULL                             /* authValue */
    );

    /* 4. 서명 수행 */
    uint8_t digest[32] = { 0x01, 0x02, /* ... */ };
    rc = Fapi_Sign(ctx,
        "/P_ECCP256/HS/SRK/mySignKey",
        NULL,  /* padding scheme */
        digest, sizeof(digest),
        &signature, &sig_size,
        &sig_json, &cert
    );

    /* 5. 정리 */
    Fapi_Free(signature);
    Fapi_Free(sig_json);
    Fapi_Free(cert);
    Fapi_Finalize(&ctx);
    return 0;
}

tss2 CLI 도구

# FAPI 프로비저닝
tss2 provision

# 서명 키 생성
tss2 createkey --path /P_ECCP256/HS/SRK/mykey --type sign,noDa

# 서명
echo "data to sign" > data.txt
tss2 sign --keyPath /P_ECCP256/HS/SRK/mykey --digest data.txt --signature sig.pem

# 암호화
tss2 encrypt --keyPath /P_ECCP256/HS/SRK/myEncKey --plainText plain.txt --cipherText cipher.json

# 키 목록 조회
tss2 list --searchPath /

# 키 삭제
tss2 delete --path /P_ECCP256/HS/SRK/mykey
💡

FAPI vs ESAPI 선택 기준: 일반적인 어플리케이션(키 생성, 서명, 암호화)은 FAPI로 충분하며, 코드량이 ESAPI 대비 1/3 수준입니다. 그러나 세밀한 세션 제어, 커스텀 인가 정책, PCR 직접 조작이 필요하면 ESAPI를 사용하세요. SAPI는 성능 최적화나 비표준 TPM 확장 명령에만 권장됩니다.

TPM2 PKCS#11 통합

tpm2-pkcs11은 TPM을 PKCS#11 토큰으로 노출하여, OpenSSL, OpenSSH, Firefox/Chrome 등 PKCS#11을 지원하는 어플리케이션에서 TPM 키를 직접 사용할 수 있게 합니다:

# tpm2-pkcs11 설치
sudo dnf install tpm2-pkcs11 tpm2-pkcs11-tools

# PKCS#11 토큰 초기화
tpm2_ptool init --primary-auth=owner_password
tpm2_ptool addtoken --pid=1 --sopin=sopin123 --userpin=userpin123 \
  --label=tpm-token

# RSA 서명 키 추가
tpm2_ptool addkey --algorithm=rsa2048 --label=tpm-token \
  --userpin=userpin123 --key-label=my-rsa-key

# ECC 서명 키 추가
tpm2_ptool addkey --algorithm=ecc256 --label=tpm-token \
  --userpin=userpin123 --key-label=my-ecc-key

# PKCS#11 모듈 경로
PKCS11_MODULE="/usr/lib64/pkcs11/libtpm2_pkcs11.so"

# OpenSSL에서 TPM 키로 서명
openssl dgst -sha256 -sign "pkcs11:token=tpm-token;object=my-rsa-key" \
  -engine pkcs11 -keyform engine \
  -out signature.bin document.txt

# OpenSSH에서 TPM 키를 SSH 키로 사용
ssh-keygen -D "$PKCS11_MODULE" > tpm_ssh_pubkey.pub
# → 이 공개키를 서버의 authorized_keys에 등록
ssh -I "$PKCS11_MODULE" user@server

# pkcs11-tool로 토큰 정보 조회
pkcs11-tool --module="$PKCS11_MODULE" --list-objects

# pkcs11-tool로 서명 수행
pkcs11-tool --module="$PKCS11_MODULE" \
  --sign --mechanism SHA256-RSA-PKCS \
  --label my-rsa-key --pin userpin123 \
  --input-file data.bin --output-file sig.bin

OpenSSL TPM2 Provider

tpm2-openssl은 OpenSSL 3.x의 Provider 인터페이스를 통해 TPM 키를 투명하게 사용할 수 있게 합니다:

# tpm2-openssl 설치
sudo dnf install tpm2-openssl

# TPM에 RSA 키 생성
openssl genpkey -provider tpm2 -algorithm RSA -pkeyopt bits:2048 \
  -out tpm_rsa_key.pem

# TPM 키로 CSR(인증서 서명 요청) 생성
openssl req -new -provider tpm2 -provider default \
  -key tpm_rsa_key.pem -out request.csr \
  -subj "/CN=TPM-Protected-Server"

# TPM 키로 TLS 서버 실행
openssl s_server -provider tpm2 -provider default \
  -cert server.crt -key tpm_rsa_key.pem \
  -port 4433
ℹ️

TPM 키 보호 TLS: tpm2-openssl이나 tpm2-pkcs11을 사용하면 TLS 서버의 개인키를 TPM 내부에 보호할 수 있습니다. 개인키가 파일시스템에 평문으로 존재하지 않으므로, 서버가 해킹되더라도 개인키 탈취가 불가능합니다. Nginx는 PKCS#11 엔진을, Apache httpd는 mod_ssl의 SSLCryptoDevice를 통해 TPM 키를 사용할 수 있습니다.

커널 설정 옵션

TPM 관련 Kconfig

설정모듈설명
CONFIG_TCG_TPMtpmTPM 서브시스템 코어 — 필수
CONFIG_TCG_TIStpm_tisTIS 인터페이스 (LPC/SPI dTPM)
CONFIG_TCG_TIS_SPItpm_tis_spiSPI 버스 TPM (현대 dTPM 주류)
CONFIG_TCG_TIS_SPI_CR50tpm_tis_spiGoogle Cr50/Ti50 (Chromebook)
CONFIG_TCG_TIS_I2Ctpm_tis_i2cI2C 버스 TPM (임베디드/IoT)
CONFIG_TCG_TIS_I2C_CR50cr50_i2cGoogle Cr50 I2C 인터페이스
CONFIG_TCG_TIS_I2C_INFINEONtpm_i2c_infineonInfineon SLB 9635/9645 (I2C)
CONFIG_TCG_CRBtpm_crbCRB 인터페이스 (fTPM 기본)
CONFIG_TCG_FTPM_TEEtpm_ftpm_teeTEE 기반 fTPM (OP-TEE)
CONFIG_TCG_VTPM_PROXYtpm_vtpm_proxyvTPM 프록시 (테스트용)
CONFIG_TCG_TIS_ST33ZP24_SPIst33zp24_spiSTMicro ST33ZP24 (SPI)
CONFIG_TCG_TIS_ST33ZP24_I2Cst33zp24_i2cSTMicro ST33ZP24 (I2C)
CONFIG_TCG_ATMELtpm_atmelAtmel TPM (레거시)
CONFIG_TCG_INFINEONtpm_infineonInfineon TPM (레거시 LPC)
CONFIG_TCG_NSCtpm_nscNational Semiconductor (레거시)

TPM 관련 보안 기능

설정설명
CONFIG_TRUSTED_KEYStrusted 키 타입 (TPM/TEE 백엔드)
CONFIG_TRUSTED_KEYS_TPMtrusted 키의 TPM 백엔드
CONFIG_ENCRYPTED_KEYSencrypted 키 타입
CONFIG_HW_RANDOM_TPMTPM을 hwrng 소스로 등록
CONFIG_IMAIMA (Integrity Measurement Architecture)
CONFIG_IMA_MEASURE_PCR_IDXIMA PCR 인덱스 (기본 10)
CONFIG_EVMEVM (Extended Verification Module)
CONFIG_INTEGRITY_PLATFORM_KEYRINGUEFI 플랫폼 키링
CONFIG_INTEGRITY_MACHINE_KEYRING머신 키링 (MOK)

개발용 .config 스니펫

# TPM 2.0 기본 지원
CONFIG_TCG_TPM=y
CONFIG_TCG_TIS=y
CONFIG_TCG_TIS_SPI=m
CONFIG_TCG_TIS_I2C=m
CONFIG_TCG_CRB=y
CONFIG_TCG_FTPM_TEE=m
CONFIG_TCG_VTPM_PROXY=m

# TPM 기반 키링
CONFIG_TRUSTED_KEYS=y
CONFIG_TRUSTED_KEYS_TPM=y
CONFIG_ENCRYPTED_KEYS=y

# TPM 난수
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_TPM=y

# 무결성 측정
CONFIG_IMA=y
CONFIG_IMA_MEASURE_PCR_IDX=10
CONFIG_EVM=y
CONFIG_INTEGRITY_SIGNATURE=y

# 보안 부팅 체인
CONFIG_INTEGRITY_PLATFORM_KEYRING=y
CONFIG_INTEGRITY_MACHINE_KEYRING=y
CONFIG_LOAD_UEFI_KEYS=y

환경별 추천 커널 설정 프로파일

# ═══════════════════════════════════════════
# 프로파일 1: 서버 환경 (최소 필수 + IMA + Audit)
# ═══════════════════════════════════════════
CONFIG_TCG_TPM=y
CONFIG_TCG_TIS=y           # dTPM SPI/LPC
CONFIG_TCG_CRB=y           # fTPM (Intel PTT / AMD PSP)
CONFIG_HW_RANDOM_TPM=y     # TPM RNG → 엔트로피 풀
CONFIG_TRUSTED_KEYS=y
CONFIG_TRUSTED_KEYS_TPM=y
CONFIG_ENCRYPTED_KEYS=y
CONFIG_IMA=y
CONFIG_IMA_MEASURE_PCR_IDX=10
CONFIG_IMA_APPRAISE=y      # 파일 무결성 검증
CONFIG_EVM=y
CONFIG_INTEGRITY_SIGNATURE=y
CONFIG_INTEGRITY_PLATFORM_KEYRING=y
CONFIG_INTEGRITY_MACHINE_KEYRING=y
CONFIG_INTEGRITY_AUDIT=y   # IMA 이벤트 audit 로그
# CONFIG_TCG_VTPM_PROXY is not set  (서버에서 불필요)
# ═══════════════════════════════════════════
# 프로파일 2: 개발/테스트 환경 (vtpm_proxy + swtpm)
# ═══════════════════════════════════════════
CONFIG_TCG_TPM=y
CONFIG_TCG_TIS=y
CONFIG_TCG_CRB=y
CONFIG_TCG_VTPM_PROXY=m    # vTPM 테스트용 프록시
CONFIG_HW_RANDOM_TPM=y
CONFIG_TRUSTED_KEYS=m
CONFIG_TRUSTED_KEYS_TPM=m
CONFIG_ENCRYPTED_KEYS=m
CONFIG_IMA=y
CONFIG_EVM=y
# swtpm + QEMU vTPM 테스트에 필요
CONFIG_VHOST=y
CONFIG_VHOST_NET=m
# ═══════════════════════════════════════════
# 프로파일 3: 임베디드/IoT (fTPM + 최소 메모리)
# ═══════════════════════════════════════════
CONFIG_TCG_TPM=y
CONFIG_TCG_CRB=y           # fTPM 전용
CONFIG_TCG_FTPM_TEE=y      # OP-TEE fTPM (ARM)
CONFIG_HW_RANDOM_TPM=y
CONFIG_TRUSTED_KEYS=y
CONFIG_TRUSTED_KEYS_TPM=y
CONFIG_ENCRYPTED_KEYS=y
# CONFIG_TCG_TIS is not set         (dTPM 없으므로 불필요)
# CONFIG_TCG_TIS_SPI is not set
# CONFIG_TCG_TIS_I2C is not set
# CONFIG_TCG_VTPM_PROXY is not set
# CONFIG_IMA is not set             (메모리 절약, 필요 시 활성화)

빌드 의존성

TPM 관련 Kconfig 옵션들의 의존성 관계를 이해하면 빌드 오류를 방지할 수 있습니다:

설정의존 대상비고
CONFIG_TCG_TPMCRYPTOTPM 명령에 해시/암호 연산 필요
CONFIG_TCG_TIS_SPITCG_TIS_CORE, SPITIS 코어 + SPI 서브시스템
CONFIG_TCG_TIS_I2CTCG_TIS_CORE, I2CTIS 코어 + I2C 서브시스템
CONFIG_TCG_CRBACPICRB는 ACPI TPM2 테이블 필수
CONFIG_TCG_FTPM_TEETEE, OPTEETEE 프레임워크 + OP-TEE 드라이버
CONFIG_TRUSTED_KEYS_TPMTCG_TPM, KEYS, ENCRYPTED_KEYStrusted 키는 encrypted 키 타입에도 의존
CONFIG_HW_RANDOM_TPMHW_RANDOM, TCG_TPMhwrng 프레임워크 + TPM 코어
CONFIG_IMAINTEGRITY, SECURITYIMA는 integrity 프레임워크의 하위 기능
CONFIG_EVMINTEGRITY, SECURITY, KEYSEVM은 키링 시스템 의존
💡

빌드 검증: TPM 관련 설정 후 make olddefconfig를 실행하면 의존성 누락으로 자동 비활성화되는 옵션을 확인할 수 있습니다. scripts/diffconfig로 변경 사항을 비교하세요. 또한 make menuconfig에서 해당 옵션의 Depends on: 항목을 확인하면 정확한 의존 관계를 파악할 수 있습니다.

트러블슈팅

TPM Troubleshooting Decision Tree /dev/tpm0 존재하는가? No 1. BIOS에서 TPM 활성화 확인 2. dmesg | grep tpm 확인 3. CONFIG_TCG_* 커널 설정 확인 Yes tpm2_getcap properties-fixed 성공? No tpm2-tss/tpm2-tools 버전 확인 TCTI 설정 확인: $TPM2TOOLS_TCTI Yes PCR 전부 0인가? Yes TPM 초기화 안 됨 / Measured Boot 비활성 BIOS에서 Measured Boot 활성화 TPM_RC 오류? 주요 오류 코드: TPM_RC_LOCKOUT — DA 잠금 TPM_RC_AUTH_FAIL — 인증 실패 TPM_RC_NV_LOCKED — NV 쓰기 잠금 TPM_RC_RETRY — 재시도 필요 권한 문제 ls -la /dev/tpm0 /dev/tpmrm0 udev 규칙: /etc/udev/rules.d/tpm.rules KERNEL=="tpm*",GROUP="tss",MODE="0660"

디버깅(Debugging) 명령 모음

# TPM 드라이버 로그 확인
dmesg | grep -i tpm
journalctl -k | grep -i tpm

# TPM 장치 존재 확인
ls -la /dev/tpm* /dev/tpmrm*

# sysfs TPM 정보
cat /sys/class/tpm/tpm0/device/description
cat /sys/class/tpm/tpm0/tpm_version_major

# TPM 고정 속성 조회
tpm2_getcap properties-fixed

# TPM 가변 속성 조회 (DA 상태, 잠금 정보 등)
tpm2_getcap properties-variable

# 지원 알고리즘 확인
tpm2_getcap algorithms

# ACPI TPM2 테이블
sudo cat /sys/firmware/acpi/tables/TPM2 | hexdump -C

# udev 규칙 설정 (tss 그룹 접근 허용)
# /etc/udev/rules.d/60-tpm.rules
# KERNEL=="tpm[0-9]*", TAG+="systemd", GROUP="tss", MODE="0660"
# KERNEL=="tpmrm[0-9]*", TAG+="systemd", GROUP="tss", MODE="0660"
sudo udevadm control --reload-rules && sudo udevadm trigger

주요 오류 코드

오류 코드원인해결
TPM_RC_LOCKOUT0x0921DA 임계값 초과 잠금tpm2_dictionarylockout --clear-lockout
TPM_RC_AUTH_FAIL0x098E인가 값 불일치올바른 비밀번호/정책 확인
TPM_RC_NV_LOCKED0x0148NV 쓰기 잠금 상태재부팅 (WRITE_STCLEAR) 또는 해제
TPM_RC_RETRY0x0922TPM이 바쁨 (재시도 요청)잠시 후 재시도
TPM_RC_YIELDED0x0800TPM이 양보 (긴 연산 중)계속 폴링
TPM_RC_OBJECT_MEMORY0x0902transient 슬롯 부족tpm2_flushcontext 또는 tpmrm 사용
TPM_RC_SESSION_MEMORY0x0903세션 슬롯 부족사용하지 않는 세션 flush
TPM_RC_POLICY_FAIL0x099D정책 조건 불충족PCR 값, 정책 순서 확인

알려진 이슈

⚠️

AMD fTPM 랜덤 딜레이: AMD Zen 3 이상 플랫폼에서 fTPM이 간헐적으로 수백 밀리초의 지연을 유발합니다. NV 쓰기 시 SPI 플래시 접근이 원인입니다. 커널 파라미터 tpm_tis.interrupts=0으로 완화하거나, BIOS에서 dTPM으로 전환하면 해결됩니다.

ℹ️

Intel PTT BIOS 설정: Intel 플랫폼에서 TPM이 감지되지 않으면, BIOS에서 "Intel Platform Trust Technology(PTT)" 또는 "Security Device Support"가 활성화되어 있는지 확인하세요. 일부 BIOS는 "fTPM"이나 "dTPM" 중 하나만 활성화하도록 옵션이 분리되어 있습니다.

TPM_RC 에러 코드 상세

TPM 2.0의 에러 코드는 Format 0(TPM 1.2 호환)과 Format 1(TPM 2.0 전용) 두 가지 형식이 있습니다. Format 1 에러 코드는 파라미터/핸들/세션 번호를 인코딩합니다:

TPM_RC Format 1 에러 코드 구조 (32비트):
  Bit 31-12: 0 (예약)
  Bit 11-8:  P/H/S 인덱스 (어떤 파라미터/핸들/세션이 문제인지)
  Bit 7:     Format 비트 (1=Format 1)
  Bit 6:     P 비트 (1=Parameter 관련, 0=Handle/Session 관련)
  Bit 5-0:   에러 번호

예: TPM_RC_VALUE | TPM_RC_P | TPM_RC_2
  = 0x0004 | 0x0040 | 0x0200 = 0x0244
  → "두 번째 파라미터의 값이 유효하지 않음"
에러 코드카테고리원인해결 방법
TPM_RC_INITIALIZE0x0100명령TPM이 초기화되지 않음 (Startup 미호출)tpm2_startup -c 실행
TPM_RC_FAILURE0x0101명령TPM 자체 테스트 실패tpm2_selftest -f 실행, 펌웨어 업데이트
TPM_RC_DISABLED0x0120명령해당 계층이 비활성화됨BIOS에서 계층 활성화 또는 tpm2_hierarchycontrol
TPM_RC_LOCKOUT0x0921세션DA 잠금 — 인가 실패 임계값 초과tpm2_dictionarylockout --clear-lockout -p <lockout_pw>
TPM_RC_AUTH_FAIL0x098E세션인가 값(비밀번호/HMAC) 불일치올바른 인가 값 확인, DA 카운터 확인
TPM_RC_POLICY_FAIL0x099D세션정책 조건 미충족PCR 값 확인, 정책 순서 검증
TPM_RC_VALUE0x0004파라미터파라미터 값이 유효 범위 밖명령 파라미터 검토
TPM_RC_SIZE0x0015파라미터데이터 크기가 예상과 다름명령 구조 크기 확인
TPM_RC_KEY_SIZE0x002C파라미터키 크기가 지원되지 않음tpm2_getcap algorithms로 지원 크기 확인
TPM_RC_NV_LOCKED0x0148NVNV 인덱스가 쓰기 잠금 상태재부팅 (STCLEAR) 또는 tpm2_nvwritelock 해제
TPM_RC_NV_SPACE0x014FNVNV 저장 공간 부족불필요한 NV 인덱스 삭제
TPM_RC_OBJECT_MEMORY0x0902리소스transient 객체 슬롯 부족 (보통 3개)tpm2_flushcontext -t 또는 /dev/tpmrm0 사용
TPM_RC_SESSION_MEMORY0x0903리소스활성 세션 슬롯 부족tpm2_flushcontext -s
TPM_RC_RETRY0x0922경고TPM이 바빠서 재시도 요청짧은 대기 후 재시도
TPM_RC_NV_UNAVAILABLE0x0923경고NV 영역이 아직 사용 불가 (부팅 초기)잠시 대기 후 재시도

실전 디버깅 시나리오

시나리오 1: systemd-cryptenroll TPM_RC_LOCKOUT

# 문제: systemd-cryptenroll이 TPM_RC_LOCKOUT (0x0921) 에러 발생
# 원인: 잘못된 비밀번호를 여러 번 입력하여 DA 잠금 활성화

# 1. DA 상태 확인
tpm2_getcap properties-variable | grep -i lockout
# lockoutCounter: 5   ← DA 실패 횟수가 maxTries에 도달
# maxTries: 5
# recoveryTime: 600   ← 자동 복구까지 600초

# 2. 방법 A: lockout 비밀번호로 즉시 해제
tpm2_dictionarylockout --clear-lockout -p 

# 3. 방법 B: recoveryTime만큼 대기 (비밀번호 모를 때)
# → 600초(10분) 대기하면 카운터가 자동 감소

# 4. 방법 C: TPM Clear + 재프로비저닝 (최후 수단)
# 주의: 모든 키/NV 데이터 삭제!
tpm2_clear -c p

시나리오 2: PCR 값이 전부 0

# 문제: tpm2_pcrread에서 모든 PCR이 0x000...000
# 원인: BIOS에서 Measured Boot가 비활성화됨

# 1. PCR 값 확인
tpm2_pcrread sha256:0,1,7
# sha256:
#   0 : 0x0000000000000000...  ← 모두 0이면 측정 안 됨

# 2. BIOS 설정 확인 항목:
# - "Measured Boot" 또는 "TCG Boot" → Enabled
# - "Secure Boot" → 함께 활성화 권장
# - TPM 장치 자체는 활성화되어 있어도 측정이 비활성화일 수 있음

# 3. 이벤트 로그 존재 여부 확인
ls -la /sys/kernel/security/tpm0/binary_bios_measurements
# 파일이 없거나 크기가 0 → 펌웨어가 측정을 수행하지 않음

시나리오 3: AMD fTPM 랜덤 프리즈

# 문제: AMD Ryzen 5000/7000에서 간헐적 마우스/오디오 끊김
# 원인: fTPM이 SPI 플래시에 NV 데이터를 쓸 때 수백 ms 블록킹

# 1. 증상 확인
dmesg | grep -i "tpm\|ftpm"
# tpm_crb MSFT0101:00: [Firmware Bug]: ...
# 간헐적으로 수백 ms 지연 로그

# 2. 해결 방법 A: BIOS에서 fTPM 비활성화 (dTPM이 있는 경우)
# BIOS → Security → Trusted Computing → fTPM → Disabled
# BIOS → Security → Trusted Computing → dTPM → Enabled

# 3. 해결 방법 B: 커널 파라미터 (완전한 해결은 아님)
# /etc/default/grub:
# GRUB_CMDLINE_LINUX="tpm_tis.interrupts=0"

# 4. 해결 방법 C: BIOS 업데이트
# AMD AGESA 1.2.0.7 이후 버전에서 일부 완화됨

시나리오 4: /dev/tpm0 권한 거부

# 문제: Permission denied on /dev/tpm0 or /dev/tpmrm0
# 원인: 일반 사용자에게 장치 접근 권한 없음

# 1. 현재 권한 확인
ls -la /dev/tpm* /dev/tpmrm*
# crw-rw---- 1 tss root 10, 224 ... /dev/tpm0
# crw-rw---- 1 tss tss  10, 225 ... /dev/tpmrm0

# 2. tss 그룹에 사용자 추가
sudo usermod -aG tss $USER
# → 재로그인 필요

# 3. udev 규칙 생성 (영구 설정)
sudo tee /etc/udev/rules.d/60-tpm.rules << 'EOF'
# TPM 장치에 tss 그룹 접근 허용
KERNEL=="tpm[0-9]*", TAG+="systemd", GROUP="tss", MODE="0660"
KERNEL=="tpmrm[0-9]*", TAG+="systemd", GROUP="tss", MODE="0660"
EOF

# 4. udev 규칙 적용
sudo udevadm control --reload-rules
sudo udevadm trigger

시나리오 5: tpm2_pcrread 행

# 문제: tpm2_pcrread가 무기한 대기 (hang)
# 원인: TPM이 self-test에서 멈춤 또는 이전 명령이 응답하지 않음

# 1. TPM 상태 확인
dmesg | tail -20 | grep -i tpm
# "tpm tpm0: tpm2_get_random failed with -62" → -ETIME (타임아웃)

# 2. TPM 리셋 시도 (소프트 리셋)
# cancel 파일에 쓰기로 현재 명령 취소
echo 1 | sudo tee /sys/class/tpm/tpm0/cancel

# 3. TPM 드라이버 리로드
sudo modprobe -r tpm_crb  # 또는 tpm_tis
sudo modprobe tpm_crb

# 4. 지속되면: 플랫폼 재부팅 (TPM 하드웨어 리셋)
# fTPM의 경우 BIOS에서 "TPM Reset" 또는 "Clear TPM" 실행

# 5. 디버깅: ftrace로 TPM 내부 호출 추적
echo 1 | sudo tee /sys/kernel/debug/tracing/events/tpm/enable
cat /sys/kernel/debug/tracing/trace_pipe | head -50

TPM 보안 모범 사례

TPM을 실무 환경에서 안전하게 운영하기 위한 보안 모범 사례를 정리합니다.

계층 인가 관리

계층인가 설정 권장비밀번호 저장 위치비고
Owner (SH)반드시 설정비밀 관리 시스템 (Vault 등)SRK, persistent 키 관리 권한
Endorsement (EH)반드시 설정비밀 관리 시스템EK 생성/인증서 접근 권한
Lockout반드시 설정오프라인 안전 저장 (금고)DA 잠금 해제용 — 분실 시 TPM Clear 필요
PlatformBIOS 관리BIOS 설정일반적으로 사용자가 직접 관리하지 않음

키 관리 원칙

  1. Persistent 핸들 최소화: persistent 객체는 TPM NV 공간을 영구 점유하므로, SRK와 AK 정도만 persistent로 등록하고 나머지는 필요 시 transient로 로드
  2. 키 계층 활용: SRK를 루트로 하여 용도별 하위 키(서명용, 암호화용, 봉인용)를 생성. 키 블롭을 파일시스템에 저장하여 필요 시 로드
  3. 키 속성 제한: fixedTPM|fixedParent|sensitiveDataOrigin 속성으로 키가 다른 TPM으로 복제되거나 외부에서 주입되는 것을 방지
  4. 정책 기반 접근: 비밀번호 대신 PolicyPCR, PolicyAuthorize 등 정책 기반 인가를 사용하여 조건부 접근 제어 구현

운영 보안 체크리스트

💡

TPM 운영 체크리스트:

  • 계층 인가 값이 기본(빈 값)이 아닌지 확인
  • DA 보호 파라미터가 적절하게 설정되었는지 확인 (maxTries ≤ 10)
  • EK 인증서가 존재하고 제조사 CA로 검증 가능한지 확인
  • TPM 펌웨어가 최신 버전인지 확인 (알려진 취약점 패치)
  • Measured Boot가 활성화되고 이벤트 로그가 정상 생성되는지 확인
  • LUKS2 봉인 사용 시 복구 키슬롯이 존재하는지 확인
  • /dev/tpm0, /dev/tpmrm0 권한이 적절한지 확인 (tss 그룹)
  • 불필요한 persistent 객체와 NV 인덱스가 없는지 주기적 점검

TPM 기반 서비스 배포 아키텍처

TPM-Based Service Deployment Production Server LUKS2 + TPM (PCR 7 봉인) Keylime Agent (IMA 모니터링) Trusted Keys → dm-crypt Secure Boot + Measured Boot dTPM 2.0 (EAL4+) Management Plane Keylime Verifier + Registrar Tang Server (NBDE) Vault (비밀 관리) SIEM (보안 이벤트 수집) Compliance NIST SP 800-147B 플랫폼 펌웨어 무결성 NIST SP 800-155 BIOS 무결성 측정 PCI DSS 4.0 암호화 키 보호 (Req 3.5) FIPS 140-3 암호 모듈 인증

일반적인 실수와 방지

실수결과방지 방법
복구 키 없이 TPM만으로 LUKS 봉인TPM 교체/고장 시 데이터 영구 손실반드시 --recovery-key로 복구 키슬롯 추가
모든 PCR에 바인딩 (PCR 0-9)사소한 변경에도 잠금, 빈번한 재봉인 필요PCR 7(Secure Boot 정책)만 또는 Signed PCR Policy 사용
계층 인가 값 미설정누구나 TPM 오너 조작 가능프로비저닝 시 Owner/Endorsement/Lockout 인가 값 설정
DA 보호 미설정무제한 비밀번호 시도 가능maxTries=10 이하, recoveryTime=300초 이상 설정
dTPM SPI 버스 보안 미고려물리적 SPI 스니핑으로 명령/응답 노출파라미터 암호화 세션 사용, 또는 fTPM 전환
TPM 펌웨어 업데이트 미적용알려진 취약점(TPM-FAIL 등) 노출제조사 보안 공지 모니터링, 정기 업데이트

외부 참고 자료