024-age-restriction.rst (31252B)
1 DD 24: Anonymous Age Restriction Extension 2 ########################################## 3 4 Summary 5 ======= 6 7 This document presents and discusses an extension to GNU Taler that provides 8 anonymous age-restriction. 9 10 Motivation 11 ========== 12 13 Merchants are legally obliged to perform age verification of customers when 14 they buy certain goods and services. Current mechanisms for age verification 15 are either ID-based or require the usage of credit/debit cards. In all cases 16 sensitive private information is disclosed. 17 18 We want to offer a better mechanism for age-restriction with GNU Taler that 19 20 * ensures anonymity and unlinkability of purchases 21 * can be set to particular age groups by parents/wardens at withdrawal 22 * is bound to particular coins/tokens 23 * can be verified by the merchant at purchase time 24 * persists even after refresh 25 26 The mechanism is presented as an 'extension' to GNU Taler, that is, as an 27 optional feature that can be switched on by the exchange operator. 28 29 Requirements 30 ============ 31 32 * legal requirements for merchants must allow for this kind of mechanism 33 34 35 Proposed Solution 36 ================= 37 38 We propose an extension to GNU Taler for age-restriction that can be enabled by 39 an Exchange¹). 40 41 Once enabled, coins with age restrictions can be withdrawn by parents/warden 42 who can choose to **commit** the coins to a certain maximum age out of a 43 predefined list of age groups. 44 45 The minors/wards receive those coins and can now **attest** a required minimum 46 age (provided that age is less or equal to the committed age of the coins) to 47 merchants, who can **verify** the minimum age. 48 49 For the rest values (change) after an transaction, the minor/ward can 50 **derive** new age-restricted coins. The exchange can **compare** the equality 51 of the age-restriction of the old coin with the new coin (in a zero-knowledge 52 protocol, that gives the minor/ward a 1/κ chance to raise the minimum age for 53 the new coin). 54 55 The proposed solution maintains the guarantees of GNU Taler with respect to 56 anonymity and unlinkability. We have published a paper 57 `Zero Knowledge Age Restriction for GNU Taler <https://link.springer.com/chapter/10.1007/978-3-031-17140-6_6>`_ 58 with the details. 59 60 ¹) Once the feature is enabled and the age groups are defined, the exchange has 61 to stick to that decision until the support for age restriction is disabled. 62 We might reconsider this design decision at some point. 63 64 65 Main ideas and building blocks 66 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 67 68 The main ideas are as follows: 69 70 #. The exchange defines and publishes M+1 different *age groups* of increasing 71 order: :math:`0 < a_1 < \ldots < a_M` with :math:`a_i \in \mathbb{N}`. The 72 zeroth age group is :math:`\{0,\ldots,a_1-1\}`. 73 74 #. An **unrestricted age commitment** is defined as a vector of length M of 75 pairs of Edx25519_ public and private keys on Curve25519. In other words: one 76 key pair for each age group after the zeroth: :math:`\bigl\langle (q_1, 77 p_1), \ldots, (q_M, p_M) \bigr\rangle`. Here, :math:`q_i` are the public keys 78 (mnemonic: **q-mitments**), :math:`p_i` are the private keys. 79 80 #. A **restricted age commitment** *to age group m* is derived from an 81 unrestricted age commitment by removing all private keys for 82 indices larger than m: :math:`\bigl\langle (q_1, p_1), \ldots, (q_m, p_m), 83 \, (q_{m+1}, \perp), \ldots, (q_M, \perp )\bigr\rangle`. F.e. if *none* of 84 the private keys is provided, the age commitment would be restricted to the 85 zeroth age group. 86 87 #. The act of restricting an unrestricted age commitment is performed by the 88 parent/ward. 89 90 #. An *age commitment* (without prefix) is just the vector of public keys: 91 :math:`\vec{Q} := \langle q_1, \ldots, q_M \rangle`. Note that from 92 just the age commitment one can not deduce if it originated from an 93 unrestricted or restricted one (and what age). 94 95 #. An *attestation of age group k* is essentially the signature to any message 96 with the private key for slot k, if the corresponding private key is 97 available in a restricted age commitment. (Unrestricted age commitments can 98 attest for any age group). 99 100 #. An age commitment is *bound to a particular coin* by incorporating the 101 SHA256 hash value of the age commitment (i.e. the M public keys) into the 102 signature of the coin. So instead of signing :math:`\text{FDH}_N(C_p)` with 103 the RSA private key of a denomination with support for age restriction, we 104 sign :math:`\text{FDH}_N(C_p, h_Q)`. Here, :math:`C_p` is the EdDSA public 105 key of a coin and :math:`h_Q` is the hash of the age commitment :math:`\vec{Q}`. 106 **Note:** A coin with age restriction can only be validated when both, the 107 public key of the coin itself **and** the hash of the age commitment, are 108 present. This needs to be supported in each subsystem: Exchange, Wallet and 109 Merchant. 110 111 112 TODO: Summarize the design based on the five functions ``Commit()``, 113 ``Attest()``, ``Verify()``, ``Derive()``, ``Compare()``, once the paper from 114 Özgür and Christian is published. 115 116 Changes in the Exchange API 117 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 118 119 The necessary changes in the exchange involve 120 121 * indication of support for age restriction as an extension 122 * modification of the refresh protocol (both, commit and reveal phase) 123 * modification of the deposit protocol 124 125 126 Extension for age restriction 127 ----------------------------- 128 129 .. note:: 130 131 Registering an extension is defined in 132 :doc:`design document 006 ― Extensions <006-extensions>`. 133 134 135 The exchange indicates support for age-restriction in response to ``/keys`` by 136 registering the extension ``age_restriction`` with a value type 137 ``ExtensionAgeRestriction``: 138 139 .. ts:def:: ExtensionAgeRestriction 140 141 interface ExtensionAgeRestriction { 142 // The field ``critical`` is mandatory for an extension. 143 // Age restriction is not required to be understood by an client, so 144 // ``critical`` will be set to ``false``. 145 critical: false; 146 147 // The field ``version`` is mandatory for an extension. It is of type 148 // `LibtoolVersion`. 149 version: "1"; 150 151 // Age restriction specific configuration 152 config: ConfigAgeRestriction; 153 } 154 155 .. ts:def:: ConfigAgeRestriction 156 157 interface ConfigAgeRestriction { 158 // The age groups. This field is mandatory and binding in the sense 159 // that its value is taken into consideration when signing the 160 // age restricted denominations in the `ExchangeKeysResponse` 161 age_groups: AgeGroups; 162 } 163 164 Age Groups 165 ~~~~~~~~~~ 166 167 Age groups are represented as a finite list of positive, increasing integers 168 that mark the beginning of the *next* age group. The value 0 is omitted but 169 implicitly marks the beginning of the *zeroth* age group and the first number 170 in the list marks the beginning of the *first* age group. Age groups are 171 encoded as a colon separated string of integer values. They are referred to by 172 their *slot*, i.e. "age group 3" is the age group that starts with the 3. 173 integer in the list. 174 175 For example: the string "8:10:12:14:16:18:21" represents the age groups 176 177 0. {0,1,2,3,4,5,6,7} 178 #. {8,9} 179 #. {10,11} 180 #. {12,13} 181 #. {14,15} 182 #. {16,17} 183 #. {18,19,20} 184 #. {21, ⋯ } 185 186 The field ``age_groups`` of type `AgeGroups` is mandatory and binding in the 187 sense that its value is taken into consideration when signing the denominations 188 in ``ExchangeKeysResponse.age_restricted_denoms``. 189 190 .. ts:def:: AgeGroups 191 192 // Representation of the age groups as colon separated edges: Increasing 193 // from left to right, the values mark the beginning of an age group up 194 // to, but not including the next value. The initial age group starts at 195 // 0 and is not listed. Example: "8:10:12:14:16:18:21". 196 type AgeGroups = string; 197 198 199 Age restricted denominations 200 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 201 202 If age-restriction is registered as extension ``age_restriction``, as described 203 above, the root-object ``ExchangeKeysResponse`` in response to ``/keys`` MUST 204 be extended by an additional field ``age_restricted_denoms``. This is an 205 *additional* list of denominations that must be used during the modified 206 ``refresh`` and ``deposit`` operations (see below). 207 208 The data structure for those denominations is the same as for the regular ones 209 in ``ExchangeKeysResponse.denoms``. **However**, the following differences 210 apply for each denomination in the list: 211 212 1. The value of ``TALER_DenominationKeyValidityPS.denom_hash`` 213 is taken over the public key of the denomination **and** the string in 214 ``ExtensionAgeRestriction.age_groups`` from the corresponding extension 215 object (see above). 216 217 2. The value of ``TALER_DenominationKeyValidityPS.purpose`` is set to 218 ``TALER_SIGNATURE_MASTER_AGE_RESTRICTED_DENOMINATION_KEY_VALIDITY``. 219 220 And similar to ``.denoms``, if the query parameter ``last_issue_date`` was 221 provided by the client, the exchange will only return the keys that have 222 changed since the given timestamp. 223 224 225 .. ts:def:: ExchangeKeysResponse 226 227 interface ExchangeKeysResponse { 228 //... 229 230 // List of denominations that support age-restriction with the age groups 231 // given in age_groups. This is only set **iff** the extension 232 // ``age_restriction`` is registered under ``entensions`` with type 233 // ``ExtensionAgeRestriction``. 234 // 235 // The data structure for each denomination is the same as for the 236 // denominations in ExchangeKeysResponse.denoms. **However**, the 237 // following differences apply for each denomination in the list: 238 // 239 // 1. The value of ``TALER_DenominationKeyValidityPS.denom_hash`` 240 // is taken over the public key of the denomination __and__ the 241 // string in ``ExtensionAgeRestriction.age_groups`` from the 242 // corresponding extension object. 243 // 244 // 2. The value of ``TALER_DenominationKeyValidityPS.purpose`` is set to 245 // ``TALER_SIGNATURE_MASTER_AGE_RESTRICTED_DENOMINATION_KEY_VALIDITY`` 246 // 247 // Similar as for ``.denoms``, if the query parameter ``last_issue_date`` 248 // was provided by the client, the exchange will only return the keys that 249 // have changed since the given timestamp. 250 age_restricted_denoms: DenomCommon[]; 251 252 //... 253 } 254 255 256 SQL schema 257 ----------- 258 259 The exchange has to mark denominations with support for age restriction as such 260 in the database. Also, during the melting phase of the refresh operation, the 261 exchange will have to persist the SHA256 hash of the age commitment of the 262 original coin. 263 264 The schema for the exchange is changed as follows: 265 266 .. sourcecode:: sql 267 268 -- Everything in one big transaction 269 BEGIN; 270 -- Check patch versioning is in place. 271 SELECT _v.register_patch('exchange-TBD', NULL, NULL); 272 273 -- Support for age restriction is marked per denomination. 274 ALTER TABLE denominations 275 ADD COLUMN age_restricted BOOLEAN NOT NULL DEFAULT (false); 276 COMMENT ON COLUMN denominations.age_restriced 277 IS 'true if this denomination can be used for age restriction'; 278 279 -- During the melting phase of the refresh, the wallet has to present the 280 -- hash value of the age commitment (only for denominations with support 281 -- for age restriction). 282 ALTER TABLE refresh_commitments 283 ADD COLUMN age_commitment_h BYTEA CHECK (LENGTH(age_commitment_h)=64); 284 COMMENT ON COLUMN refresh_commitments.age_commitment_h 285 IS 'SHA256 hash of the age commitment of the old coin, iff the corresponding 286 denomimination has support for age restriction, NULL otherwise.'; 287 COMMIT; 288 289 Note the constraint on ``refresh_commitments.age_commitment_h``: It can be 290 NULL, but only iff the corresponding denomination (indirectly referenced via 291 table ``known_coins``) has ``.age_restricted`` set to true. This constraint 292 can not be expressed reliably with SQL. 293 294 295 Protocol changes 296 ---------------- 297 298 Withdraw 299 ~~~~~~~~ 300 301 The withdraw protocol is affected in the following situations: 302 303 - A wire transfer to the exchange (to fill a reserve) was marked by the 304 originating bank as coming from a bank account of a minor, belonging to a of 305 a specific age group, or by other means. 306 - A KYC-process has been performed with the owner of a reserve and the user has 307 been identified as being a minor. 308 - A Peer-to-Peer transaction was performed between customers. The receiving 309 customer's KYC result tells the exchange that the customer belongs to a 310 specific age group. 311 312 In these cases, the wallet will have to perform a zero-knowledge protocol with 313 exchange as part of the the withdraw protocol, which we sketch here. Let 314 315 - :math:`\kappa` be the same cut-and-choose parameter for the refresh-protocol. 316 - :math:`\Omega \in E` be a published, nothing-up-my-sleeve, constant 317 group-element on the elliptic curve. 318 - :math:`a \in \{1,\ldots,M\}` be the maximum age (group) for which the wallet 319 has to prove its commitment. 320 321 The values :math:`\kappa`, :math:`\Omega` and :math:`a` are known to the 322 Exchange and the Wallet. Then, Wallet and Exchange run the following protocol 323 for the withdrawal of one coin: 324 325 - *Wallet* 326 1. creates planchets :math:`C_i` for :math:`i \in \{1,\ldots,\kappa\}` as candidates for *one* coin. 327 #. creates age-commitments :math:`\vec{Q}^i` for :math:`i \in \{1,\ldots,\kappa\}` as follows: 328 329 a) creates :math:`a`-many Edx25519-keypairs :math:`(p^i_j, q^i_j)` 330 randomly for :math:`j \in \{1,\ldots,a\}` (with public keys :math:`q^i_j`), 331 #) chooses randomly :math:`(M - a)`-many scalars :math:`s^i_j` for :math:`j \in \{a+1,\ldots,M\}`, 332 #) calculates :math:`\omega^i_j = s^i_j*\Omega` for :math:`j \in \{a+1,\ldots,M \}`, 333 #) sets :math:`\vec{Q}^i := (q^i_1,\ldots,q^i_a,\omega^i_{a+1},\ldots,\omega^i_M)` 334 335 #. calculates :math:`f_i := \text{FDH}(C_i, H(\vec{Q}^i))` for :math:`i \in \{ 1,\ldots,\kappa \}`. 336 #. chooses random blindings :math:`\beta_i(.)` for :math:`i \in \{1,\ldots,\kappa\}`. The blinding functions depend on the cipher (RSA, CS). 337 #. sends :math:`(\beta_1(f_1),\ldots,\beta_\kappa(f_\kappa))` to the Exchange 338 339 - *Exchange* 340 7. receives :math:`(b_1,\ldots,b_\kappa)` 341 #. calculates :math:`F := \text{H}(b_1||\ldots||b_\kappa)` 342 #. chooses randomly :math:`\gamma \in \{1,\ldots,\kappa\}` and 343 #. signs :math:`r := b_\gamma` resulting in signature :math:`\sigma_r` 344 #. stores :math:`F \mapsto (r, \sigma_r)` 345 #. sends :math:`\gamma` to the Wallet. 346 347 - *Wallet* 348 10. receives :math:`\gamma` 349 #. sends to the Exchange the tuple :math:`\left(F, \vec{\beta}, \vec{\vec{Q}}, \vec{\vec{S}}\right)` with 350 351 - :math:`F := \text{H}(\beta_1(f_1)||\ldots||\beta_\kappa(f_\kappa))` 352 - :math:`\vec{\beta} := (\beta_1,\ldots,\beta_{\gamma-1},\bot,\beta_{\gamma+1},\ldots,\beta_\kappa)` 353 - :math:`\vec{\vec{Q}} := (\vec{Q}^1,\ldots,\vec{Q}^{\gamma-1},\bot,\vec{Q}^{\gamma+1},\ldots,\vec{Q}^\kappa)` 354 - :math:`\vec{\vec{S}} := (\vec{S}^1,\ldots,\vec{S}^{\gamma-1},\bot,\vec{S}^{\gamma+1},\ldots,\vec{S}^\kappa)` 355 with :math:`\vec{S}^i := (s^i_j)` 356 357 - *Exchange* 358 12. receives :math:`\left(F, (\beta_i), (\vec{Q}^i), (\vec{B}^i) \right)` 359 #. retrieves :math:`(r, \sigma_r)` from :math:`F` or bails out if not present 360 #. calculates :math:`b_i := \beta_i\left(\text{FDH}(\vec{Q}^i)\right)` for :math:`i \neq \gamma` 361 #. compares :math:`F \overset{?}{=} \text{H}(b_1||\ldots||b_{\gamma - 1}||r||b_{\gamma+1}||\ldots||b_\kappa)` and bails out on inequality 362 #. for each :math:`\vec{B}^i, i \neq \gamma` 363 364 i. calculates :math:`\tilde{\omega}^i_j := b^i_j * \Omega` for :math:`j \in \{a+1,\ldots,M\}` 365 #. compares each :math:`\tilde{\omega}^i_j` to :math:`q^i_j` from :math:`\vec{Q}^i = (q^i_1, \ldots, q^i_M)` and bails out on inequality 366 #. sends (blinded) signature :math:`\sigma_r` to Wallet 367 368 - *Wallet* 369 18. receives :math:`\sigma_r` 370 #. calculates (unblinded) signature :math:`\sigma_\gamma := \beta^{-1}_\gamma(\sigma_r)` for coin :math:`C_\gamma`. 371 372 373 Note that the batch version of withdraw allows the withdrawal of *multiple* 374 coins at once. For that scenario the protocol sketched above is adapted to 375 accomodate for handling multiple coins at once -- thus multiplying the amount 376 of data by the amount of coins in question--, but all with the same value of 377 :math:`\gamma`. 378 379 The *actual* implementation of the protocol above will have major optimizations 380 to keep the bandwidth usage to a minimum and also ensure that a denomination in 381 the commitment doesn't expire before the reveal. 382 383 Instead of generating and sending the age commitment (array of public keys) and 384 blindings for each coin, the wallet *MUST* derive the corresponding blindings 385 and the age commitments from the coin's private key itself as follows: 386 387 Let 388 389 - :math:`s` be the master secret of the coin, from which the private key :math:`c_s`, blinding :math:`\beta` and nonce :math:`n` are derived as usual in the wallet core 390 - :math:`m \in \{1,\ldots,M\}` be the maximum age (according to the reserve) 391 that a wallet can commit to during the withdrawal. 392 - :math:`P` be a published constant Edx25519-public-key to which the private 393 key is not known to any client. 394 395 For the age commitment, calculate: 396 397 1. For age group :math:`a \in \{1,\ldots,m\}`, set 398 399 .. math:: 400 s_a &:= \text{HDKF}(s, \text{"age-commitment"}, a) \\ 401 p_a &:= \text{Edx25519\_generate\_private}(s_a) \\ 402 q_a &:= \text{Edx25519\_public\_from\_private}(p_a) 403 404 2. For age group :math:`a \in \{m,\ldots,M\}`, set 405 406 .. math:: 407 f_a &:= \text{HDKF}(s, \text{"age-factor"}, a) \\ 408 q_a &:= \text{Edx25519\_derive\_public}(P, f_a). 409 410 Then the vector :math:`\vec{q} = \{q_1,\ldots,q_M\}` is then the age commitment 411 associated to the coin's private key :math:`c_s`. For the non-disclosed coins, 412 the wallet can use the vector :math:`(p_1,\ldots,p_m,\bot,\ldots,\bot)` of 413 private keys for the attestation. 414 415 Provided with the secret :math:`s`, the exchange can therefore calculate the 416 private key :math:`c_s`, the blinding :math:`\beta`, the nonce :math:`n` (if 417 needed) and the age commitment :math:`\vec{q}`, along with the coin's public 418 key :math:`C_p` and use the value of 419 420 .. math:: 421 422 \text{TALER\_CoinPubHashP}(C_p, \text{age\_commitment\_hash}(\vec{q})) 423 424 during the verification of the original age-withdraw-commitment. 425 426 For the withdrawal with age restriction, a sketch of the corresponding database 427 schema in the exchange is given here: 428 429 .. graphviz:: 430 431 digraph deposit_policies { 432 rankdir = LR; 433 splines = true; 434 fontname="monospace" 435 node [ 436 fontname="monospace" 437 shape=record 438 ] 439 440 subgraph cluster_commitments { 441 label=<<B>age_withdraw</B>> 442 margin=20 443 commitments [ 444 label="age_withdraw_id\l|<hc>h_commitment\l|amount_with_fee_val\l|amount_with_fee_frac\l|noreveal_index\l|max_age\l|<res>reserve_pub\l|reserve_sig\l|<denom>[n] denominations_serials\l|[n] h_blind_evs\l|[n] denom_sigs\l" 445 ] 446 } 447 448 commitments:res->reserves:id [ label="n:1"; fontname="monospace"]; 449 commitments:denom -> denominations:id [ label="n:1"; fontname="monospace"] ; 450 } 451 452 453 Refresh - melting phase 454 ~~~~~~~~~~~~~~~~~~~~~~~ 455 456 During the melting phase of the refresh, the wallet has to present the hash 457 value of the age commitment (for denominations with support for age 458 restriction). Therefore, in the ``/coins/$COIN_PUB/melt`` POST request, the 459 ``MeltRequest`` object is extended with an optional field 460 ``age_commitment_hash``: 461 462 .. ts:def:: AgeMeltRequest 463 464 interface AgeMeltRequest { 465 ... 466 467 // SHA256 hash of the age commitment of the coin, IFF the denomination 468 // has age restriction support. MUST be omitted otherwise. 469 age_commitment_hash?: AgeCommitmentHash; 470 471 ... 472 } 473 474 .. ts:def:: AgeCommitmentHash 475 476 type AgeCommitmentHash = SHA256HashCode; 477 478 The responses to the POST request remain the same. 479 480 For normal denominations *without* support for age restriction, the calculation 481 for the signature check is as before (borrowing notation from 482 `Florian's thesis <https://taler.net/papers/thesis-dold-phd-2019.pdf>`_): 483 484 .. math:: 485 \text{FDH}_N(C_p)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}\,N 486 487 Here, :math:`C_p` is the EdDSA public key of a coin, :math:`\sigma_C` is its 488 signature and :math:`\langle e, N \rangle` is the RSA public key of the 489 denomination. 490 491 For denominations *with* support for age restriction, the exchange takes the 492 hash value ``age_commitment_hash`` (abbreviated as :math:`h_a`) into account 493 when verifying the coin's signature: 494 495 .. math:: 496 \text{FDH}_N(C_p, h_a)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}N 497 498 499 500 501 Refresh - reveal phase 502 ~~~~~~~~~~~~~~~~~~~~~~ 503 504 During the reveal phase -- that is upon POST to ``/refreshes/$RCH/reveal`` -- 505 the client has to provide the original age commitment of the old coin (i.e. the 506 vector of public keys), iff the corresponding denomination had support for age 507 restriction. The size of the vector is defined by the Exchange implicetly as 508 the amount of age groups defined in the field ``.age_groups`` of the 509 ``ExtensionAgeRestriction``. 510 511 .. ts:def:: RevealRequest 512 513 interface RevealRequest { 514 ... 515 516 // Iff the corresponding denomination has support for age restriction, 517 // the client MUST provide the original age commitment, i.e. the vector 518 // of public keys. 519 // The size of the vector is defined by the Exchange implicetly as the 520 // amount of age groups defined in the field ``.age_groups`` of the 521 // ``ExtensionAgeRestriction``. 522 old_age_commitment?: Edx25519PublicKey[]; 523 524 525 ... 526 } 527 528 529 The exchange can now check if the provided public keys ``.old_age_commitment`` 530 have the same SHA256 hash value when hashed in sequence as the 531 ``age_commitment_hash`` of the original coin from the call to melt. 532 533 The existing `cut&choose protocol during the reveal phase 534 </core/api-exchange.html#post--refreshes-$RCH-reveal>`__ is extended to perform 535 the following additional computation and checks: 536 537 Using the κ-1 transfer secrets :math:`\tau_i` from the reveal request, the 538 exchange derives κ-1 age commitments from the ``old_age_commitment`` by calling 539 ``Edx25519_derive_public()`` on each `Edx25519PublicKey`, with :math:`\tau_i` 540 as the seed, and then calculates the corresponding κ-1 hash values :math:`h_i` 541 of those age commitments. 542 543 It then calculates the κ-1 blinded hashes 544 :math:`m_i = r^{e_i}\text{FDH}_N(C^{(i)}_p, h_i)` (using the notation from Florian's 545 thesis) of the disclosed coins and together with the :math:`m_\gamma` of the 546 undisclosed coin, calculates the hash 547 :math:`h'_m = H(m_1,\cdots,m_\gamma,\cdots,m_\kappa)` which is then used in the 548 final verification step of the cut&choose protocol. 549 550 551 Deposit 552 ~~~~~~~ 553 554 As always, the merchant has to provide the public key of a coin during a POST 555 to ``/coins/$COIN_PUB/deposit``. However, for coins with age restriction, the 556 signature check requires the hash of the age commitment. Therefore the request 557 object ``DepositRequest`` is extended by an optional field 558 ``age_commitment_hash`` which MUST be set (with the SHA256 hash of the age 559 commitment), iff the corresponding denomination had support for age restriction 560 enabled. The merchant has received this value prior from the customer during 561 purchase. 562 563 .. ts:def:: DepositRequest 564 565 interface DepositRequest { 566 ... 567 568 // Iff the corresponding denomination had support for age restriction 569 // enabled, this field MUST contain the SHA256 value of the age commitment that 570 // was provided during the purchase. 571 age_commitment_hash?: AgeCommitmentHash; 572 573 ... 574 } 575 576 Again, the exchange can now check the validity of the coin with age restriction 577 by evaluating 578 579 .. math:: 580 \text{FDH}_N(C_p, h_a)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}N 581 582 Also again, :math:`C_p` is the EdDSA public key of a coin, :math:`\sigma_C` is 583 its signature, :math:`\langle e, N \rangle` is the RSA public key of the 584 denomination and :math:`h_a` is the value from ``age_commitment_hash``. 585 586 587 588 Changes in the Merchant API 589 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 590 591 592 Claiming the order 593 ------------------ 594 595 If an order requires a minimum age, the merchant MUST express that required 596 minimum age in response to order claim by the wallet, that is, a POST to 597 ``[/instances/$INSTANCE]/orders/$ORDER_ID/claim``. 598 599 The object ``ContractTerms`` is extended by an optional field 600 ``minimum_age`` that can be any integer greater than 0. In reality 601 this value will not be smaller than, say, 8, and not larger than, say, 21. 602 603 .. ts:def:: DD24ContractTerms 604 605 interface DD24ContractTerms { 606 ... 607 608 // If the order requires a minimum age greater than 0, this field is set 609 // to the integer value of that age. In reality this value will not be 610 // smaller than, say, 8, and not larger than, say, 21. 611 minimum_age?: Integer; 612 613 ... 614 } 615 616 By sending the contract term with the field ``minimum_age`` set to an 617 non-zero integer value, the merchant implicetly signals that it understands the 618 extension ``age_restriction`` for age restriction from the exchange. 619 620 621 Making the payment 622 ------------------ 623 624 If the ``ContractTerms`` had a non-zero value in field 625 ``minimum_age``, the wallet has to provide evidence of that minimum 626 age by 627 628 #. *either* using coins which are of denominations that had *no* age support 629 enabled, 630 631 #. *or* using coins which are of denominations that have support for age 632 restriction enabled 633 634 * and then ―for each such coin― it has the right private key of the 635 restricted age commitment to the age group into which the required minimum 636 age falls (i.e. a non-empty entry at the right index in vector of Edx25519 637 keys, see above). 638 639 * and signs the required minimum age with each coin's private key 640 corresponding to the age group, 641 642 * and sends ―for each coin― the complete age commitment and the signature to 643 the merchant. 644 645 The object ``CoinPaySig`` used within a ``PayRequest`` during a POST to 646 ``[/instances/$INSTANCE]/orders/$ORDER_ID/pay`` is extended as follows: 647 648 .. ts:def:: AgeCoinPaySig 649 650 export interface AgeCoinPaySig extends CoinPaySig { 651 ... 652 653 // If a minimum age was required by the order and the wallet had coins that 654 // are at least committed to the corresponding age group, this is the 655 // signature of the minimum age as a string, using the private key to the 656 // corresponding age group. 657 minimum_age_sig?: Edx25519Signature; 658 659 // If a minimum age was required by the order, this is age commitment bound 660 // to the coin, i.e. the complete vector of Edx25519_ public keys, one for each 661 // age group (as defined by the exchange). 662 age_commitment?: Edx25519PublicKey[]; 663 664 } 665 666 667 The merchant can now verify 668 669 #. the validity of each (age restricted) coin by evaluating 670 671 .. math:: \text{FDH}_N(C_p, h_a)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}N 672 673 Again, :math:`C_p` is the EdDSA public key of a coin, :math:`\sigma_C` is 674 its signature, :math:`\langle e, N \rangle` is the RSA public key of the 675 denomination and :math:`h_a` is the SHA256 hash value of the vector in 676 ``age_commitment``. 677 678 #. the minimum age requirement by checking the signature in ``minimum_age_sig`` 679 against the public key ``age_commitment[k]`` of the corresponding age group, 680 say, ``k``. (The minimum age must fall into the age group at index ``k`` as 681 defined by the exchange). 682 683 **Note**: This applies only to coins for denominations that have support for 684 age restriction. Denominations *without* support for age restriction *always* 685 satisfy any minimum age requirement. 686 687 688 689 Changes in the Wallet 690 ^^^^^^^^^^^^^^^^^^^^^ 691 692 A wallet implementation SHOULD support denominations with age restriction. In 693 that case it SHOULD allow to select an age group as upper bound during 694 withdraw. 695 696 697 Alternatives 698 ============ 699 700 * ID-based systems 701 * credit/debit card based systems 702 703 704 Drawbacks 705 ========= 706 707 * age groups, once defined, are set permanently 708 709 Also discuss: 710 711 * storage overhead 712 * computational overhead 713 * bandwidth overhead 714 * legal issues? 715 716 Discussion / Q&A 717 ================ 718 719 We had some very engaged discussions on the GNU Taler `mailing list <taler@gnu.org>`__: 720 721 * `Money with capabilities <https://lists.gnu.org/archive/html/taler/2021-08/msg00005.html>`_ 722 723 * `On age-restriction (was: online games in China) <https://lists.gnu.org/archive/html/taler/2021-09/msg00006.html>`__ 724 725 * `Age-restriction is about coins, not currencies <https://lists.gnu.org/archive/html/taler/2021-09/msg00021.html>`__ 726 727 * The published paper: `Zero Knowledge Age Restriction for GNU Taler <https://link.springer.com/chapter/10.1007/978-3-031-17140-6_6>`_ 728 729 730 .. _Edx25519: 731 732 Edx25519 733 ======== 734 735 Edx25519 is a variant of EdDSA on curve25519 which allows for repeated 736 derivation of private and public keys, independently. It is implemented in 737 `GNUNET with commit ce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32. 738 <https://git.gnunet.org/gnunet.git/commit/?id=ce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32>`__ 739 740 The private keys in Edx25519 initially correspond to the data after expansion 741 and clamping in EdDSA. However, this correspondence is lost after deriving 742 further keys from existing ones. The public keys and signature verification 743 are compatible with EdDSA. 744 745 The scheme is as follows: 746 747 :: 748 749 /* Private keys in Edx25519 are pairs (a, b) of 32 byte each. 750 * Initially they correspond to the result of the expansion 751 * and clamping in EdDSA. 752 */ 753 754 Edx25519_generate_private(seed) { 755 /* EdDSA expand and clamp */ 756 dh := SHA-512(seed) 757 a := dh[0..31] 758 b := dh[32..64] 759 a[0] &= 0b11111000 760 a[31] &= 0b01111111 761 a[31] |= 0b01000000 762 763 return (a, b) 764 } 765 766 Edx25519_public_from_private(private) { 767 /* Public keys are the same as in EdDSA */ 768 (a, _) := private 769 return [a] * G 770 } 771 772 Edx25519_blinding_factor(P, seed) { 773 /* This is a helper function used in the derivation of 774 * private/public keys from existing ones. */ 775 h1 := HKDF_32(P, seed) 776 777 /* Ensure that h == h % L */ 778 h := h1 % L 779 780 /* Optionally: Make sure that we don't create weak keys. */ 781 P' := [h] * P 782 if !( (h!=1) && (h!=0) && (P'!=E) ) { 783 return Edx25519_blinding_factor(P, seed+1) 784 } 785 786 return h 787 } 788 789 Edx25519_derive_private(private, seed) { 790 /* This is based on the definition in 791 * GNUNET_CRYPTO_eddsa_private_key_derive. But it accepts 792 * and returns a private pair (a, b) and allows for iteration. 793 */ 794 (a, b) := private 795 P := Edx25519_public_key_from_private(private) 796 h := Edx25519_blinding_factor(P, seed) 797 798 /* Carefully calculate the new value for a */ 799 a1 := a / 8; 800 a2 := (h * a1) % L 801 a' := (a2 * 8) % L 802 803 /* Update b as well, binding it to h. 804 This is an additional step compared to GNS. */ 805 b' := SHA256(b ∥ h) 806 807 return (a', b') 808 } 809 810 Edx25519_derive_public(P, seed) { 811 h := Edx25519_blinding_factor(P, seed) 812 return [h]*P 813 } 814 815 Edx25519_sign(private, message) { 816 /* As in Ed25519, except for the origin of b */ 817 (d, b) := private 818 P := Edx25519_public_from_private(private) 819 r := SHA-512(b ∥ message) 820 R := [r] * G 821 s := r + SHA-512(R ∥ P ∥ message) * d % L 822 823 return (R,s) 824 } 825 826 Edx25519_verify(P, message, signature) { 827 /* Identical to Ed25519 */ 828 (R, s) := signature 829 return [s] * G == R + [SHA-512(R ∥ P ∥ message)] * P 830 }