키
Label 309의 키 모델 — 32바이트 시드 하나, 도메인 분리된 HKDF-SHA-256으로 그로부터 파생되는 세 가지 알고리즘 키 쌍, 봉인된 PoE가 그 위에서 파생하는 슬롯별 키 암호화 키, 그리고 수신자 공개 키와 비밀 값을 인코딩하는 방법을 규정합니다.
Label 309에는 세 종류의 비대칭 키가 필요합니다. 레코드에 서명하는 Ed25519 키, 고전적 봉인 페이로드를 받는 X25519 키, 그리고 포스트 양자 봉인 페이로드를 받는 X-Wing(mlkem768x25519) 하이브리드 키입니다. 이 표준은 이들을 따로 보관하고 다뤄야 하는 세 개의 독립된 비밀로 취급하지 않습니다. 이 표준이 정의하는 것은 단 하나의 비밀 — 32바이트 시드 — 과, 그것을 세 가지 키 쌍 전부로 확장하는 결정론적 규칙입니다.
이 페이지는 그 파생을 규정합니다. 시드 그 자체, 각 알고리즘의 개인 키를 산출하는 세 가지 도메인 분리된 HKDF 확장, 도메인을 분리해 두는 이유, 봉인된 PoE가 그 위에서 파생하는 슬롯별 키 암호화 키, 그리고 그렇게 얻은 수신자 공개 키와 비밀 값을 교환용으로 인코딩하는 방법입니다. 시드를 이 외에 어떻게 다루는지 — 어디에 보관하고, 어떻게 잠금을 해제하며, 한 사람이 여러 개를 보유하는지 — 는 범위 밖입니다. Label 309가 관심을 두는 것은 오직 한 가지, 같은 32바이트가 주어지면 적합한 모든 구현이 동일한 키를 파생한다는 점뿐입니다.
시드
Label 309 키 집합은 단일한 값을 뿌리로 삼습니다.
| 속성 | 값 |
|---|---|
| 길이 | 32바이트(256비트) |
| 출처 | 암호학적으로 안전한 RNG, 또는 사용자가 소유한 임의의 32바이트 값 |
| 역할 | 아래 세 가지 HKDF 확장에 대한 입력 키 자재(IKM) |
시드는 어느 한 알고리즘 의미에서의 키가 아니라 순수한 엔트로피 원천입니다. 어떤 곡선도, 어떤 프리미티브에 묶인 길이도, 인코딩 절차도 지니지 않습니다. 구현이 실제로 사용하는 키는 파생할 때마다 정해지며, 시드는 그 위에서 이루어진 알고리즘 선택보다 오래 살아남습니다. 생성자는 플랫폼의 CSPRNG에서 시드를 새로 생성해도 되고(MAY), 기존의 32바이트 값을 가져와도 됩니다. 어느 쪽이든 그것은 정확히 32바이트로 디코딩되어야 합니다(MUST). 파생 계층에서는 어떠한 저엔트로피 패턴도 거부하지 않습니다. 전부 0인 시드도 유효한 입력이며, 바로 그 점이 전부 0인 시드를 재현 가능한 적합성 픽스처로 쓸 수 있게 합니다.
시드가 곧 전체 정체성입니다
Label 309가 어떤 당사자에 대해 표현하는 모든 공개 키 사실 — 레코드를 보증하는 키, 봉인 페이로드를 받는 키 — 은 이 32바이트의 결정론적 함수입니다. 시드를 재현하면 세 가지 키 쌍 전부를 바이트 단위로 그대로 재현할 수 있습니다.
백업용 시드 인코딩
32바이트 시드가 정체성 그 자체이므로, 이는 사용자가 백업하고, 내보내고, 가져오는 값입니다. 그런데 아무 장식 없는 32바이트 블롭은 모르는 사이에 잘려 나가거나 손상되기 쉽습니다. Label 309는 그것을 위해 체크섬이 포함된 문자열 인코딩을 정의하며, 시드를 입력으로 받는 모든 곳에서 원시 16진수와 나란히 이를 받아들입니다.
문자열 형식은 사람이 읽을 수 있는 접두사(HRP) l309-seed- 아래의 Bech32(BIP-173, 클래식, 단 90자 길이 상한은 해제)입니다. 끝의 하이픈은 HRP의 일부이므로, Bech32 구분자를 더하면 보이는 접두사는 l309-seed-1…이 됩니다. 인코딩은 대문자 표시 형식 L309-SEED-1… 을 반환합니다. 비밀 값은 한눈에 눈에 띄어야 하고, 대문자 표기는 소문자 age1… 수신자 문자열과 시각적으로 뚜렷이 구분되기 때문입니다. 모두 소문자인 형식도 같은 바이트열의 똑같이 유효한 인코딩입니다.
seed (32 bytes) 0000…0000 -> L309-SEED-1QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQLFUN82파서는 두 가지 표현을 받아들이고 그 형태에 따라 처리를 분기합니다.
- Bech32 문자열 — 단일 대소문자로(대소문자 혼용은 BIP-173에 따라 거부됨), 체크섬이 검증되고 디코딩된 페이로드가 정확히 32바이트일 것.
- 원시 16진수 — 64자리 16진수로, 대소문자를 가리지 않으며,
0x접두사와 앞뒤 또는 내부 공백을 허용함.
거부되는 입력은 저마다 별개의 구성 API 오류 코드에 대응되므로, 호출 측은 오타와 잘못된 키 종류를 구별할 수 있습니다.
| 입력 | 오류 코드 |
|---|---|
| 체크섬이 실패하는 Bech32 문자열(문자 한 개의 뒤바뀜, 잘림) | SEED_STRING_BAD_CHECKSUM |
| 대문자와 소문자가 섞인 Bech32 문자열 | SEED_STRING_MIXED_CASE |
다른 HRP 아래의 유효한 Bech32 문자열(예: age1… 수신자) | SEED_STRING_WRONG_HRP |
| 32바이트가 아닌 값으로 디코딩되는 Bech32 문자열 또는 16진수 문자열 | SEED_STRING_WRONG_LENGTH |
| 인식 가능한 Bech32 문자열도 16진수도 아닌 그 무엇(빈 문자열 포함) | SEED_STRING_UNRECOGNIZED |
이들 코드는 파생을 둘러싼 키 취급 편의 기능인 시드 문자열 코덱을 기술하는 것이며, 구조 검증기가 발행하는 와이어 오류 코드 레지스트리(검증)와는 별개입니다. 이 인코딩은 벌거벗은 32바이트만 실어 나르고 그 외에는 아무것도 — 버전도, 파생 매개변수도 — 실어 나르지 않습니다. 시드의 의미는 그것이 어떻게 전송되었는지가 아니라 아래 세 개의 info 문자열에 의해 고정되기 때문입니다.
세 가지 키 쌍의 파생
각 알고리즘의 개인 키는 RFC 5869에 따라 동일한 시드를 독립적으로 HKDF-SHA-256으로 확장한 것입니다. 세 가지 확장은 입력 키 자재와 (생략된) 솔트를 공유하며, 오직 하나의 매개변수 — 알고리즘을 지정하는 info 문자열 — 에서만 다릅니다.
| 알고리즘 | info 문자열 | 출력 |
|---|---|---|
| Ed25519 | cardano-poe-ed25519-v1 | 32바이트 Ed25519 비밀 시드 |
| X25519 | cardano-poe-x25519-v1 | 32바이트 X25519 비밀 시드 |
mlkem768x25519 | cardano-poe-mlkem768x25519-v1 | 32바이트 X-Wing 역캡슐화 키 시드 |
의사 코드로 표현한 파생은 다음과 같습니다.
ed25519_priv = HKDF-SHA-256(ikm = seed, salt = "", info = "cardano-poe-ed25519-v1", length = 32)
x25519_priv = HKDF-SHA-256(ikm = seed, salt = "", info = "cardano-poe-x25519-v1", length = 32)
mlkem768x25519_priv = HKDF-SHA-256(ikm = seed, salt = "", info = "cardano-poe-mlkem768x25519-v1", length = 32)이 출력들을 구현 간에 상호 운용 가능하게 만드는 규칙이 세 가지 있습니다.
- 솔트는 비어 있습니다. HKDF 솔트는 길이가 0인 바이트 문자열이어야 합니다(MUST). RFC 5869 §2.2에 따르면 솔트가 생략된 경우
HashLen개의 0 바이트 — SHA-256이면 32개의 0 바이트 — 로 취급되므로, 적합한 모든 라이브러리가 동일한 추출 단계에 도달합니다. - 출력은 32바이트입니다. 각 확장은 정확히 32바이트(SHA-256에 대해 단일 HKDF 블록)를 요청합니다.
info문자열은 정확한 ASCII입니다. 각info값은 표시된 바이트 그대로 정확히 인코딩되어야 합니다(MUST). 앞뒤 공백, 0 종결 문자, 바이트 순서 표시(BOM), 끝의 줄바꿈은 어느 것도 붙이지 않습니다. 세 문자열의 길이는 각각 22, 21, 29바이트입니다.
이 32바이트 출력은 확장된 곡선 스칼라가 아니라 해당 알고리즘의 비밀 시드입니다. RFC 8032 §5.1.5는 Ed25519에 대해 이 구분을 명시합니다. 비밀 시드는 32바이트이며, 서명 라이브러리가 이를 (SHA-512를 거친 뒤 클램핑하여) 실제 스칼라와 서명 접두사로 내부에서 확장합니다. X25519도 마찬가지로, 클램핑은 RFC 7748 §5에 따라 프리미티브 내부에서 적용됩니다. 구현은 원시 32바이트 HKDF 출력을 그대로 프리미티브에 넘기고 확장과 클램핑은 라이브러리에 맡겨야 합니다(MUST). 미리 클램핑하거나 미리 확장해서는 안 됩니다. X-Wing의 경우 32바이트 출력은 X-Wing 역캡슐화 키 시드이며, 이로부터 1216바이트 공개 키를 포함한 전체 키 쌍이 X-Wing 키 생성에 의해 결정론적으로 재생성됩니다. 어느 경우든 보관하고 전송할 정규 형식은, 확장된 키가 아니라 간결한 32바이트 시드입니다.
왜 하나가 아니라 세 개의 도메인인가
HKDF의 info 매개변수는 그 도메인 분리 태그입니다. 확장된 출력을 특정 애플리케이션 맥락에 결속하는 역할을 하며, RFC 5869 §3.1은 맥락을 사용할 수 있을 때 이를 제공할 것을 강력히 권고합니다. Label 309는 세 개의 개인 키가 마침 모두 32바이트 폭이라 하더라도, 하나의 확장을 세 가지에 걸쳐 재사용하지 않고 알고리즘마다 별개의 태그를 제공합니다. 이유는 격리에 있습니다.
- 장애가 국소에 머뭅니다. 두 키 쌍이 동일한 바이트열을 공유한다면, 어느 한 알고리즘 고유의 약점 — 논스 파생 결함, 스칼라 곱셈의 부채널 — 이 무관한 알고리즘의 키까지 노출시킬 수 있습니다. 도메인 분리는 세 개의 개인 키가 시드의 서로 독립적인 함수임을 보장하므로, 하나가 위태로워져도 공격자는 다른 키에 대해 아무것도 알아낼 수 없습니다.
- 마이그레이션이 가산적으로 이루어집니다. 각
info문자열은-v1로 끝납니다. 향후 개정에서 다른 곡선이나 다른 하이브리드를 채택할 경우, 같은 시드로부터 새로운 태그 아래 새로운-v2키를 파생하면 되며, 이미 배포된 v1 키와 충돌하지 않습니다. 이는 와이어 포맷 자체가 의지하는 알고리즘 민첩성과 맞닿아 있습니다.
세 번째 태그 cardano-poe-mlkem768x25519-v1은, 그 역캡슐화 키 시드가 고전적 X25519 비밀과 같은 32바이트 폭이라 하더라도, 포스트 양자 하이브리드에 자체적인 도메인을 부여합니다. 그래서 ML-KEM-768, X25519, 혹은 X-Wing 결합기에 결함이 있더라도 고전적 암호화 키나 서명 키로 교차 오염되는 일이 없습니다.
이 정체성 키 태그 cardano-poe-mlkem768x25519-v1에는 -kek- 구획도 들어 있지 않습니다. 이는 아래의 레코드별 KEK 파생 라벨 cardano-poe-kek-mlkem768x25519-v1과는 별개이며, 그래서 시드 → 정체성 키 확장과 봉인된 PoE의 슬롯별 키 래핑이 info 문자열을 공유하는 일이 결코 없습니다.
슬롯별 키 암호화 키
위의 세 가지 시드 파생 키 쌍은 오래 유지되는 정체성 키입니다. 봉인된 PoE는 그 위에 레코드별 HKDF-SHA-256 계층을 하나 더 얹습니다. 각 수신자 슬롯마다 송신자는 새로운 32바이트 키 암호화 키(KEK)를 파생하여, 그것으로 레코드의 콘텐츠 암호화 키를 래핑합니다. KEK 파생은 키 모델의 일부이므로 여기서 규정합니다. 래핑된 키가 그 뒤 봉투에 어떻게 실리는지는 봉인된 PoE에서 다룹니다.
두 KEM 모두 KEK를 HKDF-SHA-256과 KEM별 info로, 라벨이 붙은 해시 솔트 아래에서 파생합니다. 이 솔트는 세 가지 값을 결속합니다. 슬롯 자신의 KEM 자재(KEK를 슬롯별로 고유하게 만듦), 수신자 공개 키 pub_R(어느 수신자를 겨냥해 만든 캡슐화가 다른 수신자에게 중계되지 못하게 함), 그리고 봉투마다 고유한 enc.nonce(KEK를 하나의 봉투에 고정함)입니다. 공유 비밀은 KEM 자신의 ECDH(고전) 또는 X-Wing 역캡슐화(하이브리드) 출력으로, 송신자가 캡슐화하든 수신자가 역캡슐화하든 동일한 32바이트 값이며, salt와 info는 양쪽에서 동일합니다.
; x25519 (classical) — salt is a labelled SHA-256 over the ephemeral and recipient keys
kek_salt = SHA-256("cardano-poe-x25519-kek-salt-v1" || enc.nonce || pub_epk || pub_R) ; 32 bytes
KEK = HKDF-SHA-256(ikm = shared, ; the X25519 ECDH shared secret
salt = kek_salt,
info = "cardano-poe-kek-v1",
L = 32)
; mlkem768x25519 (hybrid) — same labelled-salt shape under the hybrid's own label
kek_salt = SHA-256("cardano-poe-xwing-kek-salt-v1" || enc.nonce || kem_ct || pub_R) ; 32 bytes
KEK = HKDF-SHA-256(ikm = shared, ; the X-Wing shared secret
salt = kek_salt,
info = "cardano-poe-kek-mlkem768x25519-v1",
L = 32)두 솔트는 같은 형태 — SHA-256(label || enc.nonce || <slot KEM material> || pub_R) — 를 가지며, KEM별 라벨과 어떤 KEM 자재를 싣는지에서만 다릅니다. 즉 고전 경로에서는 32바이트 임시 키 pub_epk, 하이브리드 경로에서는 1120바이트 X-Wing 암호문 kem_ct입니다. 둘 다 고정 길이 SHA-256 다이제스트로 접어 넣어집니다. 하이브리드 입력은 원시 솔트로 쓰기에는 너무 크고, 하나의 통일된 형태가 두 경로의 보조를 맞추기 때문입니다. 이 결속은 KEM 바깥에서, 슬롯 자신의 와이어 바이트열에 대해 계산되므로, X-Wing을 블랙박스 KEM으로 다루며 결합기 내부 해싱의 어떠한 성질에도 의지하지 않습니다. 또한 KEM별로 다른 info 라벨은, 동일한 32바이트 공유 비밀 아래에서도 한 KEM으로 파생한 KEK가 다른 KEM으로 파생한 KEK와 같아지는 일이 결코 없음을 추가로 보장합니다.
두 솔트 모두에서 pub_R는 수신자 키의 정규 와이어 인코딩입니다. x25519에서는 정확히 32바이트 X25519 공개 키, mlkem768x25519에서는 정확히 고정된 1216바이트 X-Wing 공개 키 바이트열입니다. 생성자와 수신자는 바로 그 인코딩을 사용해야 하며(MUST), 비정규이거나 재인코딩된 등가물로 대체해서는 안 됩니다(MUST NOT). 그렇지 않으면 양쪽이 서로 다른 솔트를 HKDF에 넣어 서로 다른 KEK를 파생하게 되고, 슬롯은 결코 열리지 않습니다.
각 KEK와 그 솔트 접두사는 enc.scheme: 1의 내부 구성 요소입니다. 와이어 식별자를 지니지 않으며 선택할 수도 없습니다. 여기서 든 두 개의 솔트 접두사 라벨과 두 개의 info 라벨은 알고리즘 레지스트리에 목록화된 봉인 구성의 열한 개 라벨 리터럴 가운데 네 개입니다. 검증자는 각각을 바이트 단위로 그대로 사용해야 합니다(MUST).
수신자 공개 키 인코딩
봉인된 PoE 송신자는 수신자의 공개 키를 휴대 가능한 문자열 형식으로 필요로 하고, 수신자는 그에 대응하는 형식으로 자신의 비밀 값을 백업합니다. Label 309는 age 생태계의 Bech32 수신자 인코딩을 재사용하며, 등록된 키 캡슐화 메커니즘마다 사람이 읽을 수 있는 접두사(HRP)를 하나씩 둡니다.
Bech32에서 1은 HRP와 데이터 부분 사이의 구분자이므로, 문자열에서 사람이 보는 접두사는 그 HRP 에다 그 1을 더한 것입니다. 따라서 HRP와 보이는 접두사는 별개이며, 표에서는 이들을 별도의 열에 둡니다.
KEM(enc.kem) | 공개 키 | 공개 키 HRP | 공개 키의 보이는 접두사 | 비밀 HRP | 비밀의 보이는 접두사 |
|---|---|---|---|---|---|
x25519 | 32바이트 X25519 공개 키 | age | age1…(62자) | AGE-SECRET-KEY- | AGE-SECRET-KEY-1… |
mlkem768x25519 | 1216바이트 X-Wing 공개 키 | age1pqc | age1pqc1…(1960자) | AGE-SECRET-KEY-PQ- | AGE-SECRET-KEY-PQ-1… |
고전적 x25519 수신자 문자열은 HRP가 age이고 표준 age v1 형식 age1…입니다. 하이브리드 공개 키는 ML-KEM-768 캡슐화 키(1184바이트)와 X25519 공개 키(32바이트)를 이어 붙인 것이며, 1216바이트이므로 그 age1pqc1… 수신자 문자열은 1960자입니다.
구현이 백업하고 가져오는 비밀은, 두 경로 모두에서 32바이트 시드입니다. X25519 비밀 시드는 AGE-SECRET-KEY- 아래에, X-Wing 역캡슐화 키 시드(위의 세 번째 HKDF 출력, info = "cardano-poe-mlkem768x25519-v1")는 AGE-SECRET-KEY-PQ- 아래에 둡니다. 1216바이트 하이브리드 공개 키는 그 시드로부터 파생됩니다. 보관할 정규 비밀은, 확장된 키가 아니라 간결한 시드입니다.
BIP-173은 Bech32 문자열을 90자로 제한하지만, 그 상한은 사람이 손으로 입력하는 결제 주소를 위한 것이며 여기에는 적용되지 않습니다. 구현은 Bech32 체크섬과 문자 집합 규칙은 계속 적용하면서, 90자 제한을 강제하지 않고 age1pqc1… 문자열을 인코딩하고 디코딩해야 합니다(MUST). 별개의 HRP age1pqc는 하이브리드 수신자가 어떤 고전적 age 수신자와도 충돌하지 않게 합니다 — 그리고 의도적으로 age1pq를 쓰지 않습니다. age1pq는 같은 프리미티브에 대해 상류의 네이티브 ML-KEM-768 + X25519 인코딩이 이미 차지한 더 짧은 접두사이며, 그렇게 함으로써 두 수신자 인코딩이 와이어 상에서 결코 충돌하지 않게 합니다. 고전적 인코딩은 통상의 길이 안에 머물며 변경 없이 처리됩니다.
이 문자열들은 오로지 수신자를 찾기 위한 편의 수단입니다. 수신자 공개 키가 Label 309 레코드의 암호화 봉투에 그대로 나타나는 일은 결코 없습니다. enc.slots[] 항목 하나가 슬롯별 키 자재와 wrap 값을 싣고, KEM 식별자는 enc.kem에 한 번 나타납니다. 봉투와 슬롯을 어떻게 구성하는지는 봉인된 PoE에서 다룹니다.
서명 kid로서의 Ed25519 공개 키
Ed25519 공개 키는 수신자 역할을 전혀 하지 않습니다. 이는 검증자가 서명을 대조하여 풀어내는 키 식별자입니다. 생성자가 레코드에 서명하면, 원시 32바이트 Ed25519 공개 키가 RFC 9052에 따라 COSE_Sign1 보호 헤더 안의 kid(라벨 4)가 됩니다. 검증자는 이 32바이트 값을 온체인 서명에서 곧바로 읽어, 그에 대해 레코드 본문을 검사합니다. 공개 키가 서명과 함께 이동하므로, 저작성을 검증하는 데 별도의 조회가 필요하지 않습니다. 서명 구성 전체, 서명 대상 페이로드, 그리고 검증 규칙은 서명에서 규정합니다.
대역 외 키 교환
Label 309가 규정하는 것은 수신자 공개 키를 어떻게 인코딩하는가이지, 어떻게 발견하는가가 아닙니다. 이 표준은 수신자 키를 위한 디렉터리도, 레지스트리도, 온체인 공지 형식도 일절 규정하지 않습니다. 봉인 페이로드를 받고자 하는 당사자는 양측이 이미 신뢰하는 어떤 채널을 통해 자신의 age1… 또는 age1pqc1… 문자열을 게시합니다 — 대면 전달, 자신의 Ed25519 키로 서명한 레코드, 안정적인 웹 위치나 콘텐츠 주소 지정 위치에 둔 레코드 등입니다 — 그리고 암호화 대상으로 삼는 어떤 키든 그 출처에 대한 책임은 송신자에게 있습니다.
이는 의도적으로 그은 경계입니다. 서버를 신뢰하지 않고도 레코드를 검증할 수 있게 하는 바로 그 성질은, 뒤집어 말하면 키 교환이 신뢰받는 중개자를 슬그머니 다시 끌어들여서는 안 된다는 뜻입니다. 키 옆에 놓인 이름은 그것을 놓은 자의 증언일 뿐, 결코 암호학적 주장이 아닙니다. 같은 핸들을 쓰는 두 당사자라도 바이트가 다른 키를 만들어 내며, 검증자가 비교하는 것은 그 바이트입니다. 사람이 읽을 수 있는 이름을 키에 대응시키는 일은 Label 309 위에 구축된 애플리케이션이 제공해도 되는(MAY) 기능이지만, 그것은 애플리케이션 기능이며 프로토콜 밖에 있습니다.
관련 페이지
- 서명 — Ed25519 키가 레코드에 어떻게 서명하고
kid가 어떻게 검증되는지. - 봉인된 PoE — X25519와 X-Wing 공개 키가 암호화된 페이로드를 특정 수신자에게 어떻게 주소 지정하는지.
- 알고리즘 레지스트리 — 여기서 참조한 서명, KEM, AEAD, KDF의 명명된 식별자.