P-256 타원 곡선

IoTBLEProtocol

P-256(secp256r1)은 NIST가 표준화한 타원 곡선이다. BLE LE Secure Connections의 ECDH 키 교환, TLS 핸드셰이크, 코드 서명 등 임베디드에서 만나는 거의 모든 공개키 암호가 이 곡선 위에서 동작한다. RSA와 같은 보안 강도를 훨씬 작은 키로 달성하므로, 메모리와 대역폭이 제한된 임베디드 환경에 적합하다.


왜 타원 곡선인가

AES 같은 대칭키 암호는 양쪽이 같은 키를 미리 공유해야 한다. 처음 만나는 두 디바이스가 공개 채널에서 키를 합의하려면 비대칭(공개키) 암호가 필요하다. 비대칭 암호의 대표주자는 RSA인데, 같은 보안 수준을 맞추려면 키가 매우 크다.

방식128비트 보안에 필요한 키 크기공개키 크기
AES128비트— (대칭키)
RSA3,072비트384바이트
ECC (P-256)256비트64바이트

RSA 공개키 384바이트는 BLE 패킷 여러 개를 써야 전송할 수 있다. P-256 공개키 64바이트는 패킷 하나면 된다. 이 크기 차이가 BLE, IoT, 임베디드에서 ECC를 쓰는 이유다.


타원 곡선의 핵심 아이디어

”쉽게 계산하지만 되돌릴 수 없는” 연산

암호의 핵심은 한 방향으로는 쉽고, 반대 방향으로는 불가능한 수학 문제를 찾는 것이다.

  • RSA는 소인수 분해를 이용한다: 두 큰 소수를 곱하는 건 쉽지만, 결과만 보고 원래 소수를 알아내는 건 어렵다
  • ECC는 타원 곡선 위의 점 곱셈을 이용한다: 점을 k번 더하는 건 쉽지만, 결과 점만 보고 k를 알아내는 건 어렵다

점 덧셈이란

타원 곡선은 y² = x³ + ax + b 형태의 방정식이다. 이 곡선 위의 두 점을 잇는 직선을 그으면, 곡선과 세 번째 교점이 생긴다. 그 교점을 x축으로 뒤집은 것이 “두 점의 합”이다.

        *  P
       / \
      /   \
  ───*─────*───── 곡선
    R'      Q


     R = P + Q   (R'를 x축 대칭)

같은 점을 자기 자신에 더하면(접선을 그으면) point doubling이 된다. 이걸 반복하면 k번 더한 것, 즉 스칼라 곱셈이다:

k × G = G + G + G + ... + G  (k번)

G는 곡선 위의 고정된 기준점(Generator)이다.

비밀키와 공개키

비밀키: k  (256비트 랜덤 정수)
공개키: Q = k × G  (곡선 위의 점)

k × G를 계산하는 건 빠르다. k가 256비트라도, double-and-add 알고리즘으로 256번 정도의 점 연산으로 끝난다.

Q만 보고 k를 역산하는 건 불가능하다. 이것이 ECDLP(Elliptic Curve Discrete Logarithm Problem)이고, P-256에서는 약 2^128번의 연산이 필요하다. 현존하는 어떤 컴퓨터로도 불가능한 수준이다.

쉬움:  비밀키 k  →  공개키 Q = k × G     (256번 연산)
어려움: 공개키 Q  →  비밀키 k = ?         (~2¹²⁸번 연산)

ECDH — 키 교환

처음 만나는 두 디바이스가 도청자가 있는 공개 채널에서 공유 비밀을 만드는 방법이다.

[Alice]                              [Bob]
a = random (비밀키)                  b = random (비밀키)
A = a × G  (공개키)                  B = b × G  (공개키)

        ──── A ────→                 (공개키만 교환)
        ←──── B ────

S = a × B                           S = b × A
  = a × (b × G)                       = b × (a × G)
  = ab × G                            = ab × G
  (같은 값!)                           (같은 값!)

Alice는 자기 비밀키 a로 Bob의 공개키 B를 곱한다. Bob은 자기 비밀키 b로 Alice의 공개키 A를 곱한다. 교환 법칙(ab = ba) 덕분에 결과가 같다.

도청자는 A, B, G를 볼 수 있지만, a와 b를 모르므로 S를 계산할 수 없다. A에서 a를 역산하는 게 ECDLP이고, 그건 불가능하다.

BLE LESC에서는 이 S가 DHKey이고, DHKey에서 f5 함수로 LTK(암호화 키)를 파생한다.


ECDSA — 전자 서명

“이 메시지가 비밀키 소유자에 의해 작성되었고, 변조되지 않았다”를 검증하는 방법이다. 펌웨어 OTA 서명 검증, TLS 인증서 등에 사용된다.

서명 생성 (비밀키 d 필요)

1. 메시지의 해시를 구한다:  e = SHA-256(message)
2. 랜덤 nonce k를 생성한다
3. k × G를 계산하고, 그 x좌표를 r로 쓴다
4. s = k⁻¹ × (e + r × d)  를 계산한다
5. 서명 = (r, s)  → 총 64바이트

서명 검증 (공개키 Q만 필요)

1. 메시지의 해시를 구한다:  e = SHA-256(message)
2. 서명의 (r, s)와 공개키 Q로 곡선 위의 점을 복원한다
3. 복원한 점의 x좌표가 r과 같으면 → 서명 유효

비밀키 없이는 유효한 (r, s) 쌍을 만들 수 없다. 메시지가 1비트라도 바뀌면 해시가 달라지므로 서명이 무효화된다.

서명 크기 64바이트. RSA-3072 서명은 384바이트. BLE처럼 대역폭이 좁은 환경에서 큰 차이다.


임베디드에서의 연산 성능

P-256의 점 곱셈은 내부적으로 수백만 번의 큰 수 곱셈을 수행한다. MCU에서의 성능:

소프트웨어 구현

MCU클럭ECDHECDSA 서명ECDSA 검증
Cortex-M0 (nRF51)16MHz~4s~4s~5s
Cortex-M4 (STM32F4)168MHz~200ms~200ms~250ms
Cortex-M4 (nRF52832)64MHz~500ms~500ms~600ms
Cortex-M33 (nRF5340)128MHz~150ms~150ms~200ms

최적화된 어셈블리(P256-Cortex-M4 등)를 쓰면 C 구현 대비 2~5배 빠르다.

하드웨어 가속기

가속기ECDH 시간
nRF52840CryptoCell 310~100ms
nRF5340CryptoCell 312~30ms
STM32WB55PKA~50ms
ESP32없음 (SW)~300ms
ESP32-S3SHA만 가속~200ms

BLE LESC 페어링에서 ECDH는 한 번만 수행되므로, 소프트웨어 구현으로도 사용자 체감 지연은 크지 않다. OTA 서명 검증처럼 반복 호출되는 경우에 하드웨어 가속기의 효과가 크다.


구현 시 주의할 것

타이밍 공격

점 곱셈에서 비밀키의 각 비트를 처리할 때, 비트가 1이면 추가 연산을 하고 0이면 건너뛰는 구현이 있다. 이러면 실행 시간을 측정해서 비밀키의 각 비트를 추론할 수 있다.

/* 취약: 비트가 1일 때만 point_add 실행 → 시간 차이 발생 */
for (int i = 255; i >= 0; i--) {
    R = point_double(R);
    if (k & (1 << i))
        R = point_add(R, G);   /* 이 줄의 실행 여부가 시간에 드러남 */
}

대응: 비트 값과 무관하게 항상 같은 수의 연산을 수행하는 constant-time 구현(Montgomery ladder 등)을 사용한다. 검증된 암호 라이브러리(mbedTLS, micro-ecc)를 쓰면 이 처리가 되어 있다.

ECDSA nonce 재사용

ECDSA 서명에서 랜덤 값 k(nonce)를 같은 값으로 두 번 쓰면, 두 서명을 비교해서 비밀키를 수학적으로 역산할 수 있다. 한 번이라도 재사용되면 즉시 끝이다.

대응: RFC 6979는 메시지와 비밀키로부터 k를 결정적으로 계산한다. 랜덤 소스가 필요 없고, 같은 메시지에 대해 항상 같은 서명이 나오므로 재사용 문제가 원천적으로 없다. mbedTLS, micro-ecc 모두 기본 지원한다.


P-256 vs Curve25519

P-256Curve25519 / Ed25519
표준NISTIETF
보안 강도128비트128비트
사이드 채널 내성구현에 의존설계 자체가 constant-time
성능 (Cortex-M4)~200ms~150ms
BLE LESC사용 (스펙 고정)미사용
TLS 1.3지원지원 (X25519)

Curve25519는 구현 실수로 인한 사이드 채널 취약점이 발생하기 어렵게 설계되었고, 성능도 약간 더 빠르다. 자체 프로토콜을 설계할 때는 Curve25519/Ed25519를 우선 고려한다.

단, BLE는 Core Spec에서 P-256을 고정하고 있으므로 선택의 여지가 없다.


펌웨어에서 P-256 사용

BLE LESC — 스택이 알아서 처리한다

BLE 스택(SoftDevice, STM32WB M0+, NimBLE 등)이 내부적으로 P-256 ECDH를 수행한다. 앱 코드에서 직접 호출할 일은 없다.

/* nRF5 SDK */
sec_params.lesc = 1;  /* 이것만 켜면 ECDH는 SoftDevice가 처리 */

/* STM32WB */
aci_gap_set_authentication_requirement(..., 1 /* sc_support */, ...);

OTA 서명 검증 — 직접 호출

펌웨어 이미지 서명을 검증할 때 P-256 ECDSA를 직접 사용한다.

#include "uECC.h"

const uint8_t public_key[64] = { /* 제조 시 주입 */ };
const uint8_t signature[64] = { /* OTA 이미지에 첨부 */ };
uint8_t hash[32];

sha256_compute(firmware_data, firmware_size, hash);

if (uECC_verify(public_key, hash, sizeof(hash),
                signature, uECC_secp256r1())) {
    /* 유효 — 펌웨어 적용 */
} else {
    /* 위조 — 거부 */
}

키 저장

저장소보안 수준용도
일반 Flash낮음 (디버거로 읽기 가능)공개키 저장, 개발용 비밀키
Flash + Read Protection중간양산 기본
Secure Element (SE)높음 (키 추출 불가)ATECC608, SE050
ARM TrustZone중~높음nRF5340, STM32L5

서명 검증은 공개키만 필요하므로 일반 Flash에 넣어도 된다. ECDH나 서명 생성은 비밀키가 필요하므로 SE나 TrustZone을 사용한다.


메모

  • P-256의 “P”는 Prime: 소수체 위의 곡선이라는 뜻. secp256r1의 r은 random seed로 곡선 파라미터를 생성했다는 의미. 이 seed의 출처가 불명확하다는 점이 보안 커뮤니티에서 논란이 되기도 한다
  • 비압축 vs 압축 공개키: 비압축은 0x04 || x || y = 65바이트. 압축은 0x02/0x03 || x = 33바이트. BLE LESC는 비압축 공개키를 교환한다
  • 키 쌍 생성에 좋은 난수가 필수: ECDH 비밀키, ECDSA nonce 모두 예측 불가능한 난수여야 한다. MCU의 TRNG(True Random Number Generator)를 사용한다. 소프트웨어 PRNG만으로는 부족하다
  • 곡선 파라미터의 의미: y² = x³ - 3x + b (mod p)에서 p는 256비트 소수이고, 이 소수의 특수한 형태(2²⁵⁶ - 2²²⁴ + 2¹⁹² + 2⁹⁶ - 1)가 모듈러 연산을 빠르게 할 수 있도록 설계된 것이다