taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

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     }