<\/colgroup>/g" | \
59 | sed -e "s/Note<\/p>/
Note<\/b><\/p>/g" | \
60 | pandoc \
61 | --from=html \
62 | --pdf-engine=xelatex \
63 | --toc \
64 | --columns=10 \
65 | --variable title="Hierarchical Deterministic Keys" \
66 | --variable subtitle="for the European Digital Identity Wallet" \
67 | --variable date="Version 0.2.0-SNAPSHOT\\\\\vspace{2cm}\href{https://github.com/sander/hierarchical-deterministic-keys}{github.com/sander/hierarchical-deterministic-keys}" \
68 | --variable colorlinks=true \
69 | --variable papersize=a4 \
70 | --variable geometry="margin=2cm" \
71 | --variable numbersections=true \
72 | --variable documentclass=report \
73 | -o hdk.pdf
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hierarchical Deterministic Keys for the European Digital Identity Wallet
2 |
3 | ## Background
4 |
5 | The [EU Digital Identity Regulation](https://eur-lex.europa.eu/eli/reg/2024/1183/oj) requires secure cryptography in wallet solutions. The regulatory requirements bring several implementation challenges:
6 |
7 | 1. How might an issuer protect document authenticity?
8 | 2. How might an issuer prevent tracking based on document authenticity signatures?
9 | 3. How might a wallet solution enable binding documents to a WSCD with a high level of assurance?
10 | 4. How might a wallet solution enable relying parties to verify WSCD binding?
11 | 5. How might a wallet solution blind verification keys for each attestation?
12 | 6. How might a wallet solution prove possession of blinded keys?
13 | 7. How might a wallet solution create qualified electronic signatures or seals?
14 |
15 | The European Commission and Member States are developing a Wallet Toolbox to enable interoperable solutions to challenges such as these. This Toolbox includes the [Architecture and Reference Framework](https://eu-digital-identity-wallet.github.io/eudi-doc-architecture-and-reference-framework/latest/arf/) (ARF). The Large Scale Pilots are implementing and testing the wallet to generate feedback on this Toolbox.
16 |
17 | In this repository, Pilot participants contribute to concrete interoperable solutions based on the ideas of Hierarchical Deterministic Keys (HDKs) and blinded key proof of possession. This approach is introduced in the Analysis of selective disclosure and zero-knowledge proofs ([ETSI TR 119476 version 1.2.1](https://www.etsi.org/deliver/etsi_tr/119400_119499/119476/01.02.01_60/tr_119476v010201p.pdf)). The Pilot participants aim to evaluate various options, present an appropriate solution, and develop a common specification to enable testing interoperability.
18 |
19 | > [!NOTE]
20 | > This information is shared by participants of the [Digital Credentials for Europe (DC4EU) Consortium](https://www.dc4eu.eu), the [EU Digital Identity Wallet Consortium (EWC)](https://eudiwalletconsortium.org), and the [Potential Consortium](https://www.digital-identity-wallet.eu). Views and opinions expressed are those of the authors only and do not necessarily reflect those of all consortium members.
21 |
22 | ## Position
23 |
24 | The participants share the following position.
25 |
26 | As of today, ARF 1.4 provides no complete and interoperable solution for key management, that can be industrially deployed at scale and used across the whole ecosystem.
27 |
28 | HDK is a viable solution to some EU Digital Identity Wallet key management challenges. It enables the management of an unlimited amount of keys using a single secret. It also allows the use of existing secure cryptographic devices with common algorithms.
29 |
30 | HDK is applicable to specific credential schemes, including one-time-use attestations and BBS#.
31 |
32 | We want to start a dialogue with the European Commission about the proposed solution in this document, and how to include this in the Toolbox. We have specific feedback on the ARF high-level requirements to make sure that the EU Digital Identity implementation addresses the key management challenges in an interoperable and scalable way. This should provide a solid basis for legislation in the Implementing Acts.
33 |
34 | Expert participants from DC4EU:
35 |
36 | - John Bradley (Yubico)
37 | - Leif Johansson (Sunet)
38 | - Nikos Voutsinas (GUnet)
39 |
40 | Expert participants from Potential:
41 |
42 | - Antoine Dumanois (Orange)
43 | - Sander Dijkhuis (Cleverbase)
44 | - Zeff Sherriff (Bundesdruckerei)
45 |
46 | ## Contents
47 |
48 | To address challenges 5 and 6, this repository contains a freely accessible, unencumbered specification of **[Hierarchical Deterministic Keys](draft-dijkhuis-cfrg-hdkeys.md)**. This enables an EU Digital Identity Wallet deployment that distributes key management efficiently:
49 |
50 | To illustrate and validate the specifications, this repository contains a **[Prototype implementation](prototype.lisp)** and **[demo](prototype.demo.lisp)** to run with [Common Lisp](https://lisp-lang.org/learn/getting-started/) using `make demo` and `make repl`.
51 |
52 | To inform further standardisation and legislation, this repository contains **[Feedback to enable Hierarchical Deterministic Keys in the Wallet Toolbox](feedback.md)**. It also contains **[Feedback to resolve HDK and PoA issues in the ARF](feedback-poa.md)**.
53 |
54 | The repository does not contain details about the implementation of HDK for key management in credential schemes, such as one-time-use document schemes for relying party unlinkability and weak issuer unlinkability, or [BBS#](https://github.com/user-attachments/files/15905230/BBS_Sharp_Short_TR.pdf) for full unlinkability. Credential schemes have not currently been analysed by the working group. When such analysis is carried out, it might result in changes to the specification. For example, delegated key generation only seems to have use cases for batch one-time-use document issuance, and not for BBS#.
55 |
56 | ## Contributing
57 |
58 | See the [contributor agreement](CONTRIBUTING.md).
59 |
60 | Feedback and other input is easiest to discuss in [GitHub issues](https://github.com/sander/hierarchical-deterministic-keys/issues).
61 |
62 | To enable reuse, new contributions to the technical reports not intended for standardisation must be provided under either [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) or [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/).
63 |
--------------------------------------------------------------------------------
/draft-dijkhuis-cfrg-hdkeys.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hierarchical Deterministic Keys
3 | abbrev: HDK
4 | category: info
5 | docname: draft-dijkhuis-cfrg-hdkeys-latest
6 | submissiontype: independent
7 | v: 3
8 | area: IRTF
9 | workgroup: Crypto Forum
10 | keyword:
11 | - KDF
12 | venue:
13 | github: sander/hierarchical-deterministic-keys
14 | author:
15 | - fullname: Sander Dijkhuis
16 | role: editor
17 | initials: S. Q.
18 | surname: Dijkhuis
19 | organization: Cleverbase
20 | email: mail@sanderdijkhuis.nl
21 | contributor:
22 | - fullname: Micha Kraus
23 | ipr: trust200902
24 | normative:
25 | FIPS180-4:
26 | title: Secure Hash Standard (SHS)
27 | target: https://csrc.nist.gov/pubs/fips/180-4/upd1/final
28 | seriesinfo:
29 | FIPS: 180-4
30 | DOI: 10.6028/NIST.FIPS.180-4
31 | author:
32 | - organization: National Institute of Standards and Technology (NIST)
33 | date: 2012-06
34 | ISO18013-5:
35 | title: "Personal identification - ISO-compliant driving licence - Part 5: Mobile driving licence (mDL) application"
36 | target: https://www.iso.org/standard/69084.html
37 | seriesinfo:
38 | ISO/IEC: 18013-5:2021
39 | author:
40 | - organization: ISO/IEC
41 | date: 2019-09
42 | RFC4648:
43 | RFC5234:
44 | RFC8017:
45 | RFC9180:
46 | RFC9380:
47 | RFC9497:
48 | SEC2:
49 | title: "SEC 2: Recommended Elliptic Curve Domain Parameters, Version 2.0"
50 | target: https://www.secg.org/sec2-v2.pdf
51 | seriesinfo:
52 | SEC: 2 Version 2.0
53 | author:
54 | - organization: Certicom Research
55 | date: 2010-01
56 | TR03111:
57 | title: Elliptic Curve Cryptography
58 | target: https://www.bsi.bund.de/EN/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/Technische-Richtlinien/TR-nach-Thema-sortiert/tr03111/tr-03111.html
59 | seriesinfo:
60 | BSI: TR-03111 Version 2.10
61 | author:
62 | - organization: Federal Office for Information Security (BSI)
63 | date: 2018-06
64 | informative:
65 | BIP32:
66 | title: Hierarchical Deterministic Wallets
67 | target: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
68 | seriesinfo:
69 | BIP: 32
70 | author:
71 | - name: Pieter Wuille
72 | date: 2021-02
73 | draft-OpenID4VCI:
74 | title: OpenID for Verifiable Credential Issuance, draft 13
75 | target: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
76 | author:
77 | - name: T. Lodderstedt
78 | - name: K. Yasuda
79 | - name: T. Looker
80 | date: 2024-02-08
81 | EU2015-1502:
82 | title: Commission Implementing Regulation (EU) 2015/1502 of 8 September 2015 on setting out minimum technical specifications and procedures for assurance levels for electronic identification means
83 | target: https://eur-lex.europa.eu/legal-content/TXT/?uri=CELEX%3A32015R1502
84 | author:
85 | - organization: European Commission
86 | seriesinfo:
87 | (EU): 2015/1502
88 | date: 2025-09
89 | EU2024-1183:
90 | title: Amending Regulation (EU) No 910/2014 as regards establishing the European Digital Identity Framework
91 | target: https://data.europa.eu/eli/reg/2024/1183/oj
92 | author:
93 | - organization: The European Parliament and the Council of the European Union
94 | seriesinfo:
95 | (EU): 2024/1183
96 | date: 2024-04
97 | I-D.draft-bradleylundberg-cfrg-arkg-02:
98 | I-D.draft-irtf-cfrg-signature-key-blinding-07:
99 | RFC7800:
100 | RFC8235:
101 | Verheul2024:
102 | title: Attestation Proof of Association – provability that attestation keys are bound to the same hardware and person
103 | target: https://eprint.iacr.org/2024/1444
104 | author:
105 | - name: E. Verheul
106 | date: 2024-09-18
107 | Wilson2023:
108 | title: Post-Quantum Account Recovery for Passwordless Authentication. Master’s thesis
109 | target: https://hdl.handle.net/10012/19316
110 | author:
111 | - name: Spencer MacLaren Wilson
112 | date: 2023-04-24
113 |
114 | --- abstract
115 |
116 | Hierarchical Deterministic Keys enables managing large sets of keys bound to a secure cryptographic device that protects a single key. This enables the development of secure digital identity wallets providing many one-time-use public keys. Some instantiations can be implemented in such a way that the secure cryptographic device does not need to support key blinding, enabling the use of devices that already are widely deployed.
117 |
118 | --- middle
119 |
120 | # Introduction
121 |
122 | This document specifies the algorithms to apply Hierarchical Deterministic Keys (HDKeys). The purpose of an HDK architecture is to manage large sets of keys bound to a secure cryptographic device that protects a single key. This enables the development of secure digital identity wallets providing many one-time-use public keys.
123 |
124 | The core idea has been introduced in [BIP32] to create multiple cryptocurrency addresses in a manageable way. The present document extends the idea towards devices commonly used for digital wallets, and towards common interaction patterns for document issuance and authentication.
125 |
126 | To store many HDKeys, only a seed string needs to be stored confidentially, associated with a device private key. Each HDK is then deterministically defined by a path of indices, optionally alternated by key handles provided by another party. Such a path can efficiently be stored and requires less confidentiality than the seed.
127 |
128 | To prove possession of many HDKeys, the secure cryptographic device only needs to perform common cryptographic operations on a single private key. The HDK acts as a blinding factor that enables blinding the device public key. In several instantiations, such as those [using ECDH shared secrets](#using-ecdh-shared-secrets) and those [using EC-SDSA signatures](#using-ec-sdsa-signatures), the secure cryptographic device does not need to support key blinding natively, and the application can pre-process the input or post-process the output from the device to compute the blinded device authentication data. This enables the application of HDK on devices that are already deployed without native support for HDK.
129 |
130 | This document provides a specification of the generic HDK function, generic HDK instantiations, and fully specified concrete HDK instantiations.
131 |
132 | An HDK instantiation is expected to be applied in a solution deployed as (wallet) units. One unit can have multiple HDK instantiations, for example to manage multiple identities or multiple cryptographic algorithms or key protection mechanisms.
133 |
134 | This document represents the consensus of the authors, based on working group input and feedback. It is not a standard. It does not include security or privacy proofs.
135 |
136 | ## Conventions and definitions
137 |
138 | {::boilerplate bcp14-tagged}
139 |
140 | ## Notation and terminology
141 |
142 | The following notation is used throughout the document.
143 |
144 | General terms:
145 |
146 | - `I2OSP(n, w)`: Convert non-negative integer `n` to a `w`-length, big-endian byte string, as described in [RFC8017].
147 |
148 | Terms specific to HDK:
149 |
150 | - HDK context `ctx`: A byte string derived from a public key and an index, used to enforce domain separation in HDK-based key derivation.
151 | - Key encapsulation mechanism (KEM): A cryptographic scheme used in remote HDK derivation to securely exchange a shared secret.
152 | - HDK Salt: A `Ns`-byte value used to introduce entropy into the HDK derivation. Without knowledge of the salt, the derived keys appear unrelated.
153 | - Blind Key `bk`: A scalar that propagates the entropy from the salt to the deterministic key derivation.
154 | - Blinding factor `bf`: A scalar applied to a private-public key pair to produce a blinded version. Obtained by (repeatedly) applying a blind key with application (HDK) context.
155 | - Blinded Private Key `sk_b`: A private key transformed using a blinding factor.
156 | - Blinded Public Key `pk_b`: A public key derived from another public key with a blinding factor to ensure that the derived key appears unrelated to any other key.
157 | - HDK Key Alias Format: A structured identifier representing HDK-derived keys.
158 |
159 | Algorithmic and cryptographic notation:
160 |
161 | - `DeriveBlindKey(ikm)`: Generates a blind key from the input key material `ikm`. Ensures uniform distribution of outputs, and propagates any entropy from `ikm`.
162 | - `DeriveBlindingFactor(bk, ctx)`: Derives a blinding factor from a blind key for a given context. Blinding factors are unlinkable if either the blind key or the context is unknown.
163 | - `BlindPrivateKey(sk, bf)`: Blinds a private key `sk` with the blinding factor `bf` to generate the private key `sk_b`.
164 | - `BlindPublicKey(pk, bf)`: Blinds a public key `pk` with the blinding factor `bf` to generate the public key `pk_b`.
165 | - `Combine(s1, s2)`: Computes a new blinding factor by combining two scalar values s1 and s2. Supports multi-stage derivations and key composability in HDK.
166 | - `SerializePublicKey(pk)`: Encodes the public key `pk` into a canonical byte string representation.
167 |
168 | Security and implementation specific notation:
169 |
170 | - Secure Cryptographic Device (WSCD): A module that securely stores and processes cryptographic keys and provides the hardware root of trust for HDK-based key derivation.
171 | - Trust Evidence: Information providing ownership or authenticity of a derived key.
172 | - Wallet Secure Cryptographic Application (WSCA): A secure application within a WSCD that exposes cryptographic functions used in key derivation.
173 |
174 | # The Hierarchical Deterministic Key function
175 |
176 | An HDK instantiation enables local key derivation to create many key pairs from a single seed value. It enables remote parties to generate key handles from which both parties can derive more key pairs asynchronously. Additionally, an HDK instantiation enables securely proving possession of the private keys, such as required in [RFC7800], either in a centralised or in a distributed manner.
177 |
178 | Solutions MAY omit application of the remote functionality. In this case, a unit can only derive keys locally.
179 |
180 | ## Introductory examples
181 |
182 | ### Local deterministic key derivation
183 |
184 | 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.
185 |
186 | ~~~
187 | +----+ +--+
188 | Confidential static data: |seed| |pk|
189 | +-+--+ +--+
190 | v
191 | +----+ +----+
192 | Level 0 HDKeys: |hdk0| |hdk1|
193 | +-+--+ +----+
194 | v
195 | +-----+ +-----+ +-----+
196 | Level 1 HDKeys: |hdk00| |hdk01| |hdk02|
197 | +-----+ ++---++ +-----+
198 | v v
199 | +------+ +------+
200 | Level 2 HDKeys at hdk01: |hdk000| |hdk001| ...
201 | +------+ +------+
202 | ~~~
203 |
204 | 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.
205 |
206 | 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.
207 |
208 | ### Remote deterministic key derivation
209 |
210 | 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.
211 |
212 | This approach is similar to Asynchronous Remote Key Generation (ARKG) [I-D.draft-bradleylundberg-cfrg-arkg-02] when considered at a single level. However, ARKG does not enable distributed proof of possession with deterministic hierarchies. Such hierarchies can be used for example to enable remote parties to derive keys from previously derived keys. Secure cryptographic devices that support ARKG may therefore not support all features of HDK.
213 |
214 | To enable remote derivation of child HDKeys, the unit uses the parent HDK 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.
215 |
216 | Locally derived parents can have remotely derived children. Remotely derived parents can have locally derived children.
217 |
218 | ### Blinded proof of possession
219 |
220 | 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.
221 |
222 | 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.
223 |
224 | ~~~
225 | +------------------+ +--------+
226 | | +--+ +---+ | | |
227 | | Unit |sk| |hdk| | | Reader |
228 | | +--+ +---+ | | |
229 | +---+--------------+ +----+---+
230 | | |
231 | | |
232 | | 1. Request and |
233 | | reader_data |
234 | | <------------------ |
235 | | |
236 | +---+-------------+ |
237 | | 2. authenticate | |
238 | +---+-------------+ |
239 | | |
240 | | 3. Proof with |
241 | | device_data |
242 | | ------------------> |
243 | | |
244 | | +-----------+ |
245 | | | Document | |
246 | | | | |
247 | | | +---+ | |
248 | | | |pk'| | |
249 | | | +---+ | |
250 | | | | |
251 | +-----------+
252 | ~~~
253 |
254 | The reader does not need to be aware that an HDK function or key blinding was used, since for common algorithms, the blinded public key and the proof are indistinguishable from non-blinded keys and proofs.
255 |
256 | When applied on HDK level `n`, the blinding private key `sk` is the device private key blinded with a combination of `n` blinding factors. These can either be combined within the secure cryptographic device, by subsequent computation of the blinded private key starting with the device private key, or outside of the secure cryptographic device, by combining the blinding factors outside of the secure cryptographic device.
257 |
258 | Blinding methods can be constructed such that the secure cryptographic device does not need to be designed for key blinding. In such cases, the computation of `device_data` is distributed between two parties: the secure cryptographic device using common cryptographic operations, and the unit component invoking these operations. Some blinded proof of possession algorithms can only be centralised.
259 |
260 | ## Instantiation parameters
261 |
262 | The parameters of an HDK instantiation are:
263 |
264 | - `Ns`: The amount of bytes of a salt value with sufficient entropy.
265 | - `H`: A cryptographic hash function.
266 | - H(msg): Outputs `Ns` bytes.
267 | - `BL`: A key blinding scheme [Wilson2023] with opaque blinding factors and algebraic properties, consisting of the functions:
268 | - DeriveBlindKey(ikm): Outputs a blind key `bk` based on input keying material `ikm`.
269 | - 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`.
270 | - 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)`.
271 | - 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`:
272 |
273 | ~~~
274 | Combine(Combine(k1, k2), k3) == Combine(k1, Combine(k2, k3))
275 | ~~~
276 | - 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`:
277 |
278 | ~~~
279 | BlindPrivateKey(sk, bk, ctx) == Combine(sk, bf)
280 | ~~~
281 | - SerializePublicKey(pk): Outputs a canonical byte string serialisation of public key `pk`.
282 | - `KEM`: A key encapsulation mechanism [RFC9180], consisting of the functions:
283 | - DeriveKeyPair(ikm): Outputs a key encapsulation key pair `(sk, pk)`.
284 | - Encap(pk): Outputs `(k, c)` consisting of a shared secret `k` and a ciphertext `c`, taking key encapsulation public key `pk`.
285 | - Decap(c, sk): Outputs shared secret `k`, taking ciphertext `c` and key encapsulation private key `sk`.
286 |
287 | An HDK instantiation MUST specify the instantiation of each of the above functions and values.
288 |
289 | 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.
290 |
291 | An HDK implementation MAY leave BlindPrivateKey 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.
292 |
293 | ## The HDK context
294 |
295 | A local unit or remote party creates an HDK context from an index.
296 |
297 | ~~~
298 | Inputs:
299 | - pk, a public key to be blinded.
300 | - index, an integer between 0 and 2^32-1 (inclusive).
301 |
302 | Outputs:
303 | - ctx, an application context byte string.
304 |
305 | def CreateContext(pk, index):
306 | ctx = SerializePublicKey(pk) || I2OSP(index, 4)
307 | return ctx
308 | ~~~
309 |
310 | This context byte string is used as input for DeriveBlindingFactor, BlindPrivateKey, BlindPublicKey, and [DeriveSalt](#the-hdk-salt).
311 |
312 | ## The HDK salt
313 |
314 | A local unit or remote party derives a next-level HDK salt from within an HDK context.
315 |
316 | ~~~
317 | Inputs:
318 | - salt, a string of Ns bytes.
319 | - ctx, an HDK context byte string.
320 |
321 | Outputs:
322 | - salt', the next salt for HDK derivation.
323 |
324 | def DeriveSalt(salt, ctx):
325 | salt' = H(salt || ctx)
326 | return salt'
327 | ~~~
328 |
329 | Salt values are used as input for DeriveBlindKey, DeriveKeyPair, and DeriveSalt.
330 |
331 | Salt values, including the original seed value, MUST NOT be reused outside of HDK.
332 |
333 | ## The HDK function
334 |
335 | 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.
336 |
337 | ~~~
338 | Inputs:
339 | - index, an integer between 0 and 2^32-1 (inclusive).
340 | - pk, a public key to be blinded.
341 | - salt, a string of Ns bytes.
342 | - bf, a blinding factor to combine with, if any, Nil otherwise.
343 | - skD, a private key to be blinded, if known, Nil otherwise.
344 |
345 | Outputs:
346 | - pk', the blinded public key at the provided index.
347 | - salt', the salt for HDK derivation at the provided index.
348 | - bf', the blinding factor at the provided index.
349 | - bk, the current blind key.
350 | - ctx, the current key blinding application context byte string.
351 | - sk', the blinded private key.
352 |
353 | def HDK(index, pk, salt, bf = Nil, skD = Nil):
354 | ctx = CreateContext(pk, index)
355 | salt' = DeriveSalt(salt, ctx)
356 |
357 | bk = DeriveBlindKey(salt)
358 | pk' = BlindPublicKey(pk, bk, ctx)
359 | sk' = if skD == Nil: Nil
360 | elif bf == Nil: BlindPrivateKey(skD, bk, ctx)
361 | else : BlindPrivateKey(Combine(skD, bf), bk, ctx)
362 | bf' = if bf == Nil: DeriveBlindingFactor(bk, ctx)
363 | else : Combine(bf, DeriveBlindingFactor(bk, ctx))
364 |
365 | return ((pk', salt', bf'), (bk, ctx), sk')
366 | ~~~
367 |
368 | 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.
369 |
370 | If the unit uses the blinded private key directly, the unit MUST use it within the secure cryptographic device protecting the device private key.
371 |
372 | If the unit uses the blinded private key directly, the unit MUST ensure the secure cryptographic device deletes it securely from memory after usage.
373 |
374 | 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 Combine to obtain values to compute this association.
375 |
376 | ## The local HDK procedure
377 |
378 | This is a procedure executed locally by a unit.
379 |
380 | To begin, the unit securely generates a `seed` salt of `Ns` bytes and a device key pair:
381 |
382 | ~~~
383 | seed = random(Ns) # specification of random out of scope
384 | (skD, pkD) = GenerateKeyPair()
385 | ~~~
386 |
387 | The unit MUST generate `skD` within a secure cryptographic device.
388 |
389 | Whenever the unit requires the HDK with some `index` at level 0, the unit computes:
390 |
391 | ~~~
392 | ((pk, salt, bf), (bk, ctx), sk) = HDK(index, pkD, seed, Nil, sk)
393 | ~~~
394 |
395 | Now the unit can use the blinded key pair `(sk, pk)` or derive child HDKeys.
396 |
397 | 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:
398 |
399 | ~~~
400 | ((pk', salt', bf'), (bk, ctx), sk') = HDK(index, pk, salt, bf, sk)
401 | ~~~
402 |
403 | Now the unit can use the blinded key pair `(sk', pk')` or derive child HDKeys.
404 |
405 | 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:
406 |
407 | ~~~
408 | sk' = BlindPrivateKey(Combine(sk, bf), bk, ctx)
409 | ~~~
410 |
411 | ## The remote HDK protocol
412 |
413 | This is a protocol between a local unit and a remote issuer.
414 |
415 | 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.
416 |
417 | ~~~
418 | # 1. Unit computes:
419 | (skR, pkR) = DeriveKeyPair(salt)
420 |
421 | # 2. Unit shares with issuer: (pk, pkR)
422 |
423 | # 3. Issuer computes:
424 | (salt_kem, kh) = Encap(pkR)
425 |
426 | # 4. Issuer shares with unit: kh
427 |
428 | # Subsequently, for any index known to both parties:
429 |
430 | # 5. Issuer computes:
431 | ((pk', salt', bf'), _, _) = HDK(index, pk, salt_kem)
432 |
433 | # 6. Issuer shares with unit: pkA = pk'
434 |
435 | # 7. Unit verifies integrity:
436 | salt_kem = Decap(kh, skR)
437 | ((pk', salt', bf'), (bk, ctx), _) = HDK(index, pk, salt_kem, bf)
438 | pk' == pkA
439 |
440 | # 8. Unit computes:
441 | sk' = BlindPrivateKey(Combine(sk, bf), bk, ctx) # optional
442 | ~~~
443 |
444 | After step 7, the unit can use the value of `salt'` to derive next-level HDKeys.
445 |
446 | Step 4 MAY be postponed to be combined with step 6. Steps 5 to 8 MAY be combined in concurrent execution for multiple indices.
447 |
448 | ## The HDK key alias format
449 |
450 | An HDK can be represented canonically using the following string format, in augmented Backus-Naur form (ABNF) [RFC5234] and applying non-padded base64url encoding [RFC4648] for key handles:
451 |
452 | ~~~
453 | hdk-key-alias = origin-alias "/" path
454 |
455 | ; The origin-alias is an opaque identifier for a device
456 | ; key pair, the associated HDK instantiation, and the seed.
457 | origin-alias = 1*255no-slash
458 |
459 | ; The hdk-path identifies the indices and key handles to
460 | ; apply from left to right.
461 | hdk-path = hdk-index *("/" hdk-sub-path)
462 |
463 | hdk-sub-path = *(hdk-edge "/") hdk-index
464 | hdk-edge = ("#" hdk-key-handle) / hdk-index
465 |
466 | ; The index is to be parsed to an integer between 0 and
467 | ; 2^32-1 (inclusive) and used as input to CreateContext.
468 | hdk-index = non-zero-digit 0*9DIGIT
469 |
470 | ; The key handle is to be decoded from
471 | hdk-key-handle = 1*base64url-char
472 |
473 | no-slash = %x21-%x2E / %x30-%x7E ; ASCII printable, no "/"
474 | non-zero-digit = %31-39
475 | base64url-char = ALPHA / DIGIT / "-" / "_"
476 | ~~~
477 |
478 | A unit MAY use the HDK key alias format to represent keys internally.
479 |
480 | A unit MUST NOT directly include the device private key in the `origin-alias`.
481 |
482 | A unit MUST NOT directly include the seed in the `origin-alias`.
483 |
484 | When taking input in the HDK key alias format:
485 |
486 | - a unit MAY pose further limitations on the value of `origin-alias`;
487 | - a unit MUST limit either the amount of `hdk-edge` instances or the total length of the `hdk-key-alias`;
488 | - a unit MUST verify that the byte strings represented by `hdk-key-handle` has the size of ciphertext in `KEM`.
489 |
490 | Example key handles:
491 |
492 | ~~~
493 | my_pid_key/0
494 |
495 | my_pid_key/12345
496 |
497 | my_pid_key/0/iS2ipkvGCDI0-Lps25Ex2KdjTfGRmIBjGEHkjBCPoQg/3
498 |
499 | ; newline for printing purposes not in the actual hdk-path
500 | second_key/123/45/itnCVhZ-DYZDaUqofDNhHEbNc9XOrdnLL9B-9dVZ
501 | tTg/6789/3JVRsML8NvUnCx1CvzpZrHSn4TkSUpGgn8r-X_RiQ1Y/3
502 | ~~~
503 |
504 | # Generic HDK instantiations
505 |
506 | ## Using digital signatures
507 |
508 | Instantiations of HDK using digital signatures require:
509 |
510 | - `DSA`: A digital signature algorithm, consisting of the functions:
511 | - GenerateKeyPair(): Outputs a new key pair `(sk, pk)` consisting of private key `sk` and public key `pk`.
512 | - Sign(sk, msg): Outputs the signature created using private signing key `sk` over byte string `msg`.
513 | - Verify(signature, pk, msg): Outputs whether `signature` is a signature over `msg` using public verification key `pk`.
514 |
515 | Using these constructs, an example proof of possession protocol is:
516 |
517 | ~~~
518 | # 1. Unit shares with reader: pk
519 |
520 | # 2. Reader computes:
521 | nonce = generate_random_nonce() # out of scope for this spec
522 |
523 | # 3. Reader shares with unit: nonce
524 |
525 | # 4. Unit computes:
526 | msg = create_message(pk, nonce) # out of scope for this spec
527 | signature = Sign(sk, msg)
528 |
529 | # 5. Reader computes:
530 | msg = create_message(pk, nonce) # out of scope for this spec
531 | Verify(signature, pk, msg)
532 | ~~~
533 |
534 | Instantiations of HDK using digital signatures provide:
535 |
536 | - `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:
537 | - 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)`:
538 |
539 | ~~~
540 | Verify( BlindKeySign(sk, bk, ctx, msg),
541 | BlindPublicKey(pk, bk, ctx)) == 1
542 | ~~~
543 |
544 | 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.
545 |
546 | In the default implementation, BlindKeySign requires support from the secure cryptographic device protecting `sk`:
547 |
548 | ~~~
549 | def BlindKeySign(sk, bk, ctx, msg):
550 | sk' = BlindPrivateKey(sk, bk, ctx)
551 | signature = Sign(sk', msg)
552 | return signature
553 | ~~~
554 |
555 | 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).
556 |
557 | 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.
558 |
559 | ## Using prime-order groups
560 |
561 | Instantiations of HDK using prime-order groups require:
562 |
563 | - `G`: A prime-order group as defined in [RFC9497] with elements of type Element and scalars of type Scalar, consisting of the functions:
564 | - RandomScalar(): Outputs a random Scalar `k`.
565 | - Add(A, B): Outputs the sum between Elements `A` and `B`.
566 | - ScalarMult(A, k): Outputs the scalar multiplication between Element `A` and Scalar `k`.
567 | - ScalarBaseMult(k): Outputs the scalar multiplication between the base Element and Scalar `k`.
568 | - Order(): Outputs the order of the base Element.
569 | - SerializeScalar(k): Outputs a byte string representing Scalar `k`.`
570 | - 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].
571 |
572 | Instantiations of HDK using prime-order groups provide:
573 |
574 | ~~~
575 | def GenerateKeyPair():
576 | sk = GenerateRandomScalar()
577 | pk = ScalarBaseMult(sk)
578 | return (sk, pk)
579 |
580 | def DeriveBlindKey(ikm):
581 | bk_scalar = HashToScalar(ikm)
582 | bk = SerializeScalar(bk_scalar)
583 | return bk
584 |
585 | def DeriveBlindingFactor(bk, ctx):
586 | msg = bk || 0x00 || ctx
587 | bf = HashToScalar(msg)
588 | return bf
589 | ~~~
590 |
591 | 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):
592 |
593 | ~~~
594 | def DeriveBlindKey_ARKG(ikm):
595 | # There is no need for additional processing,
596 | # since bk is in ARKG as intermediate input
597 | # for a pseudo-random function only.
598 | bk = ikm
599 | return bk
600 | ~~~
601 |
602 | ### Using additive blinding
603 |
604 | Instantiations of HDK using additive blinding use:
605 |
606 | - [prime-order groups](#using-prime-order-groups)
607 |
608 | Instantiations of HDK using additive blinding provide:
609 |
610 | ~~~
611 | def BlindPublicKey(pk, bk, ctx):
612 | bf = DeriveBlindingFactor(bk, ctx)
613 | pk' = Add(pk, ScalarBaseMult(bf))
614 | return pk
615 |
616 | def BlindPrivateKey(sk, bk, ctx):
617 | bf = DeriveBlindingFactor(bk, ctx)
618 | sk' = sk + bf mod Order()
619 | if sk' == 0: abort with an error
620 | return sk
621 |
622 | def Combine(bf1, bf2):
623 | bf = bf1 + bf2 mod Order()
624 | return bf
625 | ~~~
626 |
627 | Note that all algorithms in [I-D.draft-bradleylundberg-cfrg-arkg-02] use additive blinding.
628 |
629 | ### Using multiplicative blinding
630 |
631 | Instantiations of HDK using multiplicative blinding use:
632 |
633 | - [prime-order groups](#using-prime-order-groups)
634 |
635 | Instantiations of HDK using multiplicative blinding provide:
636 |
637 | ~~~
638 | def BlindPublicKey(pk, bk, ctx):
639 | bf = DeriveBlindingFactor(bk, ctx)
640 | pk' = ScalarMult(pk, bf)
641 | return pk
642 |
643 | def BlindPrivateKey(sk, bk, ctx):
644 | bf = DeriveBlindingFactor(bk, ctx)
645 | sk' = sk * bf mod Order()
646 | if sk' == 1: abort with an error
647 | return sk
648 |
649 | def Combine(bf1, bf2):
650 | bf = bf1 * bf2 mod Order()
651 | return bf
652 | ~~~
653 |
654 | Note that all algorithms in [I-D.draft-irtf-cfrg-signature-key-blinding-07] use multiplicative blinding.
655 |
656 | ## Using elliptic curves
657 |
658 | Instantiations of HDK using elliptic curves use:
659 |
660 | - [prime-order groups](#using-prime-order-groups)
661 |
662 | Instantiations of HDK using elliptic curves require:
663 |
664 | - `DST`: A domain separation tag for use with HashToScalar.
665 | - `H2C`: A hash-to-curve suite [RFC9380].
666 |
667 | Instantiations of HDK using elliptic curves provide:
668 |
669 | - `H`: `H` from `H2C`.
670 | - `Ns`: The output size of `H`.
671 |
672 | ~~~
673 | def HashToScalar(msg):
674 | scalar = hash_to_field(msg, 1) with the parameters:
675 | DST: DST
676 | F: GF(Order()), the scalar field
677 | of the prime order subgroup of EC
678 | p: Order()
679 | m: 1
680 | L: as defined in H2C
681 | expand_message: as defined in H2C
682 | return scalar
683 | ~~~
684 |
685 | We illustrate also what would be needed instead for full compatibility with [I-D.draft-bradleylundberg-cfrg-arkg-02] below:
686 |
687 | ~~~
688 | def DeriveBlindingFactor_ARKG(bk, ctx):
689 | bf = HashToScalar_ARKG(msg, ctx)
690 | return bf
691 |
692 | def HashToScalar_ARKG(msg, info):
693 | scalar = hash_to_field(msg, 1) with the parameters:
694 | DST: DST || info
695 | F: GF(Order()), the scalar field
696 | of the prime order subgroup of EC
697 | p: Order()
698 | m: 1
699 | L: as defined in H2C
700 | expand_message: as defined in H2C
701 | return scalar
702 | ~~~
703 |
704 | ### Using ECDH shared secrets
705 |
706 | Instantiations of HDK using ECDH shared secrets use:
707 |
708 | - [elliptic curves](#using-elliptic-curves)
709 | - [multiplicative blinding](#using-multiplicative-blinding)
710 |
711 | Instantiations of HDK using ECDH shared secrets provide:
712 |
713 | - `DH`: The Elliptic Curve Key Agreement Algorithm - Diffie-Hellman (ECKA-DH) [TR03111] with elliptic curve `G`, consisting of the functions:
714 | - CreateSharedSecret(skX, pkY): Outputs a shared secret byte string `Z_AB` representing the x-coordinate of the Element `ScalarMult(pkY, skX)`.
715 |
716 | Note that DH enables an alternative way of authenticating a key pair `(sk, pk)` without creation or verification of a signature:
717 |
718 | ~~~
719 | # 1. Unit shares with reader: pk
720 |
721 | # 2. Reader computes:
722 | (skR, pkR) = GenerateKeyPair()
723 |
724 | # 3. Reader shares with unit: pkR
725 |
726 | # 4. Unit computes:
727 | Z_AB = CreateSharedSecret(sk, pkR)
728 |
729 | # 5. Reader computes:
730 | Z_AB = CreateSharedSecret(skR, pk)
731 | ~~~
732 |
733 | Now with the shared secret `Z_AB`, the unit and the reader can compute a secret shared key. The unit can convince the reader that it possesses `sk` for example by sharing a message authentication code created using this key. The reader can verify this by recomputing the code using its value of `Z_AB`. This is for example used in ECDH-MAC authentication defined in [ISO18013-5].
734 |
735 | 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.
736 |
737 | 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:
738 |
739 | ~~~
740 | bf = DeriveBlindingFactor(bk, ctx)
741 | sk' = BlindPrivateKey(sk, bk, ctx)
742 | = sk * bf mod Order()
743 | pk' = ScalarMult(pk, bf)
744 | ~~~
745 |
746 | In this case, the computation in step 4 can be performed as such:
747 |
748 | ~~~
749 | # 4. Unit computes:
750 | Z_AB = CreateSharedSecret(sk', pkR)
751 | = CreateSharedSecret(sk * bf mod Order(), pkR)
752 | = CreateSharedSecret(sk, ScalarMult(pkR, bf))
753 | ~~~
754 |
755 | Note that the value of `ScalarMult(pkR, bf)` does not need to be computed within the secure cryptographic device that protects `sk`.
756 |
757 | ### Using EC-SDSA signatures
758 |
759 | Instantiations of HDK using EC-SDSA (Schnorr) signatures use:
760 |
761 | - [additive blinding](#using-additive-blinding)
762 | - [digital signatures](#using-digital-signatures)
763 | - [elliptic curves](#using-elliptic-curves)
764 |
765 | Instantiations of HDK using EC-SDSA signatures provide:
766 |
767 | - `DSA`: An EC-SDSA digital signature algorithm [TR03111], representing signatures as pairs `(c, s)`.
768 |
769 | Note that in this case, the following definition is equivalent to the [original definition of BlindKeySign](#using-digital-signatures):
770 |
771 | ~~~
772 | def BlindKeySign(sk, bk, ctx, msg):
773 | # Compute signature within the secure cryptographic device.
774 | (c, s) = Sign(sk, msg)
775 |
776 | # Post-process the signature outside of this device.
777 | bf = DeriveBlindingFactor(bk, ctx)
778 | s' = s + c * bf mod Order()
779 |
780 | signature = (c, s')
781 | return signature
782 | ~~~
783 |
784 | ### Using P-256
785 |
786 | Instantiations of HDK using P-256 use:
787 |
788 | - [elliptic curves](#using-elliptic-curves)
789 |
790 | Instantiations of HDK using P-256 provide:
791 |
792 | - `G`: The NIST curve `secp256r1` (P-256) [SEC2].
793 | - `H2C`: P256_XMD:SHA-256_SSWU_RO_ [RFC9380], which uses SHA-256 [FIPS180-4] as `H`.
794 | - `KEM`: DHKEM(P-256, HKDF-SHA256) [RFC9180].
795 |
796 | # Concrete HDK instantiations
797 |
798 | The RECOMMENDED instantiation is the HDK-ECDH-P256. This avoids the risk of having the holder unknowingly producing a potentially non-repudiable signature over reader-provided data. Secure cryptographic devices that enable a high level of assurance typically support managing ECDH keys with the P-256 elliptic curve.
799 |
800 | ## HDK-ECDH-P256
801 |
802 | The HDK-ECDH-P256 instantiation of HDK uses:
803 |
804 | - [P-256](#using-p-256)
805 | - [ECDH shared secrets](#using-ecdh-shared-secrets)
806 |
807 | The HDK-ECDH-P256 instantiation defines:
808 |
809 | - `DST`: `"ECDH Key Blind"`
810 |
811 | ## HDK-ECDSA-P256add
812 |
813 | The HDK-ECDSA-P256add instantiation of HDK uses:
814 |
815 | - [digital signatures](#using-digital-signatures)
816 | - [P-256](#using-p-256)
817 | - [additive blinding](#using-additive-blinding)
818 |
819 | The HDK-ECDSA-P256add instantiation of HDK defines:
820 |
821 | - `DST`: `"ARKG-BL-EC.ARKG-P256ADD-ECDH"` for interoperability with [I-D.draft-bradleylundberg-cfrg-arkg-02].
822 | - `DSA`: ECDSA [TR03111] with curve `G`.
823 |
824 | ## HDK-ECDSA-P256mul
825 |
826 | The HDK-ECDSA-P256mul instantiation of HDK uses:
827 |
828 | - [digital signatures](#using-digital-signatures)
829 | - [P-256](#using-p-256)
830 | - [multiplicative blinding](#using-multiplicative-blinding)
831 |
832 | The HDK-ECDSA-P256mul instantiation of HDK defines:
833 |
834 | - `DST`: `"ECDSA Key Blind"` for interoperability with [I-D.draft-irtf-cfrg-signature-key-blinding-07].
835 | - `DSA`: ECDSA [TR03111] with curve `G`.
836 |
837 | ## HDK-ECSDSA-P256
838 |
839 | The HDK-ECSDSA-P256 instantiation of HDK uses:
840 |
841 | - [EC-SDSA signatures](#using-ec-sdsa-signatures)
842 | - [P-256](#using-p-256)
843 |
844 | The HDK-ECSDSA-P256 instantiation of HDK defines:
845 |
846 | - `DST`: `"EC-SDSA Key Blind"`
847 |
848 | # Application considerations
849 |
850 | ## Secure cryptographic device
851 |
852 | The HDK approach assumes that the holder controls a secure cryptographic device that protects the device key pair `(sk_device, pk_device)`. The device key is under sole control of the holder.
853 |
854 | In the context of [EU2024-1183], this device is typically called a Wallet Secure Cryptographic Device (WSCD), running a personalised Wallet Secure Cryptographic Application (WSCA) that exposes a Secure Cryptographic Interface (SCI) to a Wallet Instance (WI) running on a User Device (UD). The WSCD is certified to protect access to the device private key with high attack potential resistance to achieve high level of assurance authentication as per [EU2015-1502]. This typically means that the key is associated with a strong possession factor and with a rate-limited Personal Identification Number (PIN) check as a knowledge factor, and the verification of both factors actively involve the WSCD.
855 |
856 | An example deployment of HDK in this context is illustrated below.
857 |
858 | ~~~
859 | +---------------------+ +----------------------+
860 | |Issuer infrastructure| |User Device (UD) |
861 | | | | |
862 | |+-------------------+|OpenID4VCI|+--------------------+|
863 | ||Issuer service |<----------++Wallet Instance (WI)||
864 | || || |++-------------------+|
865 | ||Optionally an || +-+--------------------+
866 | ||ARKG subordinate || |Secure
867 | ||party || |Cryptographic
868 | |+-------------------+| |Interface (SCI)
869 | +---------------------+ +v-------------------+
870 | |Wallet Secure |
871 | |Cryptographic |
872 | Internal Manages |Application (WSCA) |
873 | registry <-----------+ |
874 | |Optionally an |
875 | |ARKG delegating |
876 | |party |
877 | ++-------------------+
878 | |Uses
879 | +v-------------------+
880 | Protects |Wallet secure |
881 | Device keys <-----------+cryptographic |
882 | |device (WSCD) |
883 | +--------------------+
884 | ~~~
885 |
886 | The WSCA could be a single program or could be deployed in a distributed architecture, as illustrated below.
887 |
888 | ~~~
889 | +--------------+
890 | |User device |
891 | |+------------+|
892 | ||WI ||
893 | |++-----------+|
894 | | |SCI |
895 | |+v-----------+|
896 | ||WSCA agent ||
897 | |++-----------+|
898 | +-+------------+
899 | |WSCA protocol
900 | +v-----------+
901 | |WSCA service|
902 | +------------+
903 | ~~~
904 |
905 | In the case of a distributed WSCA, the UD contains a local component, here called WSCA agent, accessing an external and possibly remote WSCA service from one or more components over a WSCA protocol. For example, the WSCA agent may be a local web API client and the WSCA service may be provided at a remote web API server. In such cases, typically the WSCA service receives a high-assurance security evaluation, while the WSCA agent is assessed to not be able to compromise the system's security guarantees.
906 |
907 | The internal registry can be managed by the WSCA agent, by the WSCA service, or by the combination. When the user device is a natural person’s mobile phone, WSCA agent management could provide better confidentiality protection against compromised WSCA service providers. When the user device is a cloud server used by a legal person, and the legal person deploys its own WSCD, WSCA service management could provide better confidentiality protection against compromised Wallet Instance cloud providers.
908 |
909 | In a distributed WSCA architecture, the WSCA could internally apply distributed key generation. A description of this is out of scope for the current document.
910 |
911 | The solution proposal discussed herein works in all any WSCD architecture that supports the required cryptographic primitives:
912 |
913 | - In the case of HDK-ECDH-P256 (see [HDK-ECDH-P256](#hdk-ecdh-p256)):
914 | - P-256 ECDH key pair generation
915 | - P-256 ECDH key agreement
916 | - In the case of HDK-ECDSA-P256mul (see [HDK-ECDSA-P256mul](#hdk-ecdsa-p256mul)):
917 | - P-256 ECDSA blinding key pair generation
918 | - P-256 ECDSA blinded signature creation
919 | - In the case of HDK-ECSDSA-P256 (see [HDK-ECSDSA-P256](#hdk-ecsdsa-p256)):
920 | - P-256 EC-SDSA key pair generation
921 | - P-256 EC-SDSA signature creation
922 |
923 | The other HDK operations can be performed in a WSCA or WSCA agent running on any UD, including hostile ones with limited sandboxing capabilities, such as in a smartphone's rich execution environment or in a personal computer web browser.
924 |
925 | ## Trust evidence
926 |
927 | Some issuers could require evidence from a solution provider of the security of the holder's cryptographic device. This evidence can in the context of [EU2024-1183] be divided into initial "Wallet Trust Evidence" and related "Issuer Trust Evidence". Each is a protected document that contains a trust evidence public key associated with a private key that is protected in the secure cryptographic device. With HDK, these public keys are specified as follows.
928 |
929 | ### Wallet Trust Evidence
930 |
931 | The Wallet Trust Evidence public key is the first level 0 HDK public key. To achieve reader unlinkability, the wallet SHOULD limit access to a trusted person identification document provider only.
932 |
933 | To prevent association across identities, the solution provider MUST before issuing Wallet Trust Evidence ensure that (a) a newly generated device key pair is used and (b) the wallet follows the protocol so that the HDK output is bound to exactly this key. For (a), the solution provider could rely on freshness of a key attestation and ensure that each device public key is attested only once. For (b), the wallet could proof knowledge of the blinding factor `bf` with a Schnorr non-interactive zero-knowledge proof [RFC8235] with base point `pk_device`. This would ensure that the root blinding key `bf` is not shared with the solution provider to reduce the risk of the solution provider unblinding future derived keys.
934 |
935 | ### Issuer Trust Evidence
936 |
937 | The Issuer Trust Evidence public key can be any other HDK public key. The solution provider MUST verify that the wallet knows the associated private key before issuing Issuer Trust Evidence. The solution provider MUST ensure that `sk_device` is under sole control of the unit holder. To achieve reader unlinkability, the unit MUST limit access of Issuer Trust Evidence to a single issuer. Subsequent issuers within the same HDK tree do not need to receive any Issuer Trust Evidence, since they can derive equally secure keys by applying the remote HDK protocol to presented keys attested by trusted (other) issuers.
938 |
939 | ## Applying HDK in OpenID for Verifiable Credential Issuance
940 |
941 | In [draft-OpenID4VCI], the following terminology applies:
942 |
943 | | OpenID4VCI | HDK |
944 | | ----------------- | ----------------- |
945 | | Credential | document |
946 | | Credential Issuer | issuer |
947 | | Verifier | reader |
948 | | Wallet | unit |
949 |
950 | HDK enables unit and issuers cooperatively to establish the cryptographic key material that issued documents will be bound to.
951 |
952 | 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.
953 |
954 | ## Applying HDK with ARKG
955 |
956 | 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).
957 |
958 | ~~~
959 | def DeriveSeed(ikm, (skD, bf), pk):
960 | (skR, pkR) = DeriveKeyPair(ikm)
961 | skA = (skR, (skD, bf, pk))
962 | pkA = (pkR, pk)
963 | return (skA, pkA)
964 |
965 | def DerivePublicKey((pkR, pk), index):
966 | (salt_kem, kh) = Encap(pkR)
967 |
968 | bk = DeriveBlindKey(salt_kem)
969 | ctx = CreateContext(pk, index)
970 | pk' = BlindPublicKey(pk, bk, ctx)
971 |
972 | return (pk', kh)
973 |
974 | def DerivePrivateKey((skR, (skD, bf, pk)), (pk', kh), index):
975 | salt_kem = Decap(kh, skR)
976 |
977 | bk = DeriveBlindKey(salt_kem)
978 | ctx = CreateContext(pk, index)
979 | pkE = BlindPublicKey(pk, bk, ctx)
980 |
981 | if pk' != pkE: abort with an error
982 |
983 | sk = Combine(skD, bf)
984 | sk' = BlindPrivateKey(sk, bk, ctx)
985 |
986 | return sk'
987 | ~~~
988 |
989 | This enables the [remote HDK protocol](#the-remote-hdk-protocol) to be performed as such, given an `index` known to both parties:
990 |
991 | ~~~
992 | # 1. Unit computes:
993 | (skA, pkA) = DeriveSeed(salt, (skD, bf), pk)
994 |
995 | # 2. Unit shares with issuer: pkA
996 |
997 | # 3. Issuer computes:
998 | (pk', kh) = DerivePublicKey(pkA, index)
999 |
1000 | # 4. Issuer shares with unit: (pk', kh)
1001 |
1002 | # 5. Unit verifies integrity and computes the private key:
1003 | sk' = DerivePrivateKey(skA, (pk', kh), index)
1004 | ~~~
1005 |
1006 | For using a single `kh` with multiple values of `index`, the DerivePublicKey needs to be refactored to be able to reuse the Encap output.
1007 |
1008 | # Security considerations
1009 |
1010 | ## Confidentiality of key handles
1011 |
1012 | The key handles MUST be considered confidential, since they provide knowledge about the blinding factors. Compromise of this knowledge could introduce undesired linkability. In HDK, both the holder and the issuer know the key handle during issuance.
1013 |
1014 | In an alternative to HDK, the holder independently generates blinded key pairs and proofs of association, providing the issuer with zero knowledge about the blinding factors. However, this moves the problem: the proofs of association would now need to be considered confidential.
1015 |
1016 | --- back
1017 |
1018 | # Acknowledgements
1019 | {:numbered="false"}
1020 |
1021 | This design is based on ideas introduced to the EU Digital Identity domain by Peter Lee Altmann.
1022 |
1023 | Helpful feedback came from Emil Lundberg, John Bradley and Remco Schaar.
1024 |
--------------------------------------------------------------------------------
/feedback-poa.md:
--------------------------------------------------------------------------------
1 | # Feedback to resolve HDK and PoA issues in the ARF
2 |
3 | **Editor:** Sander Dijkhuis (Cleverbase)
4 |
5 | **License:** [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
6 |
7 | ## Context
8 |
9 | The binding of attestation (PID, QEAA, PuB-EAA, EAA) keys to critical assets in certified WSCDs, and the ability to prove this binding is critical for security of EUDI wallets. Relying party unlinkability is critical to its privacy. Several experts are proposing solutions that could work for the first wallet deployments.
10 |
11 | The Commission and standardisation organisations are looking into two documents related to these subjects:
12 |
13 | - [Attestation Proof of Association](https://eprint.iacr.org/2024/1444) (PoA)
14 | - [Hierarchical Deterministic Keys](README.md) (HDK)
15 |
16 | The first is prepared in collaboration between experts from Member State. The second is prepared in an informal collaboration between experts from public and private sector participants in Large Scale Pilots. Both PoA and HDK propose concrete short-term considerations in two areas:
17 |
18 | - implementation of the wallet solution internally
19 | - interoperability between wallet units and other entities
20 |
21 | Both of these types are relevant to security and interface requirements in law and standards. This document analyses the common ground and contentious issues and proposes next steps.
22 |
23 | ## Implementation considerations
24 |
25 | ### Common grounds on implementation
26 |
27 | #### Verifiable association of public keys to a single attested WSCD
28 |
29 | Both docs suggest applications of key blinding for unlinkability between presentations, based on a single WSCD key pair. At least one trusted issuer should receive trust evidence issued by the wallet provider, which attests this WSCD key pair, potentially with blinding. This is an important requirement to protect against unauthorised use of attestations.
30 |
31 | To follow up:
32 |
33 | - Ensure the ARF and implementing acts continue to contain this requirement.
34 |
35 | #### Distributed WSCA deployment
36 |
37 | Both docs suggest that a WSCA can be distributed across the user device and an external, possibly remote WSCD. They apply threshold signatures or multiple-ECDH schemes, performing the critical security functions in an off-the-shelf WSCD. This may be the only way to meet the regulation deadlines, since developing and certifying new WSCDs will take too much time in practice.
38 |
39 | Related ARF issue: [#283](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/issues/283). Method to resolve the issue:
40 |
41 | - Update the ARF and implementing act text to acknowledge that the WSCA does not need to be hosted on the WSCD, but can for example be distributed.
42 |
43 | ### Contentious issues on implementation
44 |
45 | #### Patent risks to wallet providers
46 |
47 | In practice, EUDI wallets would use either ECDSA or ECDH-MAC, which are widely supported by off-the-shelf WSCDs and can be applied in a distributed WSCA deployment. Several patent claims seem to apply to the new and innovative ECDSA option. While HDK could be extended to support ECDSA, it is left out of the doc to leave any commercial interests out of the informal cross-LSP working group. If ECDSA becomes the de-facto standard, this would require all wallet providers using off-the-shelf WSCDs to deal with these patents. Experts so far do not see similar risk with ECDH-MAC.
48 |
49 | Related ARF issue: [#286](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/issues/286). Options to resolve this issue:
50 |
51 | - Investigate the patents and potentially negotiate licensing conditions at the EU-level.
52 | - Mandate the use of at least ECDH-MAC to enable at least one open standards-based route.
53 | - Leave the risk to the individual wallet providers.
54 |
55 | ## Interoperability considerations
56 |
57 | ### Common grounds on interoperability
58 |
59 | #### Cryptographic proofs of association to relying parties
60 |
61 | Both approaches enable the construction of a cryptographic proof of association based on zero-knowledge proofs. This enables proving association between attestations towards relying parties. The method is complementary to other methods for proof of association. These should co-exist since parties are also working on WSCAs that cannot create the zero-knowledge proof but will have other certified ways to assure relying parties of association. One question that is not yet addressed explicitly is whether the proof of association may be non-repudiable, or if designated verifier protocols are needed for plausible deniability.
62 |
63 | To follow up:
64 |
65 | - Require attestation protocols to provide sufficient flexiblity to support zero-knowledge proof of association.
66 | - with an advisory group, analyse pros and cons of having non-repudiable versus plausibly deniable proof of association.
67 |
68 | ### Contentious issues on interoperability
69 |
70 | #### Proof of association requirements to attestation providers
71 |
72 | Both PoA and HDK convince attestation providers that a newly generated key is associated with a previously attested key. But the methods are different:
73 |
74 | - PoA provides the same zero-knowledge proof as to relying parties.
75 | - HDK applies key share agreement with the wallet unit, which directly proves to the attestation provider association with a previously presented key.
76 |
77 | There is a tradeoff to make. Arguably, HDK is simpler and enables other features such as delegated key share generation. However, it is not in zero-knowledge, which at least theoretically reduces confidentiality. So far, no concrete abuse scenarios are known.
78 |
79 | Options to resolve the issue:
80 |
81 | - Leave the ARF and implementing acts open towards the method of proving association.
82 | - With an advisory group, analyse the pros and cons of each approach and select one.
83 |
84 | #### Delegated key share generation
85 |
86 | By applying key share agreement, HDK can delegate the generation of additional association key shares to the attestation provider. This enables more efficient batch re-issuance. For example, the provider could periodically issue a new batch, without wallet unit interaction, while still being sure that the new public keys are bound to the same WSCD. In the PoA approach, this would generate periodic interaction with the wallet unit, or the wallet unit should pre-generate a high number of keys and proofs of association.
87 |
88 | Related ARF issue: [#284](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/issues/284). Options to resolve the issue:
89 |
90 | - Update the ARF and implementing acts to enable delegated key share generation.
91 | - With an advisory group, analyse concrete re-issuance scenarios and decide whether the pros outweigh the cons.
92 |
93 | #### Use of implicit trust evidence
94 |
95 | One driver for development of HDK is to enable attestation providers to rely on PID presentation not only for identity verification, but also for assurance of WSCD binding. If the attestation provider can generate a new public key based on a previously presented PID key, they do not need additional trust evidence. This could simplify the process for non-PID issuers and reduce dependence on wallet provider availability. The current ARF and implementing act texts instead make the use of trust evidence mandatory, and the PoA document focuses on methods to apply this trust evidence in combination with proof of association.
96 |
97 | Related ARF issues: [#285](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/issues/285), [#286](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/issues/286). Options to resolve the issue:
98 |
99 | - Update the ARF and implementing acts to enable the use of implicit trust evidence.
100 | - With an advisory group, analyse pros and cons of each approach to the verify-PID-then-issue-EAA use case.
101 | - Leave explicit trust evidence mandatory, even when it is not needed for attestation issuers.
102 |
103 | ## Proposed next steps
104 |
105 | 1. Discuss these issues with the Commission and ARF authors for shared understanding, and triage if some may have already been resolved.
106 | 2. Organise an ad-hoc technical advisory group to analyse the remaining issues.
107 | 3. Present the results to an expert group for Member State feedback.
108 | 4. Record the conclusions and any remaining open issues in the ARF project on GitHub.
109 |
--------------------------------------------------------------------------------
/feedback.md:
--------------------------------------------------------------------------------
1 | # Feedback to enable Hierarchical Deterministic Keys in the Wallet Toolbox
2 |
3 | **Version:** 0.2.0-SNAPSHOT
4 |
5 | **Editor:** Sander Dijkhuis (Cleverbase)
6 |
7 | **License:** [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
8 |
9 | **Discussion:** [eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework#282](https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/discussions/282)
10 |
11 | ## Context
12 |
13 | For a general introduction, see [Hierarchical Deterministic Keys for the European Digital Identity Wallet](README.md). In the current document, the authors develop and share structured feedback on one part of the Wallet Toolbox: the [Architecture and Reference Framework](https://eu-digital-identity-wallet.github.io/eudi-doc-architecture-and-reference-framework/latest/arf/) (ARF). The purpose of this feedback is to enable implementation of [Hierarchical Deterministc Keys](draft-dijkhuis-cfrg-hierarchical-deterministic-keys.md) (HDKs).
14 |
15 | By enabling Hierarchical Deterministic Keys, we aim for interoperability with a concrete and desirable cryptographic architecture in the context of person identification data and some (qualified) electronic attestations of attributes. We do not suggest to mandate the application of this cryptographic architecture for all digital identity documents. Instead, we aim to address two risks to the ARF and subsequently the implementing acts: the risk of accidentally disabling desirable technical solutions, and the risk of accidentally requiring undesirable technical solutions.
16 |
17 | > [!NOTE]
18 | > This feedback document is a first version, and requires detailed exchanges to understand each other’s detailed points of view. This information is shared by participants of the [Digital Credentials for Europe (DC4EU) Consortium](https://www.dc4eu.eu), the [EU Digital Identity Wallet Consortium (EWC)](https://eudiwalletconsortium.org), and the [Potential Consortium](https://www.digital-identity-wallet.eu). Views and opinions expressed are those of the authors only and do not necessarily reflect those of all consortium members.
19 |
20 | ## Feedback on the high level requirements
21 |
22 | The original requirement texts are copied from ARF 1.4.0.
23 |
24 | ### Topic 9 - Wallet Trust Evidence
25 |
26 | This topic currently specifies a very specific technical solution. To be applicable to HDKs, the requirements need to be either at a higher level, or more open to different technical solutions. The feedback below proposes more open requirements in Topic 9.
27 |
28 | |Index|Proposed change|Rationale|
29 | |--|--|--|
30 | |WTE_*|Split WTE requirements into WTE requirements and Issuer Trust Evidence (ITE) requirements. Limit the WTE audience to authorised Person Identification Data (PID) Providers. Make requesting ITE optional to other providers.|Splitting requirements makes explicit the associated security and privacy conditions. HDK splits the WTE and ITE solutions as well. Splitting the requirements does not preclude solutions other than HDK from applying a single solution to create both WTE and ITE.|
31 | |WTE_01–09|None.|N/A|
32 | |WTE_10|Modify: “A WSCA SHALL generate a new key pair for a new WTE on request of the Wallet Provider via the Wallet Instance. The WSCA Wallet Instance SHALL register a unique identifier of the new key as a WTE key in an internal registry. The WSCA Wallet Instance SHALL register the WTE key as an independent (i.e., non-associated) key in an internal registry.”
Add: “A Wallet Instance SHALL generate a new key pair for a new ITE on request of the Wallet Provider, with the same WSCA-enforced access controls (see WTE_02) as a valid WTE key. The Wallet Instance SHALL register the new key in an internal registry. The Wallet Instance SHALL register the ITE key as associated with the WTE in an internal registry.”|This change is essential to the HDK architecture, where the WSCA is responsible only for device key pairs, and the other keys are managed as HDKs within the Wallet Instance. This change does not preclude solutions other than HDK from having the Wallet Instance delegate this functionality to a WSCA.|
33 | |WTE_10–11|None.|N/A|
34 | |WTE_13|Modify: “During PID or attestation issuance, a WSCA Wallet Instance SHALL generate a new key pair for a new PID or attestation, on request of the PID Provider or Attestation Provider via the Wallet Instance. The attestation key MUST be protected using the same WSCA-enforced access controls (see WTE_02) as a valid WTE key. The Wallet Instance MAY delegate key generation to the PID Provider or Attestation Provider. The PID Provider or Attestation Provider SHALL indicate a single WTE public key (see WTE_10) with which the new PID or attestation key must be associated, along with data identifying the method to be used for association. This indication can either be direct, by providing the WTE public key value, or indirect, by providing a public key value that has been associated with the WTE key previously. In the latter case, the WSCA SHALL look up the associated WTE key in its internal registry.
The WSCA Wallet Instance SHALL register the new key in an internal registry as an attestation key. The WSCA Wallet Instance SHALL register the association between the new private key and the WTE private key in an internal registry.”|In HDK, the Wallet Provider, PID Provider, or Attestation Provider may asynchronously, remotely generate batches of single-use keys. These keys are under sole control of the User by association to a WTE key. Other proposed modifications are required to remove non-essential implementation details and thereby to enable HDK as in WTE_10.|
35 | |WTE_14|Modify: “During PID or attestation issuance, a WSCA Wallet Instance SHALL prove possession of the private key corresponding to the new PID or attestation public key, or, in the case of delegated key generation, by proving possession of the key indicated in WTE_13, on request of the PID Provider or Attestation Provider via the Wallet Instance, for example by signing a challenge with that private key. Note that by design, this proof of possession implies that the WSCA has authenticated the user.”|In HDK, a single proof of possession to the PID or (Q)EAA Provider is sufficient to enable multiple unique one-time-use keys.|
36 | |WTE_15–16|None.|N/A|
37 | |WTE_17|Modify: “During PID or attestation issuance, a WSCA Wallet Instance SHALL prove possession of the private key corresponding to a WTE public key on request of a PID Provider or Attestation Provider via the Wallet Instance, for example by signing a challenge with that private key.”|With HDK, there is no need for roles other than PID Providers to learn wallet metadata. Therefore, disclosing an ITE is no requirement for issuance. Users may choose to disclose ITE data as part of releasing attributes, which is sufficiently covered by other requirements.|
38 | |WTE_18|Modify: “During PID or attestation issuance, a WSCA Wallet Instance SHOULD generate a proof of association for two or more public keys whenever two or more public keys are disclosed, if and only if the corresponding private keys are protected by the same WSCA and the WSCA Wallet Instance has internally registered an association between these private keys.”|In HDK, typically only one public key is provided for issuance. In such cases, a proof of association is not applicable.|
39 | |WTE_19|Modify: “During PID or attestation issuance, the PID Provider or Attestation Provider SHALL verify that:
• The WSCA Wallet Instance described in the WTE, if any, or ITE, if any, received from the Wallet Instance has proven possession of the private key corresponding to the public key in the WTE or ITE (see WTE_17),
•The WSCA has proven possession of Wallet Instance possesses the new PID or attestation private key (see WTE_14)
In addition, the PID Provider or Attestation Provider SHOULD verify that:
• The WSCA has proven association (see WTE_18) between protects with the same access controls the new PID or attestation public key and the public key requested by the PID Provider or Attestation Provider according to WTE_15 or WTE_16.”|In HDK, the PID Provider or Attestation Provider can apply HDK instead of a proof of association to verify equivalent WSCA protection between two keys.|
40 | |WTE_20|Modify: “During PID or attestation issuance, a Wallet Instance SHALL provide the PID Provider or Attestation Provider with the WTE describing the properties of the WSCA that generated the new PID or attestation private key and protects it.”|In HDK, only PID Providers require WTE, and Attestation Providers do not require WTE or ITE. This modification does not preclude other technical solutions from still providing ITE to Attestation Providers over the same interface.|
41 | |WTE_21–22|None.|N/A|
42 | |WTE_23|Modify: “The common OpenID4VCI protocol defined in requirement ISSU_01 SHALL enable a Wallet Instance to transfer a WTE to a PID Provider or Attestation Provider.”|See WTE_20.|
43 | |WTE_24|Modify: “A Wallet Instance SHALL release a WTE only to a PID Provider or Attestation Provider, and not to a Attestation Provider or Relying Party or any other party.”
Add: “A Wallet Instance SHALL release a ITE only to an Attestation Provider, and not to a Relying Party or any other party.”|See WTE_20.|
44 | |WTE_25–26|None.|N/A|
45 | |WTE_27|Add: “The common OpenID4VCI protocol SHALL enable a PID Provider or Attestation Provider to indicate in the Token Response:
• in the case of delegated key generation, data enabling the Wallet Instance to prove possession of the private key associated with the new PID or attestation key”|This is essential to enable application of HDK to batch issuance.|
46 | |WTE_28–30|None.|N/A|
47 | |WTE_31|A WSCA Wallet Instance SHALL register each newly generated key pair as either a WTE key or an attestation key, depending on information provided by the Wallet Instance in the key generation request. The internal registry used by the WSCA Wallet Instance for this purpose SHALL be protected against modification by external parties.|See WTE_10.|
48 | |WTE_32|None.|N/A|
49 | |WTE_33|Modify: “A WSCA Wallet Instance SHALL associate each newly generated attestation key with a WTE key indicated by the Wallet Instance. The WSCA Wallet Instance SHALL record the association between these keys in an internal registry, which SHALL be protected against modification by external parties.”|In HDK, the Wallet Instance maintains the associations.|
50 | |WTE_34|Drop.|This is an implementation detail only to some technical solutions.|
51 | |WTE_35|A WSCA Wallet Instance SHALL consider two keys to be associated if they are associated to a common WTE key.|See WTE_10.|
52 | |WTE_36|Modify: “A WSCA Wallet Instance SHOULD be able to generate a proof of association for two or more public keys. The WSCA Wallet Instance SHALL generate such a proof if and only if the corresponding private keys are protected by that a single WSCA, and the WSCA Wallet Instance has internally registered an association between these private keys.”|In HDK, the Wallet Instance manages such associations. This does not preclude solutions other than HDK to delegate this to the WSCA.|
53 |
54 | ### Topic 18 - Relying Party handling EUDI Wallet attribute combined presentation
55 |
56 | With HDK, document readers do not need the proof of association, as they may instead rely on attributes attested by issuers. Since the issuers apply HDK, they can for example ensure binding to PID.
57 |
58 | To enable HDK, the following changes to Topic 18 are needed.
59 |
60 | |Index|Proposed change|Rationale|
61 | |--|--|--|
62 | |ACP_01–03|None.|N/A|
63 | |ACP_04|Drop or modify: “If (as a result of ACP_03) a Wallet Instance determines it must release multiple attestations to a Relying Party in a combined presentation of attributes, it SHALL requestMAY generate a proof of association between the public keys of these attestations from the WSC.”|Proof of association is not necessary in the case of attribute-based binding. It could introduce disproportional complications. For example, depending on the proof mechanism, this could produce a potentially non-repudiable proof that a certain combination of documents was revealed. Also, by disclosing public keys related to blinding scalars, it will be more difficult for solutions to guarantee unconditional privacy.|
64 | |ACP_05|Drop.|It is up to the User whether to authorise sharing a proof of association with another party.|
65 | |ACP_06–08|None.|N/A|
66 | |ACP_09|Drop.|With HDK, proof of association is not essential.|
67 |
--------------------------------------------------------------------------------
/prototype.demo.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:demo (:use #:common-lisp #:prototype))
2 | (in-package #:demo)
3 |
4 | (defvar *wallet* (make-unit +hdk-ecdh-p256+))
5 | (defvar *evidence* (activate *wallet*))
6 |
7 | ;; Present wallet trust evidence to the PID provider
8 | (let* ((reader (make-reader +ecdh-p256+))
9 | (device-data (prove-possession *wallet* *evidence* (pk reader))))
10 | (assert (verify reader *evidence* device-data)))
11 |
12 | ;; Request issuance using remote key derivation
13 | (defvar *pk-kem* (request *wallet* *evidence*))
14 |
15 | ;; Create a key handle and issue a first batch of PID
16 | (defvar *kh*)
17 | (defvar *pid*)
18 | (multiple-value-bind (salt kh) (encap (kem *wallet*) *pk-kem*)
19 | (setf *kh* kh)
20 | (setf *pid* (loop for i in '(0 1 2 3)
21 | collect (make-document (hdk *wallet*) *evidence* salt i))))
22 |
23 | ;; Accept the first batch of PID, using synchronised indices
24 | ;; (synchronisation is implicit: easy upon first batch)
25 | (loop for i in '(0 1 2 3)
26 | for doc in *pid*
27 | do (accept *wallet* *evidence* *kh* i doc))
28 |
29 | ;; Present PID to various readers
30 | (loop for doc in *pid* do
31 | (let* ((reader (make-reader +ecdh-p256+))
32 | (device-data (prove-possession *wallet* doc (pk reader))))
33 | (assert (verify reader doc device-data))))
34 |
35 | (format t "Demo finished~%")
36 |
--------------------------------------------------------------------------------
/prototype.lisp:
--------------------------------------------------------------------------------
1 | (defpackage #:prototype
2 | (:export #:kem #:encap
3 | #:hdk
4 | #:make-unit #:activate #:prove-possession #:request #:accept
5 | #:make-reader #:pk #:verify
6 | #:make-document
7 | #:+hdk-ecdh-p256+ #:+ecdh-p256+)
8 | (:use #:common-lisp)
9 | (:import-from #:crypto
10 | #:+secp256r1-l+ #:+secp256r1-g+ #:EC-Scalar-Mult #:EC-Point-Equal))
11 |
12 | (in-package #:prototype)
13 |
14 | (defun concat (&rest bs) (apply #'concatenate
15 | '(vector (unsigned-byte 8)) bs))
16 | (defun i2osp (i n) (crypto:integer-to-octets i :n-bits (* n 8)))
17 | (defun os2ip (os) (crypto:octets-to-integer os))
18 | (defun strxor (s1 s2) (map 'crypto::simple-octet-vector #'logxor s1 s2))
19 | (defun ascii (s) (crypto:ascii-string-to-byte-array s))
20 | (defun read-bytes (&rest hex-strings)
21 | (read-from-string (apply #'concatenate 'string "#x" hex-strings)))
22 |
23 | (defclass ec () ((id :reader id :initarg :id)
24 | (n :reader order :initarg :n)
25 | (g :reader base :initarg :g)))
26 | (defclass ec-kg (ec) ())
27 |
28 | (defclass h () ((id :reader id :initarg :id)))
29 | (defclass h2c () ((ec :reader ec :initarg :ec)
30 | (h :reader hash :initarg :h)
31 | (dst :reader dst :initarg :dst)))
32 |
33 | (defclass dh () ((n-dh :reader output-length :initarg :n-dh)))
34 | (defclass ec-dh (dh) ((ec :reader ec :initarg :ec)))
35 |
36 | (defclass bl () ((id :reader id :initarg :id)))
37 | (defclass ec-bl (bl) ((ec :reader ec :initarg :ec)
38 | (h :reader hash :initarg :h)))
39 | (defclass ec-bl-mul (ec-bl) ())
40 | (defclass ec-bl-mul-dh (ec-bl-mul) ((ec-dh :reader ec-dh :initarg :ec-dh)))
41 |
42 | (defclass hmac () ((id :reader id :initarg :id)))
43 |
44 | (defclass hkdf () ((hmac :reader hmac :initarg :hmac)))
45 |
46 | (defclass hdk () ((id :reader id :initarg :id)
47 | (bl :reader bl :initarg :bl)
48 | (kem :reader kem :initarg :kem)
49 | (n-s :reader seed-length :initarg :n-s)
50 | (h :reader hash :initarg :h)))
51 |
52 | (defclass kem () ((n-secret :reader secret-length :initarg :n-secret)
53 | (n-sk :reader private-key-length :initarg :n-sk)
54 | (id :reader id :initarg :id)
55 | (bitmask :reader bitmask :initarg :bitmask)))
56 | (defclass dhkem (kem) ((dh :reader dh :initarg :dh)
57 | (hkdf :reader hkdf :initarg :hkdf)))
58 | (defclass ec-dhkem (dhkem) ((ec :reader ec :initarg :ec)))
59 |
60 | (defmethod h ((h h) &rest bs)
61 | (loop with hash = (crypto:make-digest (id h))
62 | for b in bs do (crypto:update-digest hash b)
63 | finally (return (crypto:produce-digest hash))))
64 | (defmethod expand-message-xmd ((h2c h2c) msg dst len)
65 | (loop with dst = (concat dst (i2osp (length dst) 1))
66 | with b = (make-array len :fill-pointer 0)
67 | with b0 = (h (hash h2c) (i2osp 0 64) msg (i2osp len 2) (i2osp 0 1) dst)
68 | for i from 1 upto (ceiling (/ len 32))
69 | for bi = (h (hash h2c) b0 (i2osp 1 1) dst)
70 | then (h (hash h2c) (strxor b0 bi) (i2osp i 1) dst)
71 | do (loop for j across bi do (vector-push j b))
72 | finally (return (coerce b 'crypto::simple-octet-vector))))
73 | (defmethod hash-to-field ((h2c h2c) &rest msg)
74 | (mod (os2ip (expand-message-xmd h2c
75 | (apply #'concat msg) (dst h2c) 48))
76 | (order (ec h2c))))
77 |
78 | (defmethod random-scalar ((ec ec)) (1+ (crypto:strong-random (1- (order ec)))))
79 | (defmethod scalar-mult ((ec ec) el k) (crypto:ec-scalar-mult el k))
80 | (defmethod scalar-base-mult ((ec ec) k) (scalar-mult ec +secp256r1-g+ k))
81 |
82 | (defmethod generate-key-pair ((ec ec-kg))
83 | (let ((sk (random-scalar ec))) (values sk (scalar-base-mult ec sk))))
84 | (defmethod serialize-public-key ((ec ec-kg) pk)
85 | (concat (i2osp (getf (crypto:ec-destructure-point pk) :x) 32)
86 | (i2osp (getf (crypto:ec-destructure-point pk) :y) 32)))
87 | (defmethod deserialize-public-key ((ec ec-kg) b)
88 | (crypto:ec-make-point (id ec) :x (os2ip (subseq b 0 32))
89 | :y (os2ip (subseq b 32))))
90 |
91 | (defmethod create-shared-secret ((dh ec-dh) sk-x pk-y)
92 | (i2osp (getf (crypto:ec-destructure-point (scalar-mult (ec dh) pk-y sk-x)) :x)
93 | (output-length dh)))
94 |
95 | (defmethod derive-blind-key ((bl ec-bl) ikm)
96 | (let ((h2c (make-instance 'h2c :ec (ec bl) :h (hash bl) :dst (id bl))))
97 | (i2osp (hash-to-field h2c ikm) 32)))
98 | (defmethod derive-blinding-factor ((bl ec-bl) bk ctx)
99 | (let ((h2c (make-instance 'h2c :ec (ec bl) :h (hash bl) :dst (id bl))))
100 | (hash-to-field h2c bk '(#x00) ctx)))
101 | (defmethod combine ((bl ec-bl-mul) bf1 bf2)
102 | (mod (* bf1 bf2) (order (ec bl))))
103 | (defmethod blind-public-key ((bl ec-bl-mul) pk-s bk ctx)
104 | (scalar-mult (ec bl) pk-s (derive-blinding-factor bl bk ctx)))
105 | (defmethod blind-dh ((bl ec-bl-mul-dh) sk-x bf pk-y)
106 | (create-shared-secret (ec-dh bl) sk-x (scalar-mult (ec bl) pk-y bf)))
107 |
108 | (defmethod create-context ((hdk hdk) index) (concat (id hdk) (i2osp index 4)))
109 | (defmethod derive-salt ((hdk hdk) salt ctx) (h (hash hdk) (id hdk) salt ctx))
110 | (defmethod hdk-apply ((hdk hdk) index pk salt &optional bf)
111 | (let* ((bk (derive-blind-key (bl hdk) salt))
112 | (ctx (create-context hdk index)))
113 | (values (blind-public-key (bl hdk) pk bk ctx)
114 | (derive-salt hdk salt ctx)
115 | (let ((bf2 (derive-blinding-factor (bl hdk) bk ctx)))
116 | (if bf (combine (bl hdk) bf bf2) bf2)))))
117 |
118 | (defmethod mac ((hmac hmac) key &rest bs)
119 | (loop with mac = (crypto:make-mac :hmac key (id hmac))
120 | for b in bs do (crypto:update-mac mac b)
121 | finally (return (crypto:produce-mac mac))))
122 |
123 | (defmethod extract ((hkdf hkdf) salt ikm) (mac (hmac hkdf) salt ikm))
124 | (defmethod expand ((hkdf hkdf) prk info len)
125 | (loop with tb = (make-array len :fill-pointer 0)
126 | for i from 1 upto (ceiling (/ len 32))
127 | for ti = (mac (hmac hkdf) prk (concat info (i2osp i 1)))
128 | then (mac (hmac hkdf) prk (concat ti info (i2osp i 1)))
129 | do (loop for j across ti do (vector-push j tb))
130 | finally (return (coerce tb '(vector (unsigned-byte 8))))))
131 |
132 | (defmethod labeled-extract ((kem dhkem) salt label ikm)
133 | (extract (hkdf kem) salt (concat (ascii "HPKE-v1") (id kem)
134 | (ascii label) ikm)))
135 | (defmethod labeled-expand ((kem dhkem) prk label info length)
136 | (expand (hkdf kem)
137 | prk
138 | (concat (i2osp length 2) (ascii "HPKE-v1") (id kem)
139 | (ascii label) info)
140 | length))
141 | (defmethod extract-and-expand ((kem kem) dh kem-context)
142 | (let ((eae-prk (labeled-extract kem (ascii "") "eae_prk" dh)))
143 | (labeled-expand
144 | kem eae-prk "shared_secret" kem-context (secret-length kem))))
145 | (defmethod derive-key-pair ((kem ec-dhkem) ikm)
146 | (loop with dkp-prk = (labeled-extract kem (ascii "") "dkp_prk" ikm)
147 | for counter from 0 upto 254
148 | for bytes = (labeled-expand kem dkp-prk "candidate" (i2osp counter 1)
149 | (private-key-length kem))
150 | for sk = (progn
151 | (setf (aref bytes 0) (logand (aref bytes 0) (bitmask kem)))
152 | (os2ip bytes))
153 | when (not (= sk 0)) return (values sk (scalar-base-mult (ec kem) sk))))
154 | (defmethod encap ((kem ec-dhkem) pk-r)
155 | (multiple-value-bind (sk-e pk-e) (generate-key-pair (ec kem))
156 | (let* ((dh (create-shared-secret (dh kem) sk-e pk-r))
157 | (enc (serialize-public-key (ec kem) pk-e))
158 | (pk-rm (serialize-public-key (ec kem) pk-r))
159 | (kem-context (concat enc pk-rm))
160 | (shared-secret (extract-and-expand kem dh kem-context)))
161 | (values shared-secret enc))))
162 | (defmethod decap ((kem ec-dhkem) enc sk-r)
163 | (let* ((pk-e (deserialize-public-key (ec kem) enc))
164 | (dh (create-shared-secret (dh kem) sk-r pk-e))
165 | (pk-rm (serialize-public-key (ec kem)
166 | (scalar-base-mult (ec kem) sk-r)))
167 | (kem-context (concat enc pk-rm))
168 | (shared-secret (extract-and-expand kem dh kem-context)))
169 | shared-secret))
170 |
171 | (defconstant +sha256+
172 | (make-instance 'h :id :sha256))
173 | (defconstant +p256+
174 | (make-instance 'ec-kg :n +secp256r1-l+ :g +secp256r1-g+ :id :secp256r1))
175 | (defconstant +ecdh-p256+
176 | (make-instance 'ec-dh :n-dh 32 :ec +p256+))
177 | (defconstant +bl-ecdh-p256+
178 | (make-instance 'ec-bl-mul-dh :id (ascii "ECDH Multiplicative Key Blind")
179 | :ec +p256+
180 | :ec-dh +ecdh-p256+
181 | :h +sha256+))
182 | (defconstant +hmac-sha256+
183 | (make-instance 'hmac :id :sha256))
184 | (defconstant +hkdf-sha256+
185 | (make-instance 'hkdf :hmac +hmac-sha256+))
186 | (defconstant +dhkem-p256-hkdf-sha256+
187 | (make-instance 'ec-dhkem :id (concat (ascii "KEM") (i2osp #x0010 2))
188 | :n-secret 32
189 | :n-sk 32
190 | :bitmask #xff
191 | :dh +ecdh-p256+
192 | :hkdf +hkdf-sha256+
193 | :ec +p256+))
194 | (defconstant +hdk-ecdh-p256+
195 | (make-instance 'hdk :id (ascii "HDK-ECDH-P256-v1")
196 | :bl +bl-ecdh-p256+
197 | :kem +dhkem-p256-hkdf-sha256+
198 | :n-s 32
199 | :h +sha256+))
200 |
201 | (defmethod fold ((hdk hdk) path pk salt &optional bf)
202 | (cond ((null path) (values pk bf salt))
203 | ((typep (car path) 'number)
204 | (multiple-value-bind (pk salt bf)
205 | (hdk-apply hdk (car path) pk salt bf)
206 | (fold hdk (cdr path) pk salt bf)))
207 | (t (let* ((sk-r (derive-key-pair (kem hdk) salt))
208 | (salt (decap (kem hdk) (car path) sk-r)))
209 | (fold hdk (cdr path) pk salt bf)))))
210 |
211 | (defclass document () ((pk :reader pk :initarg :pk)))
212 | (defun make-document (hdk doc salt index)
213 | (make-instance 'document :pk (hdk-apply hdk index (pk doc) salt)))
214 |
215 | (defclass app ()
216 | ((hdk :reader hdk :initarg :hdk)
217 | (device :reader device :initarg :device)
218 | (seed :reader seed :initarg :seed)))
219 | (defun make-app (hdk)
220 | (make-instance 'app
221 | :hdk hdk
222 | :device (multiple-value-list (generate-key-pair (ec (bl hdk))))
223 | :seed (crypto:random-data (seed-length hdk))))
224 | (defun sk-device (app) (car (device app)))
225 | (defun pk-device (app) (cadr (device app)))
226 | (defun fold-hdk (app hdk) (fold (hdk app) hdk (pk-device app) (seed app)))
227 | (defun get-key-info (app hdk)
228 | (let ((pk (fold-hdk app hdk)))
229 | (values pk '(:agree-key) (make-instance 'document :pk pk))))
230 | (defmethod create-shared-secret (app hdk reader-pk)
231 | (blind-dh (bl (hdk app))
232 | (sk-device app)
233 | (nth-value 1 (fold-hdk app hdk))
234 | reader-pk))
235 | (defun delegate-key-creation (app hdk)
236 | (derive-key-pair (kem (hdk app))
237 | (nth-value 2 (fold-hdk app hdk))))
238 | (defun accept-key (app hdk kh index pk-expected)
239 | (let* ((salt (decap (kem (hdk app)) kh (delegate-key-creation app hdk)))
240 | (pk (hdk-apply (hdk app) index (get-key-info app hdk) salt)))
241 | (assert (crypto:ec-point-equal pk-expected pk)))
242 | (append hdk (list kh index)))
243 |
244 | (defconstant +hdk-root+ '(0))
245 | (defclass unit ()
246 | ((app :reader app :initarg :app)
247 | (index :reader index :initform (make-hash-table :weakness :key))))
248 | (defmacro unit-hdk (unit doc) (list 'gethash doc (list 'index unit)))
249 | (defun make-unit (hdk) (make-instance 'unit :app (make-app hdk)))
250 | (defun activate (unit)
251 | (multiple-value-bind (pk purposes doc) (get-key-info (app unit) +hdk-root+)
252 | (declare (ignore pk purposes))
253 | (setf (unit-hdk unit doc) +hdk-root+)
254 | doc))
255 | (defun prove-possession (unit doc reader-data)
256 | (create-shared-secret (app unit) (unit-hdk unit doc) reader-data))
257 | (defun request (unit doc-parent)
258 | (nth-value 1 (delegate-key-creation (app unit) (unit-hdk unit doc-parent))))
259 | (defun accept (unit doc-parent kh index doc)
260 | (let* ((hdk (unit-hdk unit doc-parent))
261 | (app (app unit)))
262 | (setf (unit-hdk unit doc) (accept-key app hdk kh index (pk doc)))))
263 | (defmethod hdk ((unit unit)) (hdk (app unit)))
264 | (defmethod kem ((unit unit)) (kem (hdk unit)))
265 |
266 | (defclass reader ()
267 | ((sk :reader sk :initarg :sk)
268 | (dh :reader dh :initarg :dh)))
269 | (defun make-reader (ec-dh)
270 | (make-instance 'reader :sk (random-scalar (ec ec-dh))
271 | :dh ec-dh))
272 | (defun verify (reader doc device-data)
273 | (= (os2ip device-data)
274 | (os2ip (create-shared-secret (dh reader) (sk reader) (pk doc)))))
275 | (defmethod pk ((reader reader))
276 | (scalar-base-mult (ec (dh reader)) (sk reader)))
277 |
278 | (loop with vectors =
279 | `((""
280 | "QUUX-V01-CS02-with-expander-SHA256-128" #x20
281 | ,(read-bytes
282 | "68a985b87eb6b46952128911f2a4412bbc302a9d759667f87f7a21d803f07235"))
283 | ("abc"
284 | "QUUX-V01-CS02-with-expander-SHA256-128" #x20
285 | ,(read-bytes
286 | "d8ccab23b5985ccea865c6c97b6e5b8350e794e603b4b97902f53a8a0d605615"))
287 | (""
288 | "QUUX-V01-CS02-with-expander-SHA256-128" #x80
289 | ,(read-bytes
290 | "af84c27ccfd45d41914fdff5df25293e221afc53d8ad2ac06d5e3e29485dadbe"
291 | "e0d121587713a3e0dd4d5e69e93eb7cd4f5df4cd103e188cf60cb02edc3edf18"
292 | "eda8576c412b18ffb658e3dd6ec849469b979d444cf7b26911a08e63cf31f9dc"
293 | "c541708d3491184472c2c29bb749d4286b004ceb5ee6b9a7fa5b646c993f0ced"
294 | )))
295 | for (msg dst len result) in vectors
296 | do (assert
297 | (= (let ((h2c (make-instance 'h2c :ec +p256+
298 | :h +sha256+
299 | :dst dst)))
300 | (os2ip (expand-message-xmd h2c (ASCII msg) (ASCII dst) len))
301 | result))))
302 |
303 | (assert
304 | (let* ((prk
305 | (extract +hkdf-sha256+
306 | (i2osp #x000102030405060708090a0b0c 13)
307 | (i2osp #x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 22)))
308 | (okm (expand +hkdf-sha256+ prk (i2osp #xf0f1f2f3f4f5f6f7f8f9 10) 42)))
309 | (and
310 | (= (os2ip prk)
311 | #x077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5)
312 | (= (os2ip okm)
313 | (read-bytes
314 | "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34"
315 | "007208d5b887185865")))))
316 |
317 | (assert
318 | (= (derive-key-pair +dhkem-p256-hkdf-sha256+
319 | (i2osp
320 | #x4270e54ffd08d79d5928020af4686d8f6b7d35dbe470265f1f5aa22816ce860e 32))
321 | #x4995788ef4b9d6132b249ce59a77281493eb39af373d236a1fe415cb0c2d7beb))
322 |
323 | (assert (let ((kem +dhkem-p256-hkdf-sha256+))
324 | (multiple-value-bind (sk pk) (derive-key-pair kem (i2osp #x01 4))
325 | (multiple-value-bind (k c) (encap kem pk)
326 | (= (os2ip k) (os2ip (decap kem c sk)))))))
327 |
328 | (let* ((bl +bl-ecdh-p256+)
329 | (ikm #(1 2 3))
330 | (bk (derive-blind-key bl ikm))
331 | (ctx #(4 5 6))
332 | (bf (derive-blinding-factor bl bk ctx)))
333 | (multiple-value-bind (sk-x pk-x) (generate-key-pair (ec bl))
334 | (multiple-value-bind (sk-y pk-y) (generate-key-pair (ec bl))
335 | (assert (= (os2ip (blind-dh bl sk-x bf pk-y))
336 | (let ((pk-blinded (blind-public-key bl pk-x bk ctx)))
337 | (os2ip (create-shared-secret
338 | (ec-dh bl) sk-y pk-blinded))))))))
339 |
340 | (let* ((bl +bl-ecdh-p256+)
341 | (ikm #(1 2 3))
342 | (bk (derive-blind-key bl ikm))
343 | (ctx1 #(4 5 6))
344 | (ctx2 #(7 8 9))
345 | (bf1 (derive-blinding-factor bl bk ctx1))
346 | (bf2 (derive-blinding-factor bl bk ctx2)))
347 | (multiple-value-bind (sk pk) (generate-key-pair (ec bl))
348 | (assert (= (os2ip
349 | (create-shared-secret
350 | (ec-dh bl) 1
351 | (blind-public-key bl
352 | (blind-public-key bl pk bk ctx1)
353 | bk ctx2)))
354 | (os2ip
355 | (blind-dh
356 | bl sk
357 | (combine bl bf1 bf2)
358 | +secp256r1-g+))))))
359 |
360 | (let* ((app (make-app +hdk-ecdh-p256+))
361 | (hdk +hdk-root+)
362 | (pk-bl (get-key-info app hdk))
363 | (pk-kem (nth-value 1 (delegate-key-creation app hdk))))
364 | (multiple-value-bind (salt kh) (encap (kem (hdk app)) pk-kem)
365 | (let* ((bk (derive-blind-key (bl (hdk app)) salt))
366 | (index 42)
367 | (ctx (create-context (hdk app) index))
368 | (pk-expected (blind-public-key (bl (hdk app)) pk-bl bk ctx)))
369 | (accept-key app hdk kh index pk-expected))))
370 |
371 | (let* ((unit (make-unit +hdk-ecdh-p256+))
372 | (doc (activate unit)))
373 | (let* ((reader (make-reader +ecdh-p256+))
374 | (device-data (prove-possession unit doc (pk reader))))
375 | (assert (verify reader doc device-data)))
376 | (let ((pk-kem (request unit doc)))
377 | (multiple-value-bind (salt kh) (encap (kem (hdk (app unit))) pk-kem)
378 | (let* ((range '(0 1 2 3 4 5 6 7 8))
379 | (docs (loop for i in range
380 | collect (make-document (hdk (app unit)) doc salt i))))
381 | (loop for i in range for d in docs do (accept unit doc kh i d))
382 | (assert (= 9 (length docs)))
383 | (loop for doc in docs do
384 | (let* ((reader (make-reader +ecdh-p256+))
385 | (device-data (prove-possession unit doc (pk reader))))
386 | (assert (verify reader doc device-data))))))))
387 |
388 | (format t "Tests ran successfully~%")
389 |
--------------------------------------------------------------------------------