LE Secure Connections 페어링
LE Secure Connections(LESC)는 BLE 4.2에서 도입된 페어링 방식이다. Legacy Pairing이 약한 TK(Temporary Key)를 기반으로 키를 만들어 패시브 스니핑에 취약한 문제를 ECDH로 해결했다. BLE 보안 연결과 페어링에서 Legacy와 LESC의 차이를 개괄적으로 다뤘다. 이 문서는 LESC의 암호학적 흐름을 구체적으로 정리한다.
Legacy Pairing의 문제
Legacy Pairing의 키 생성 과정:
TK (Temporary Key)
Just Works: TK = 0
Passkey: TK = 6자리 숫자 (최대 20비트)
↓
STK = s1(TK, Srand, Mrand) ← TK를 알면 역산 가능
↓
암호화 → LTK 배포
TK가 0이거나 20비트 숫자이므로, 페어링 과정을 캡처하면 오프라인에서 STK를 역산할 수 있다. 이건 암호화를 깨는 것이 아니라 키 자체가 약한 것이다.
LESC는 ECDH(Elliptic Curve Diffie-Hellman)로 키를 교환해서, 공개 키만 보고는 공유 시크릿을 알 수 없게 만든다.
ECDH 키 교환
LESC는 P-256 타원 곡선을 사용한다.
[Initiator] [Responder]
SK_a (비밀키, 256비트) SK_b (비밀키, 256비트)
PK_a = SK_a × G (공개키) PK_b = SK_b × G (공개키)
──── PK_a ────→
←──── PK_b ────
DHKey = SK_a × PK_b DHKey = SK_b × PK_a
= SK_a × SK_b × G = SK_b × SK_a × G
(양쪽이 같은 값) (양쪽이 같은 값)
공격자는 PK_a, PK_b만 볼 수 있다. P-256 곡선의 이산 로그 문제(ECDLP) 때문에 공개키에서 비밀키를 역산할 수 없고, DHKey도 알 수 없다.
Legacy와의 핵심 차이: Legacy는 TK(약한 비밀)를 기반으로 모든 키를 파생한다. LESC는 ECDH로 강한 공유 시크릿(DHKey)을 먼저 만들고, 인증(Passkey 등)은 이 DHKey를 검증하는 역할만 한다.
암호 함수
LESC 페어링에서 사용하는 함수는 4개다. 모두 AES-CMAC 기반이다.
| 함수 | 입력 | 출력 | 역할 |
|---|---|---|---|
| f4 | PK_a, PK_b, Nonce, Z | 128비트 Confirm | 공개키 커밋먼트 검증 |
| f5 | DHKey, N_a, N_b, ADDR_a, ADDR_b | MacKey + LTK | 키 파생 |
| f6 | MacKey, N_a, N_b, r, IOcap, ADDR | 128비트 Check | DHKey 검증 |
| g2 | PK_a, PK_b, N_a, N_b | 6자리 숫자 | Numeric Comparison 표시값 |
f4 — Commitment
Confirm = f4(PK, PK_peer, Nonce, Z)
= AES-CMAC(PK, PK_peer || Nonce || Z)
Nonce를 공개하기 전에 Confirm을 먼저 보낸다. 상대가 Nonce를 받은 후 f4를 재계산해서 Confirm과 비교한다. Nonce를 바꿔치기하면 Confirm이 맞지 않으므로 탐지된다.
Z 값은 Association Model에 따라 다르다:
- Just Works / Numeric Comparison: Z = 0
- Passkey Entry: Z = Passkey의 현재 비트 (0 또는 1)
f5 — 키 파생
MacKey || LTK = f5(DHKey, N_a, N_b, ADDR_a, ADDR_b)
DHKey에서 두 가지 키를 파생한다:
- MacKey: f6에서 DHKey check에 사용. 페어링 완료 후 폐기
- LTK: 연결 암호화 키. 본딩 시 Flash에 저장
f6 — DHKey Check
Check = f6(MacKey, N_a, N_b, r, IOcap, ADDR_a, ADDR_b)
양쪽이 같은 DHKey를 가지고 있는지 검증한다. r 값은 Association Model에 따라 다르다:
- Just Works / Numeric Comparison: r = 0
- Passkey Entry: r = Passkey (6자리 숫자 전체)
g2 — 6자리 표시값
Display = g2(PK_a, PK_b, N_a, N_b) mod 1,000,000
Numeric Comparison에서 양쪽 화면에 표시할 6자리 숫자를 생성한다. 중간자가 끼면 PK가 달라지므로 숫자가 달라진다.
Association Model별 흐름
ECDH 공개키 교환까지는 모든 모델이 동일하다. 그 이후 인증 방식이 갈린다.
Just Works
사용자 확인 없이 진행한다. MITM 방어가 안 된다.
[Initiator] [Responder]
──── PK_a ────→
←──── PK_b ────
(DHKey 계산) (DHKey 계산)
Cb = f4(PK_b, PK_a, Nb, 0)
←──── Cb ──────
──── Na ──────→
←──── Nb ──────
Ca = f4(PK_a, PK_b, Na, 0)
Ca 검증 (건너뜀) Cb 검증 (건너뜀)
(f5로 MacKey, LTK 파생) (f5로 MacKey, LTK 파생)
Ea = f6(MacKey, Na, Nb, 0, ...)
──── Ea ──────→ Ea 검증
Eb = f6(MacKey, Nb, Na, 0, ...)
←──── Eb ──────
Eb 검증
──── 암호화 시작 ──→
Just Works에서도 ECDH 덕분에 패시브 스니핑은 방어된다. 공격자가 PK_a, PK_b, Na, Nb를 모두 캡처해도 DHKey를 알 수 없으므로 LTK를 파생할 수 없다.
Numeric Comparison
양쪽 화면에 6자리 숫자를 표시하고, 사용자가 일치 여부를 확인한다.
[Initiator] [Responder]
──── PK_a ────→
←──── PK_b ────
Cb = f4(PK_b, PK_a, Nb, 0)
←──── Cb ──────
──── Na ──────→
←──── Nb ──────
Ca = f4(PK_a, PK_b, Na, 0)
Ca, Cb 검증
Va = g2(PK_a, PK_b, Na, Nb) Vb = g2(PK_a, PK_b, Na, Nb)
화면에 Va 표시 화면에 Vb 표시
사용자: "둘 다 같은 숫자? → Yes"
(f5, f6으로 LTK 파생 + DHKey check)
중간자가 양쪽과 각각 ECDH를 수행하면, 자신의 PK가 원래 디바이스의 PK와 다르므로 g2 결과가 달라진다. 사용자가 숫자가 다른 것을 보고 거부하면 MITM이 차단된다.
Passkey Entry — 20라운드 커밋먼트
LESC Passkey Entry는 Legacy와 완전히 다르게 동작한다. 6자리 passkey(20비트)를 한 비트씩 20라운드에 걸쳐 검증한다.
Passkey = 6자리 숫자 → 20비트 (예: 123456 → 0x1E240)
라운드 i (i = 0 ~ 19):
ri = passkey의 i번째 비트 (0 또는 1)
[Initiator] [Responder]
Nai = random 128비트
Cai = f4(PK_a, PK_b, Nai, ri)
──── Cai ──────→
Nbi = random 128비트
Cbi = f4(PK_b, PK_a, Nbi, ri)
←──── Cbi ──────
──── Nai ──────→
←──── Nbi ──────
Cai 검증 Cbi 검증
(다음 라운드로)
Legacy Passkey와의 차이: Legacy는 passkey 전체를 한 번에 TK로 사용하므로, 20비트 브루트포스(~100만 경우)로 해독할 수 있다. LESC는 각 비트를 독립적인 커밋먼트로 검증하므로, i번째 라운드에서 공격자가 ri를 맞추려면 확률 1/2이고, 20라운드 전부를 맞출 확률은 1/2^20 ≈ 1/100만이다. 하지만 핵심은 각 라운드에서 틀리면 즉시 실패한다는 점이다. 공격자가 중간에서 passkey를 하나씩 추측하면 한 비트라도 틀리는 순간 페어링이 중단된다.
OOB (Out of Band)
NFC 등 BLE 외부 채널로 인증 데이터를 교환한다.
[디바이스 A] [NFC 터치] [디바이스 B]
────── OOB Data (Confirm + Random) ──────→
←────── OOB Data (Confirm + Random) ──────
이후 BLE로 ECDH + DHKey check 진행
OOB 데이터에는 f4로 계산한 Confirm과 Random이 들어간다. NFC는 물리적 근접(~4cm)이 필요하므로 원격 MITM이 사실상 불가능하다.
펌웨어 설정
LESC 강제 활성화
/* STM32WB */
aci_gap_set_authentication_requirement(
1, /* bonding */
1, /* MITM protection required */
1, /* SC support: 1=지원, 2=SC only (Legacy 거부) */
0, /* keypress notification */
16, 16,/* enc key size min/max */
0, /* use_fixed_pin: 0=동적 생성 */
0, /* fixed_pin (미사용) */
0x00 /* identity address type */
);
/* nRF5 SDK (SoftDevice) */
ble_gap_sec_params_t sec_params = {
.bond = 1,
.mitm = 1,
.lesc = 1, /* LE Secure Connections 활성화 */
.io_caps = BLE_GAP_IO_CAPS_DISPLAY_YESNO,
.min_key_size = 16,
.max_key_size = 16,
};
/* ESP-IDF */
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE,
&auth_req, sizeof(auth_req));
esp_ble_io_cap_t io_cap = ESP_IO_CAP_IO; /* DisplayYesNo */
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE,
&io_cap, sizeof(io_cap));
ECDH 키 쌍 관리
대부분의 BLE 스택은 ECDH 키 쌍을 자동 생성한다. 주의할 점:
/* nRF5 SDK — ECDH 키 쌍을 매 페어링마다 새로 생성 (권장) */
/* SoftDevice가 자동 처리. 별도 코드 불필요 */
/* STM32WB — P-256 키 쌍 생성은 M0+에서 수행 */
/* aci_gap_set_authentication_requirement 호출 시 내부적으로 생성 */
같은 키 쌍을 재사용하면 안 되는 이유: 공격자가 한 번의 페어링에서 private key를 어떤 방식으로든 추출하면(사이드 채널 등), 이후 모든 페어링의 DHKey를 계산할 수 있다. 매 페어링마다 새 키 쌍을 생성하면 피해가 해당 세션으로 제한된다.
Numeric Comparison 이벤트 처리
/* STM32WB */
void aci_gap_numeric_comparison_value_event(
uint8_t conn_handle, uint32_t numeric_value)
{
/* numeric_value를 화면에 6자리로 표시 */
display_printf("%06lu", numeric_value);
/* 사용자가 버튼으로 확인/거부 */
/* 확인 시: */
aci_gap_numeric_comparison_value_confirm_yesno(conn_handle, 0x01);
/* 거부 시: */
aci_gap_numeric_comparison_value_confirm_yesno(conn_handle, 0x00);
}
/* nRF5 SDK */
case BLE_GAP_EVT_PASSKEY_DISPLAY:
/* p_evt->params.passkey_display.passkey — 6자리 문자열 */
/* p_evt->params.passkey_display.match_request — true면 NC */
if (p_evt->params.passkey_display.match_request) {
/* Numeric Comparison: 화면에 표시 후 사용자 확인 */
sd_ble_gap_auth_key_reply(conn_handle,
BLE_GAP_AUTH_KEY_TYPE_PASSKEY, NULL); /* 확인 */
}
break;
보안 레벨과 LESC의 관계
| Security Level | 요구사항 | LESC 관련 |
|---|---|---|
| Level 2 | 암호화, 인증 없음 | Just Works (Legacy or LESC) |
| Level 3 | 암호화 + MITM 방어 | Passkey/NC/OOB (Legacy or LESC) |
| Level 4 | Level 3 + LESC 필수 | Passkey/NC/OOB + LESC만 허용 |
Level 4를 요구하는 characteristic에 접근하면, LESC가 아닌 Legacy 페어링으로는 접근이 거부된다. Level 4는 BLE 4.2+에서만 가능하다.
메모
- sc_support = 1 vs 2
- 1: LESC를 지원하되, 상대가 LESC를 못하면 Legacy로 폴백. 호환성 우선
- 2: LESC만 허용. Legacy 폴백 차단. BLE 4.0/4.1 디바이스와 페어링 불가
- 보안이 중요하면 2를 쓴다. Legacy 폴백을 허용하면 공격자가 의도적으로 Legacy를 유도(downgrade attack)할 수 있다
- Debug Key 취약점
- Bluetooth Core Spec에 정의된 debug private key(고정값)를 쓰면 ECDH가 무력화된다. 개발용으로만 존재하며, 프로덕션에서 절대 사용하면 안 된다
- 일부 칩이 debug key 사용 여부를 HCI 이벤트로 알려준다. 프로덕션 빌드에서는 debug key로 페어링을 거부하는 로직을 넣는다
- P-256 연산 시간
- ECDH 키 쌍 생성 + DHKey 계산은 연산량이 크다. Cortex-M4에서 소프트웨어 구현 시 200~500ms
- 하드웨어 암호 가속기가 있는 칩(nRF52840 CryptoCell, STM32WB PKA)은 50ms 이하
- Passkey Entry의 20라운드 커밋먼트는 ECDH에 비하면 가벼운 AES 연산이므로 추가 지연은 미미
- Cross-Transport Key Derivation (CTKD)
- BLE 4.2+에서 BR/EDR 링크의 키를 LE로 파생하거나 그 반대가 가능하다
- BLURtooth 취약점(CVE-2020-15802): CTKD를 악용해 인증을 우회할 수 있다. 불필요하면 CTKD를 비활성화한다
- Passkey Entry 방향
- Initiator가 표시 + Responder가 입력, 또는 그 반대, 또는 양쪽 모두 입력 가능
- 양쪽 모두 같은 passkey를 입력하는 방식은 MITM 방어가 약해진다(공격자가 양쪽에 같은 숫자를 중계 가능). 한쪽이 표시하고 다른 쪽이 입력하는 방식이 더 안전하다