You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -237,16 +237,19 @@ The parameters of an HDK instantiation are:
237
237
- H(msg): Outputs `Ns` bytes.
238
238
- `BL`: A key blinding scheme [Wilson2023] with opaque blinding factors and algebraic properties, consisting of the functions:
239
239
- DeriveBlindKey(ikm): Outputs a blind key `bk` based on input keying material `ikm`.
240
-
- DeriveBlindingFactor(bk, ctx): Outputs a blinding factor `bf` based on a blind key `bk` and an application context byte string `ctx`.
241
240
- BlindPublicKey(pk, bk, ctx): Outputs the result public key `pk'` of blinding public key `pk` with blind key `bk` and application context byte string `ctx`.
242
-
- BlindPrivateKey(sk, bf): Outputs the result private key `sk'` of blinding private key `sk` with blinding factor `bf`. This result `sk'` is such that if `bf = DeriveBlindingFactor(bk, ctx)` for some `bk` and `ctx`, `(sk', pk')` forms a key pair for `pk' = BlindPublicKey(pk, bk, ctx)`.
243
-
- Combine(bf1, bf2): Outputs a blinding factor `bf` such that for all key pairs `(sk, pk)`:
241
+
- BlindPrivateKey(sk, bk, ctx): Outputs the result private key `sk'` of blinding private key `sk` with blind key `bk` and application context byte string `ctx`. The result `sk'` is such that if `pk` is the public key for `sk`, then `(sk', pk')` forms a key pair for `pk' = BlindPublicKey(pk, bk, ctx)`.
242
+
- Combine(k1, k2): Outputs a blinding factor `bf` given input keys `k1` and `k2` which are either private keys or blinding factors, with the following associative property. For all input keys `k1`, `k2`, `k3`:
- DeriveBlindingFactor(bk, ctx): Outputs a blinding factor `bf` based on a blind key `bk` and an application context byte string `ctx`, such that for all private keys `sk`:
249
248
249
+
~~~
250
+
BlindPrivateKey(sk, bk, ctx) == Combine(sk, bf)
251
+
~~~
252
+
- SerializePublicKey(pk): Outputs a canonical byte string serialisation of public key `pk`.
250
253
- `KEM`: A key encapsulation mechanism [RFC9180], consisting of the functions:
251
254
- DeriveKeyPair(ikm): Outputs a key encapsulation key pair `(sk, pk)`.
252
255
- Encap(pk): Outputs `(k, c)` consisting of a shared secret `k` and a ciphertext `c`, taking key encapsulation public key `pk`.
@@ -264,17 +267,18 @@ A local unit or remote party creates an HDK context from an index.
264
267
265
268
~~~
266
269
Inputs:
270
+
- pk, a public key to be blinded.
267
271
- index, an integer between 0 and 2^32-1 (inclusive).
268
272
269
273
Outputs:
270
274
- ctx, an application context byte string.
271
275
272
-
def CreateContext(index):
273
-
ctx = ID || I2OSP(index, 4)
276
+
def CreateContext(pk, index):
277
+
ctx = SerializePublicKey(pk) || I2OSP(index, 4)
274
278
return ctx
275
279
~~~
276
280
277
-
This context byte string is used as input for DeriveBlindingFactor, BlindPublicKey, and [DeriveSalt](#the-hdk-salt).
281
+
This context byte string is used as input for DeriveBlindingFactor, BlindPrivateKey, BlindPublicKey, and [DeriveSalt](#the-hdk-salt).
278
282
279
283
## The HDK salt
280
284
@@ -306,25 +310,30 @@ Inputs:
306
310
- index, an integer between 0 and 2^32-1 (inclusive).
307
311
- pk, a public key to be blinded.
308
312
- salt, a string of Ns bytes.
309
-
- bf, a blinding factor to combine with, Nil otherwise.
313
+
- bf, a blinding factor to combine with, if any, Nil otherwise.
314
+
- skD, a private key to be blinded, if known, Nil otherwise.
310
315
311
316
Outputs:
312
317
- pk', the blinded public key at the provided index.
313
318
- salt', the salt for HDK derivation at the provided index.
314
319
- bf', the blinding factor at the provided index.
320
+
- bk, the current blind key.
321
+
- ctx, the current key blinding application context byte string.
322
+
- sk', the blinded private key.
315
323
316
-
def HDK(index, pk, salt, bf = Nil):
317
-
ctx = CreateContext(index)
324
+
def HDK(index, pk, salt, bf = Nil, skD = Nil):
325
+
ctx = CreateContext(pk, index)
318
326
salt' = DeriveSalt(salt, ctx)
319
327
320
328
bk = DeriveBlindKey(salt)
321
-
pk' = BlindPublicKey(bk, ctx)
322
-
bf' = if bf == Nil:
323
-
DeriveBlindingFactor(bk, ctx)
324
-
else:
325
-
Combine(bf, DeriveBlindingFactor(bk, ctx))
329
+
pk' = BlindPublicKey(pk, bk, ctx)
330
+
sk' = if skD == Nil: Nil
331
+
elif bf == Nil: BlindPrivateKey(skD, bk, ctx)
332
+
else : BlindPrivateKey(Combine(skD, bf), bk, ctx)
333
+
bf' = if bf == Nil: DeriveBlindingFactor(bk, ctx)
334
+
else : Combine(bf, DeriveBlindingFactor(bk, ctx))
326
335
327
-
return (pk', salt', bf')
336
+
return ((pk', salt', bf'), (bk, ctx), sk')
328
337
~~~
329
338
330
339
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 Combine in the HDK function enables reconstruction of the blinding factor with respect to the original private key, enabling application of for example BlindPrivateKey.
@@ -351,28 +360,30 @@ The unit MUST generate `skD` within a secure cryptographic device.
351
360
Whenever the unit requires the HDK with some `index` at level 0, the unit computes:
Now the unit can use the blinded key pair `(sk, pk)` or derive child HDKeys.
360
367
361
-
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:
368
+
Whenever the unit requires the HDK with some `index` at level `n > 0` based on a parent HDK `(pk, salt, bf)` with blinded key pair `(sk, pk)` at level `n`, the unit computes:
Now the unit can use the blinded key pair `(sk', pk')` or derive child HDKeys.
370
375
376
+
Note that providing `sk` is optional. Alternatively, the unit can use the returned `bk` and `ctx` with the parent `bf` separately in a key blinding scheme, for example using:
377
+
378
+
~~~
379
+
sk' = BlindPrivateKey(Combine(sk, bf), bk, ctx)
380
+
~~~
381
+
371
382
## The remote HDK protocol
372
383
373
384
This is a protocol between a local unit and a remote issuer.
374
385
375
-
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.
386
+
As a prerequisite, the unit possesses a `salt` of `Ns` bytes associated with a parent key pair `(sk, pk)` with blinding factor `bf` (potentially `Nil`) generated using the local HDK procedure.
376
387
377
388
~~~
378
389
# 1. Unit computes:
@@ -388,17 +399,17 @@ As a prerequisite, the unit possesses a `salt` of `Ns` bytes associated with a p
388
399
# Subsequently, for any index known to both parties:
After step 7, the unit can use the value of `salt'` to derive next-level HDKeys.
@@ -493,20 +504,26 @@ Verify(signature, pk, msg)
493
504
494
505
Instantiations of HDK using digital signatures provide:
495
506
496
-
- `BL`: A cryptographic construct that extends `DSA` as specified in [I-D.draft-irtf-cfrg-signature-key-blinding-07], implementing the interface from [Instantiation parameters](#instantiation-parameters).
507
+
- `BL`: A cryptographic construct that extends `DSA` as specified in [I-D.draft-irtf-cfrg-signature-key-blinding-07], implementing the interface from [Instantiation parameters](#instantiation-parameters), as well as:
508
+
- BlindKeySign(sk, bk, ctx, msg): Outputs the result of signing a message `msg` using the private key `sk` with the private blind key `bk` and application context byte string `ctx` such that for key pair `(sk, pk)`:
509
+
510
+
~~~
511
+
Verify( BlindKeySign(sk, bk, ctx, msg),
512
+
BlindPublicKey(pk, bk, ctx)) == 1
513
+
~~~
497
514
498
-
While [I-D.draft-irtf-cfrg-signature-key-blinding-07] does not expose blinding factors, it provides public algorithms to compute these. In HDK, the computed blinding factors are applied in `BL` as follows:
515
+
By design of `BL`, the same proof of possession protocol can be used with blinded key pairs and BlindKeySign, in such a way that the reader does not recognise that key blinding was used.
516
+
517
+
In the default implementation, BlindKeySign requires support from the secure cryptographic device protecting `sk`:
499
518
500
519
~~~
501
-
def BlindSign(sk, bf, msg):
502
-
sk' = BlindPrivateKey(sk, bf)
520
+
def BlindKeySign(sk, bk, ctx, msg):
521
+
sk' = BlindPrivateKey(sk, bk, ctx)
503
522
signature = Sign(sk', msg)
504
523
return signature
505
524
~~~
506
525
507
-
By design of `BL`, the same proof of possession protocol can be used with blinded key pairs and BlindSign, in such a way that the reader does not recognise that key blinding was used.
508
-
509
-
In the default implementation, BlindSign requires support from the secure cryptographic device protecting `sk`. In some cases, BlindSign can be implemented in an alternative, distributed way. An example will be provided below.
526
+
In some cases, BlindKeySign can be implemented in an alternative, distributed way. An example will be provided for [using EC-SDSA signatures](#using-ec-sdsa-signatures).
510
527
511
528
Applications MUST bind the message to be signed to the blinded public key. This mitigates attacks based on signature malleability. Several proof of possession protocols require including document data in the message, which includes the blinded public key indeed.
512
529
@@ -520,7 +537,6 @@ Instantiations of HDK using prime-order groups require:
520
537
- ScalarMult(A, k): Outputs the scalar multiplication between Element `A` and Scalar `k`.
521
538
- ScalarBaseMult(k): Outputs the scalar multiplication between the base Element and Scalar `k`.
522
539
- Order(): Outputs the order of the base Element.
523
-
- SerializeElement(A): Outputs a byte string representing Element `A`.
524
540
- SerializeScalar(k): Outputs a byte string representing Scalar `k`.`
525
541
- HashToScalar(msg): Outputs the result of deterministically mapping a byte string `msg` to an element in the scalar field of the prime order subgroup of `G`, using the `hash_to_field` function from a hash-to-curve suite [RFC9380].
Note that DeriveBlindingFactor is compatible with the definitions in [I-D.draft-irtf-cfrg-signature-key-blinding-07]. The function is almost compatible with the definitions in [I-D.draft-bradleylundberg-cfrg-arkg-02]: only in AKRG, the context string needs to be prefixed with `0x00`.
562
+
Note that DeriveBlindKey and DeriveBlindingFactor are compatible with the definitions in [I-D.draft-irtf-cfrg-signature-key-blinding-07]. We illustrate also what would be needed instead for full compatibility with [I-D.draft-bradleylundberg-cfrg-arkg-02] below and when [Using elliptic curves](#using-elliptic-curves):
We illustrate also what would be needed instead for full compatibility with [I-D.draft-bradleylundberg-cfrg-arkg-02] below:
657
+
658
+
~~~
659
+
def DeriveBlindingFactor_ARKG(bk, ctx):
660
+
bf = HashToScalar_ARKG(msg, ctx)
661
+
return bf
662
+
663
+
def HashToScalar_ARKG(msg, info):
664
+
scalar = hash_to_field(msg, 1) with the parameters:
665
+
DST: DST || info
666
+
F: GF(Order()), the scalar field
667
+
of the prime order subgroup of EC
668
+
p: Order()
669
+
m: 1
670
+
L: as defined in H2C
671
+
expand_message: as defined in H2C
672
+
return scalar
673
+
~~~
674
+
627
675
### Using ECDH shared secrets
628
676
629
677
Instantiations of HDK using ECDH shared secrets use:
@@ -657,10 +705,11 @@ Now with the shared secret `Z_AB`, the unit and the reader can compute a secret
657
705
658
706
In this example, step 1 can be postponed in the interactions between the unit and the reader if a trustworthy earlier commitment to `pk` is available, for example in a sealed document.
659
707
660
-
Similarly, ECDH enables authentication of key pair `(sk', pk')` blinded from an original key pair `(sk, pk)` using a blinding factor `bf` such that:
708
+
Similarly, ECDH enables authentication of key pair `(sk', pk')` blinded from an original key pair `(sk, pk)` using a blind key `ctx` and application context byte string `ctx` such that:
661
709
662
710
~~~
663
-
sk' = BlindPrivateKey(sk, bf)
711
+
bf = DeriveBlindingFactor(bk, ctx)
712
+
sk' = BlindPrivateKey(sk, bk, ctx)
664
713
= sk * bf mod Order()
665
714
pk' = ScalarMult(pk, bf)
666
715
~~~
@@ -688,14 +737,15 @@ Instantiations of HDK using EC-SDSA signatures provide:
688
737
689
738
- `DSA`: An EC-SDSA digital signature algorithm [TR03111], representing signatures as pairs `(c, s)`.
690
739
691
-
Note that in this case, the following definition is equivalent to the original definition of BlindSign:
740
+
Note that in this case, the following definition is equivalent to the [original definition of BlindKeySign](#using-digital-signatures):
692
741
693
742
~~~
694
-
def BlindSign(sk, bf, msg):
743
+
def BlindKeySign(sk, bk, ctx, msg):
695
744
# Compute signature within the secure cryptographic device.
696
745
(c, s) = Sign(sk, msg)
697
746
698
747
# Post-process the signature outside of this device.
748
+
bf = DeriveBlindingFactor(bk, ctx)
699
749
s' = s + c * bf mod Order()
700
750
701
751
signature = (c, s')
@@ -872,6 +922,60 @@ HDK enables unit and issuers cooperatively to establish the cryptographic key ma
872
922
873
923
For the remote HDK protocol, HDK proposes an update to the OpenID4VCI endpoints. This proposal is under discussion in [openid/OpenID4VCI#359](https://github.com/openid/OpenID4VCI/issues/359). In the update, the unit shares a key encapsulation public key with the issuer, and the issuer returns a key handle. Then documents can be re-issued, potentially in batches, using synchronised indices. Alternatively, re-issued documents can have their own key handles.
874
924
925
+
## Applying HDK with ARKG
926
+
927
+
This section illustrates how an Asynchronous Remote Key Generation (ARKG) instance can be constructed using the interfaces from the current document. It is not fully compatible with [I-D.draft-bradleylundberg-cfrg-arkg-02] due to subtle differences, such as those in [Using prime-order groups](#using-prime-order-groups) and [Using elliptic curves](#using-elliptic-curves).
0 commit comments