Skip to content

Commit afbf5be

Browse files
committed
Start updating spec
1 parent 57fcd23 commit afbf5be

File tree

2 files changed

+98
-64
lines changed

2 files changed

+98
-64
lines changed

draft-dijkhuis-cfrg-hdkeys.md

+98-63
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ normative:
3939
author:
4040
- organization: ISO/IEC
4141
date: 2019-09
42-
RFC7800:
4342
RFC8017:
44-
RFC8235:
4543
RFC9180:
4644
RFC9380:
4745
SEC2:
@@ -94,6 +92,9 @@ informative:
9492
(EU): 2024/1183
9593
date: 2024-04
9694
I-D.draft-bradleylundberg-cfrg-arkg-02:
95+
I-D.draft-irtf-cfrg-signature-key-blinding-07:
96+
RFC7800:
97+
RFC8235:
9798
Verheul2024:
9899
title: Attestation Proof of Association – provability that attestation keys are bound to the same hardware and person
99100
target: https://eprint.iacr.org/2024/1444
@@ -145,9 +146,9 @@ Solutions MAY omit application of the remote functionality. In this case, a unit
145146
The following example illustrates the use of local key derivation. An HDK tree is associated with a device key pair and initiated using confidential static data: a `seed` value, which is a byte array containing sufficient entropy. Now tree nodes are constructed as follows.
146147

147148
~~~
148-
+----+
149-
Confidential static data: |seed|
150-
+-+--+
149+
+----+ +--+
150+
Confidential static data: |seed| |pk|
151+
+-+--+ +--+
151152
v
152153
+----+ +----+
153154
Level 0 HDKeys: |hdk0| |hdk1|
@@ -162,15 +163,15 @@ Level 2 HDKeys at hdk01: |hdk000| |hdk001| ...
162163
+------+ +------+
163164
~~~
164165

165-
The unit computes a Level 0 HDK at the root node using a deterministic function: `(bf0, salt0) = hdk0 = HDK(seed, 0)`. The HDK consists of a first blinding factor `bf0` and a first byte string `salt0` to derive next-level keys. Using `bf0` and the device key pair, the unit can compute blinded public and private keys and proofs of possession.
166+
The unit computes a Level 0 HDK at the root node using a deterministic function, taking the device public key `pk` and the `seed` as input: `(pk0, salt0, bf0) = hdk0 = HDK(0, pk, seed)`. The HDK consists of a first blinded public key `pk0`, a first byte string `salt0` to derive next-level keys, and a first blinding factor `bf0`. Using `bf0` and the device key pair, the unit can compute blinded private keys and proofs of possession.
166167

167-
The unit computes any Level `n > 0` HDK from any other HDK `(bf, salt)` using the same deterministic function: `(bf', salt') = hdk' = HDK(salt, index)`. The function takes the previous-level `salt` as input, and an `index` starting at 0. The function returns a new HDK as output, which can be used in the same way as the root HDK.
168+
The unit computes any Level `n > 0` HDK from any other HDK `(pk, salt, bf)` using the same deterministic function: `(pk', salt', bf') = hdk' = HDK(index, pk, salt, bf)`. The function takes as input the `index` starting at 0, an the previous-level HDK. The function returns a new HDK as output, which can be used in the same way as the root HDK.
168169

169170
### Remote deterministic key derivation
170171

171172
Instead of local derivation, an HDK salt can also be derived using a key handle that is generated remotely. Using the derived salt, the local and remote parties can derive the same new HDKeys. The remote party can use these to derive public keys. The local party can use these to derive associated private keys for proof of possession.
172173

173-
This approach is similar to Asynchronous Remote Key Generation (ARKG) [I-D.draft-bradleylundberg-cfrg-arkg-02], but not the same since ARKG does not enable distributed proof of possession with deterministic hierarchies. This makes it difficult to implement with cryptographic devices that lack specific firmware support.
174+
This approach is similar to Asynchronous Remote Key Generation (ARKG) [I-D.draft-bradleylundberg-cfrg-arkg-02], but not the same since ARKG does not enable distributed proof of possession with deterministic hierarchies. These hierarchies can be used for example to enable remote parties to derive keys from previously derived keys. Not supporting these makes it difficult to implement with cryptographic devices that lack specific firmware support.
174175

175176
To enable remote derivation of child HDKeys, the unit uses the parent HDKey to derive the parent public key and a second public key for key encapsulation. The issuer returns a key handle, using which both parties can derive a sequence of child HDKeys. Key encapsulation prevents other parties from discovering a link between the public keys of the parent and the children, even if the other party knows the parent HDK or can eavesdrop communications.
176177

@@ -180,7 +181,7 @@ Locally derived parents can have remotely derived children. Remotely derived par
180181

181182
The next concept to illustrate is blinded proof of possession. This enables a unit to prove possession of a (device) private key without disclosing the directly associated public key. This way, solutions can avoid linkability across readers of a digital document that is released with proof of possession.
182183

183-
In this example, a document is issued with binding to a public key `pk'`, which is a blinding public key `pk` blinded with the blinding factor `bf` in some HDK `hdk = (bf, salt)`. The unit can present the document with a proof of possession of the corresponding blinded private key, which is the blinding private key `sk` blinded with `bf`. The unit applies some authentication function `device_data = authenticate(sk, reader_data, bf)` to the blinding private key, reader-provided data and the blinding factor. The unit can subsequently use the output `device_data` to prove possession to the reader using common algorithms.
184+
In this example, a document is issued with binding to a public key `pk'`, which is a blinding public key `pk` blinded with the blinding factor `bf` in some HDK `hdk = (pk', salt, bf)`. The unit can present the document with a proof of possession of the corresponding blinded private key, which is the blinding private key `sk` blinded with `bf`. The unit applies some authentication function `device_data = authenticate(sk, reader_data, bf)` to the blinding private key, reader-provided data and the blinding factor. The unit can subsequently use the output `device_data` to prove possession to the reader using common algorithms.
184185

185186
~~~
186187
+------------------+ +--------+
@@ -225,45 +226,98 @@ The parameters of an HDK instantiation are:
225226
- `ID`: A domain separation tag, represented as a string of ASCII bytes.
226227
- `Ns`: The amount of bytes of a salt value with sufficient entropy.
227228
- `H`: A cryptographic hash function.
228-
- H1(msg): Outputs `Ns` bytes.
229-
- `BL`: An asymmetric key blinding scheme with opaque blinding factors and algebraic properties, consisting of the functions:
230-
- BL-Generate-Blinding-Key-Pair(): Outputs a blinding key pair `(pk, sk)`.
231-
- BL-Derive-Blinding-Factor(msg, ctx): Outputs a blinding factor `bf` based on two byte string inputs, message `msg` and domain separation parameter `ctx`.
232-
- BL-Blind-Public-Key(pk, bf): Outputs the result `pk'` of blinding `pk` with `bf`. This again is a blinding public key.
233-
- BL-Blind-Private-Key(sk, bf): Outputs the result `sk'` of blinding `sk` with `bf`. This again is a blinding private key.
234-
- BL-Combine-Blinding-Factors(bf1, bf2): Outputs a blinding factor `bf` such that for all blinding key pairs `(pk, sk)`:
235-
- `BL-Blind-Public-Key(pk, bf) == BL-Blind-Public-Key(BL-Blind-Public-Key(pk, bf1), bf2)`
229+
- H(msg): Outputs `Ns` bytes.
230+
- `D`: An digital signature algorithm or shared secret creation algorithm based on public key cryptography, consisting of the functions:
231+
- D-Generate-Key-Pair(): Outputs a key pair `(sk, pk)`.
232+
- `BL`: An asymmetric key blinding scheme for `D` as defined in [I-D.draft-irtf-cfrg-signature-key-blinding-07], with opaque blinding factors and algebraic properties, consisting of the functions:
233+
- BL-Derive-Blind-Key(ikm): Outputs a blind key `bk` based on input keying material `ikm`.
234+
- BL-Derive-Blinding-Factor(bk, ctx): Outputs a blinding factor `bf` based on a blind key `bk` and an application context byte string `ctx`.
235+
- BL-Blind-Public-Key(pk, bk, ctx): Outputs the result `pk'` of blinding public key `pk` with blind key `bk` and application context byte string `ctx`. This again is a public key in `D`.
236+
- BL-Blind-Private-Key(sk, bf): Outputs the result `sk'` of blinding private key `sk` with blinding factor `bf`. This result is a private key in `D`, such that if `bf = BL-Derive-Blinding-Factor(bk, ctx)` for some `bk` and `ctx`, the associated public key in `D` equals `BL-Blind-Public-Key(pk, bk, ctx)`.
237+
- BL-Combine(bf1, bf2): Outputs a blinding factor `bf` such that for all key pairs `(pk, sk)` in `D`:
236238
- `BL-Blind-Private-Key(pk, bf) == BL-Blind-Private-Key(BL-Blind-Private-Key(pk, bf1), bf2)`
237239
- `KEM`: A key encapsulation mechanism [RFC9180], consisting of the functions:
238240
- KEM-Derive-Key-Pair(ikm): Outputs a key encapsulation key pair `(sk, pk)`.
239241
- KEM-Encap(pk): Outputs `(k, c)` consisting of a shared secret `k` and a ciphertext `c`, taking key encapsulation public key `pk`.
240242
- KEM-Decap(c, sk): Outputs shared secret `k`, taking ciphertext `c` and key encapsulation private key `sk`.
241-
- `Authenticate(sk_device, reader_data, bf)`: Outputs `device_data` for use in a protocol for proof of possession, taking a BL blinding private key `sk_device`, remotely received `reader_data`, and a BL blinding factor `bf`.
242243

243244
An HDK instantiation MUST specify the instantiation of each of the above functions and values.
244245

245-
An HDK instantiation MUST define Authenticate such that the `device_data` can be verified using the blinded public key `pk = BL-Blind-Public-Key(sk, bf)`. The reader does not need to know that HDK was applied: the public key will look like any other public key used for proofs of possession.
246+
Note that by design of `BL`, when a document is issued using HDK, the reader does not need to know that HDK was applied: the public key will look like any other public key used for proofs of possession.
246247

247-
## The HDK function
248+
An HDK implementation MAY leave BL-Blind-Private-Key implicit in cases where the blinding method is constructed in a distributed way. In those cases, the secure cryptographic device holding the private key does not need to support key blinding, and the value of the blinded private key is never available during computation.
249+
250+
## The HDK context
251+
252+
A local unit or remote party creates an HDK context from an index.
253+
254+
~~~
255+
Inputs:
256+
- index, an integer between 0 and 2^32-1 (inclusive).
257+
258+
Outputs:
259+
- ctx, an application context byte string.
260+
261+
def HDK-Create-Context(index):
262+
ctx = ID || I2OSP(index, 4)
263+
return ctx
264+
~~~
248265

249-
A local unit or a remote party deterministically computes an HDK from a salt and an index. The salt can be an initial seed value of `Ns` bytes or it can be taken from another parent HDK. The secure generation of the seed is out of scope for this specification.
266+
## The HDK salt
267+
268+
A local unit or remote party derives a next-level HDK salt from within an HDK context.
250269

251270
~~~
252271
Inputs:
253272
- salt, a string of Ns bytes.
273+
- ctx, an HDK context byte string.
274+
275+
Outputs:
276+
- salt', the next salt for HDK derivation.
277+
278+
def HDK-Derive-Salt(salt, ctx):
279+
salt' = H(ID || salt || ctx)
280+
return salt'
281+
~~~
282+
283+
## The HDK function
284+
285+
A local unit or a remote party deterministically computes an HDK from an index, a parent public key, a salt, and an optional parent blinding factor. The salt can be an initial seed value of `Ns` bytes or it can be taken from another parent HDK. The secure generation of the seed is out of scope for this specification.
286+
287+
~~~
288+
Inputs:
254289
- index, an integer between 0 and 2^32-1 (inclusive).
290+
- pk, a public key to be blinded.
291+
- salt, a string of Ns bytes.
292+
- bf, a blinding factor to combine with, Nil otherwise.
255293

256294
Outputs:
257-
- bf, the blinding factor at the provided index.
295+
- pk', the blinded public key at the provided index.
258296
- salt', the salt for HDK derivation at the provided index.
297+
- bf', the blinding factor at the provided index.
259298

260-
def HDK(salt, index):
261-
msg = salt || I2OSP(index, 4)
262-
bf = BL-Derive-Blinding-Factor(msg, ID)
263-
salt' = H1(msg)
264-
return (bf, salt')
299+
def HDK(index, pk, salt, bf = Nil):
300+
ctx = HDK-Create-Context(index)
301+
salt' = HDK-Derive-Salt(salt, ctx)
302+
303+
bk = BL-Derive-Blind-Key(salt)
304+
pk' = BL-Blind-Public-Key(bk, ctx)
305+
bf' = if bf == Nil:
306+
BL-Derive-Blinding-Factor(bk, ctx)
307+
else:
308+
BL-Combine(bf, BL-Derive-Blinding-Factor(bk, ctx))
309+
310+
return (pk', salt', bf')
265311
~~~
266312

313+
A unit MUST NOT persist a blinded private key. Instead, if persistence is needed, a unit can persist either the blinding factor of each HDK, or a path consisting of the seed salt, indices and key handles. In both cases, the application of BL-Combine in the HDK function enables reconstruction of the blinding factor with respect to the original private key, enabling application of for example BL-Blind-Private-Key.
314+
315+
If the unit uses the blinded private key directly, the unit MUST use it within the secure cryptographic device protecting the device private key.
316+
317+
If the unit uses the blinded private key directly, the unit MUST ensure the secure cryptographic device deletes it securely from memory after usage.
318+
319+
When presenting multiple documents, a reader can require a proof that multiple keys are associated to a single device. Several protocols for a cryptographic proof of association are possible, such as [Verheul2024]. For example, a unit could prove in a zero-knowledge protocol knowledge of the association between two elliptic curve keys `B1 = [bf1]D` and `B2 = [bf2]D`, where `bf1` and `bf2` are multiplicative blinding factors for a common blinding public key `D`. In this protocol, the association is known by the discrete logarithm of `B2 = [bf2/bf1]B1` with respect to generator `B1`. The unit can apply BL-Combine-Blinding-Factors to obtain values to compute this association.
320+
267321
## The local HDK procedure
268322

269323
This is a procedure executed locally by a unit.
@@ -272,38 +326,36 @@ To begin, the unit securely generates a `seed` salt of `Ns` bytes and a device k
272326

273327
~~~
274328
seed = random(Ns) # specification of random out of scope
275-
(pk_device, sk_device) = BL-Generate-Blinding-Key-Pair()
329+
(sk_device, pk_device) = D-Generate-Key-Pair()
276330
~~~
277331

278332
The unit MUST generate `sk_device` within a secure cryptographic device.
279333

280334
Whenever the unit requires the HDK with some `index` at level 0, the unit computes:
281335

282336
~~~
283-
(bf, salt) = HDK(seed, index)
337+
(pk, salt, bf) = HDK(index, pk_device, seed)
284338

285-
pk = BL-Blind-Public-Key( pk_device, bf) # optional
286339
sk = BL-Blind-Private-Key(sk_device, bf) # optional
287340
~~~
288341

289-
Now the unit can use the blinded key pair `(pk, sk)` or derive child HDKeys.
342+
Now the unit can use the blinded key pair `(sk, pk)` or derive child HDKeys.
290343

291-
Whenever the unit requires the HDK with some `index` at level `n > 0` based on a parent HDK `hdk = (bf, salt)` with blinded key pair `(pk, sk)` at level `n`, the unit computes:
344+
Whenever the unit requires the HDK with some `index` at level `n > 0` based on a parent HDK `hdk = (pk, salt, bf)` with blinded key pair `(sk, pk)` at level `n`, the unit computes:
292345

293346
~~~
294-
(bf', salt') = HDK(salt, index)
347+
(pk', salt', bf') = HDK(index, pk, salt)
295348

296-
pk' = BL-Blind-Public-Key( pk, bf') # optional
297349
sk' = BL-Blind-Private-Key(sk, bf') # optional
298350
~~~
299351

300-
Now the unit can use the blinded key pair `(pk', sk')` or derive child HDKeys.
352+
Now the unit can use the blinded key pair `(sk', pk')` or derive child HDKeys.
301353

302354
## The remote HDK protocol
303355

304356
This is a protocol between a local unit and a remote issuer.
305357

306-
As a prerequisite, the unit possesses a `salt` of `Ns` bytes associated with a parent blinding key pair `(pk, sk)` generated using the local HDK procedure.
358+
As a prerequisite, the unit possesses a `salt` of `Ns` bytes associated with a parent key pair `(sk, pk)` generated using the local HDK procedure.
307359

308360
~~~
309361
# 1. Unit computes:
@@ -312,47 +364,30 @@ As a prerequisite, the unit possesses a `salt` of `Ns` bytes associated with a p
312364
# 2. Unit shares with issuer: (pk, pk_kem)
313365

314366
# 3. Issuer computes:
315-
(salt, kh) = KEM-Encap(pk_kem)
367+
(salt_kem, kh) = KEM-Encap(pk_kem)
316368

317369
# 4. Issuer shares with unit: kh
318370

319371
# Subsequently, for any index known to both parties:
320372

321373
# 5. Issuer computes:
322-
(bf, salt') = HDK(salt, index)
323-
pk' = BL-Blind-Public-Key(pk, bf)
374+
(pk', salt', bf') = HDK(index, pk, salt_kem)
324375

325376
# 6. Issuer shares with unit: pk'
326377

327378
# 7. Unit verifies integrity:
328-
salt' = KEM-Decap(kh, sk_kem)
329-
(bf, salt'') = HDK(salt', index)
330-
pk' == BL-Blind-Public-Key(pk, bf)
379+
salt_kem = KEM-Decap(kh, sk_kem)
380+
(pk_expected', salt', bf') = HDK(index, pk, salt_kem)
381+
pk' == pk_expected'
331382

332383
# 8. Unit computes:
333-
sk' = BL-Blind-Private-Key(sk, bf)
384+
sk' = BL-Blind-Private-Key(sk, bf) # optional
334385
~~~
335386

336-
After step 7, the unit can use the value of `salt''` to derive next-level HDKeys.
387+
After step 7, the unit can use the value of `salt'` to derive next-level HDKeys.
337388

338389
Step 4 MAY be postponed to be combined with step 6. Steps 5 to 8 MAY be combined in concurrent execution for multiple indices.
339390

340-
## Combining blinding factors
341-
342-
A unit MUST not persist a blinded private key. Instead, if persistence is needed, a unit can persist either the blinding factor of each HDK, or a path consisting of the seed salt, indices and key handles. In both cases, the unit needs to combine parent blinding factor `bf1` with child blinding factor `bf2` before blinding the parent private key `sk`:
343-
344-
~~~
345-
bf = BL-Combine-Blinding-Factors(bf1, bf2)
346-
~~~
347-
348-
Subsequently, the unit can apply the Authenticate function to the parent blinding key. The unit can combine multiple blinding factors in the HDK path.
349-
350-
If the unit uses the blinded private key directly, the unit MUST use it within the secure cryptographic device protecting the device private key.
351-
352-
If the unit uses the blinded private key directly, the unit MUST ensure the secure cryptographic device deletes it securely from memory after usage.
353-
354-
When presenting multiple documents, a reader can require a proof that multiple keys are associated to a single device. Several protocols for a cryptographic proof of association are possible, such as [Verheul2024]. For example, a unit could prove in a zero-knowledge protocol knowledge of the association between two elliptic curve keys `B1 = [bf1]D` and `B2 = [bf2]D`, where `bf1` and `bf2` are multiplicative blinding factors for a common blinding public key `D`. In this protocol, the association is known by the discrete logarithm of `B2 = [bf2/bf1]B1` with respect to generator `B1`. The unit can apply BL-Combine-Blinding-Factors to obtain values to compute this association.
355-
356391
# Generic HDK instantiations
357392

358393
## Using elliptic curves
@@ -370,12 +405,12 @@ Instantiations of HDK using elliptic curves require the following cryptographic
370405
- hash_to_field(msg, count): Outputs `count` EC Elements based on the result of cryptographically hashing `msg` (see [RFC9380], Section 5.2).
371406

372407
~~~
373-
def BL-Generate-Blinding-Key-Pair():
408+
def D-Generate-Blinding-Key-Pair():
374409
sk = EC-Random()
375410
pk = EC-Scalar-Base-Mult(sk)
376-
return (pk, sk)
411+
return (sk, pk)
377412

378-
def BL-Derive-Blinding-Factor(msg, ctx):
413+
def BL-Derive-Blind-Key(ikm, ctx):
379414
bf = hash_to_field(msg, count) with the parameters:
380415
DST: ID || ctx
381416
F: GF(EC-Order()), the scalar field

prototype.lisp

-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@
9292
(i2osp (getf (crypto:ec-destructure-point (scalar-mult (ec dh) pk-y sk-x)) :x)
9393
(output-length dh)))
9494

95-
;;(defmethod generate-blind-key ((bl ec-bl)) (random-scalar (ec bl)))
9695
(defmethod derive-blind-key ((bl ec-bl) ikm)
9796
(let ((h2c (make-instance 'h2c :ec (ec bl) :h (hash bl) :dst (id bl))))
9897
(i2osp (hash-to-field h2c ikm) 32)))

0 commit comments

Comments
 (0)