taler-docs

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

062-pq-refresh.rst (14628B)


      1 DD 62: PQ Refresh Protocol
      2 ##########################
      3 
      4 Summary
      5 =======
      6 This document specifies a change to GNU Taler's refresh protocol that provides
      7 post-quantum resistance through hash-based cryptography and unique
      8 signatures, eliminating reliance on Diffie-Hellman operations, for the key
      9 derivation for the fresh coin from a dirty coin.
     10 
     11 Motivation
     12 ==========
     13 
     14 The current refresh protocol uses cryptographic primitives vulnerable to
     15 quantum attacks to derive the key material for a fresh coin from the key of the
     16 dirty coin.  The rational for the elaborate derivation in first place is to
     17 ensure taxability:  When original and new coins remain linked (by the owner of
     18 the original coin), passing coin outside the Taler protocols is discouraged.
     19 
     20 This redesign:
     21 
     22 1. Removes DH operations from refresh derivation
     23 2. Uses unique signatures for ownership proofs
     24 3. Derives key material from (unforgeable) signatures
     25 4. Maintains backward compatibility with other parts of the protocol stack
     26 
     27 Requirements
     28 ============
     29 
     30 * PQ-resistant refresh key derivation without computational hardness assumptions
     31 * Preservation of unlinkability between old/new coins (except for coin owner)
     32 * Compatibility with existing denomination types
     33 * Minimal bandwidth/storage overhead
     34 
     35 Proposed Solution
     36 =================
     37 
     38 RefreshDerive Algorithm
     39 ^^^^^^^^^^^^^^^^^^^^^^^^^
     40 
     41 The core mechanism uses two hash functions and unique signatures to
     42 derive the key material of a fresh coin from the old coin:
     43 
     44 .. sourcecode:: python
     45 
     46     # Notation:
     47     # r = random seed, cs = dirty coin secret, Cp = dirty coin public key
     48     # pkD = denomination public key
     49 
     50     def RefreshDerive(r, cs, Cp, pkD):
     51         t = Hash1a("Refresh", Cp, r)
     52         s = SignUnique(cs, t)
     53         x = Hash1b(s)
     54         b = Hash2(s)
     55         c2_s, C2_p = KeyGen(x)
     56         m = Blind(C2_p, b, pkD)
     57         return (s, c2_s, C2_p, m)
     58 
     59 
     60 Key Changes to the existing ``RefreshDerive``:
     61   1. *Proof of ownership*: ``s`` proves ownership through signature, without DH
     62   2. *Key derivation*: ``x`` derived through hashing of the signature
     63 
     64 The hash functions ``Hash1x`` might be the same, but can be pair-wise
     65 different.  However, the hash function ``Hash2`` must be different from
     66 all of the others.
     67 
     68 Note that the value ``r`` in the algorithm itself is a public value and can be
     69 considered as a kind of a commitment ("I'm going to sign this value") by the
     70 dirty coin.  Actual *secret* is the signature which needs to be disclosed
     71 during the reveal-part of the refresh operation.
     72 
     73 A variant of this algorithm that is suitable for retrieving a batch of ``n``
     74 fresh coins from a dirty coin is as follows:
     75 
     76 .. sourcecode:: python
     77 
     78     # Notation:
     79     # r = random dees, cs = dirty coin's secret, Cp = dirty coin's public key
     80     # pkD[] = array of denomination public keys,
     81     # meta = additional information, f.e. the index in a cut-and-choose
     82     def RefreshDeriveBatch(r, cs, Cp, pkDs: list[denomPublicKey], meta):
     83         t = Hash1a("Refresh", Cp, r, pkDs, meta)
     84         s = SignUnique(cs, t)
     85         for i, pkD in enumerate(pkDs):
     86             x[i] = Hash1b(s, i) # Note: use one HKDF for all i
     87             b[i] = Hash2(s, i)  # Note: use one HKDF for all i
     88         for i, pkD in enumerate(pkDs):
     89             c2_s[i], C2_p[i] = KeyGen(x[i])
     90             m[i] = Blind(C2_p[i], b[i], pkD)
     91         return (s, c2_s, C2_p, m)
     92 
     93 Note that the above deriviation will need to be done ``κ`` times with
     94 ``κ - 1`` of the signatures ``s`` being checked by the exchange as part of
     95 the reveal state of the cut-and-choose protocol.  Again, each of the ``κ``
     96 values ``r`` is a public value and can be considered as a kind of a commitment
     97 ("I'm going to sign this value") by the dirty coin.  The actual *secrets* are
     98 the ``κ`` signatures ``s`` which need to be disclosed during the
     99 reveal-part of the refresh operation.
    100 
    101 
    102 Protocol Modifications
    103 ^^^^^^^^^^^^^^^^^^^^^^
    104 
    105 Here is a short description of the main steps, with only one fresh coin being
    106 requested.  We will provide further details, once the related paper [1]_ is
    107 published.
    108 
    109 1. **Melting/Commit Phase**:
    110 
    111    - Client chooses a master (public) seed ``r`` and derives ``κ`` nonces ``r_1, ... r_κ``.
    112    - Client generates, using ``RefreshDeriveBatch``, ``κ*n`` blinded coin planchets
    113      ``m[1][1],...,m[1][n],...,m[κ][1],...,m[κ][n]`` from the nonces
    114    - Sends dirty coin public key ``Cp``, seed ``r``, all ``m[i][j]`` and
    115      fresh coin denomination selections ``pkD[1],...pkD[n]`` to the exchange,
    116      with signature ``σ_c`` made with the dirty coins' private key ``cs`` over the request.
    117    - Exchange verifies the request.
    118    - Exchange calculates ``h_m[i] = H(m[i][1],...,m[i][n])`` for all ``i`` from ``1...κ``
    119    - Exchange calculates ``H_m = H(h_m[1],...,h_m[κ])``
    120    - Exchange calculates ``rc = H(r, pkD[], H_m, meta, <maybe more>)``
    121    - Exchange chooses ``γ`` from ``1...κ`` and signs all ``m[γ][]``,
    122      resulting in ``σ[γ][]``.  This is done now as the exchange may later
    123      have deleted (or lost) its private signing key.
    124    - Exchange persists ``rc → (r, γ, pkD[], H_m, h_m[γ], σ[γ][], σ_c)``,
    125      deducts the cost for the operation from the old coin balance
    126      (in the same database transaction) and returns ``γ`` to the client.
    127 
    128 2. **Reveal Phase**:
    129 
    130    - Client discloses together with ``h_m`` all except the ``γ``-th
    131      (secret) signatures ``s[1],...,s[κ]`` from the ``κ`` calls to
    132      ``RefreshDeriveBatch``.
    133    - Exchange derives ``r_i`` from ``r`` and verifies each signature
    134      ``s[i]`` over ``Hash1a("Refresh", C_p, r_i, pkDs)``.
    135    - Exchange reconstructs the blinded coins ``m'[i][]`` for ``i != γ``.
    136    - Exchange calculates ``h'_m[i] = H(m'[i][])`` for all ``i != γ``.
    137    - Exchange calculates ``H'_m = H(h'_m[1],...,h_m[γ],...h'_m[κ])``.
    138    - Exchange verifies ``rc == H(r, pkD[], H'_m, ...)`` equality.
    139    - Exchange returns ``σ[γ][]`` on success.
    140 
    141 It is worth noting that, in contrast to the existing refresh protocol, the
    142 client sends all ``n*κ`` tuples ``m[][]`` already in the commitment phase.  This is
    143 necessary such that the exchange can sign the request with a valid denomination
    144 key *at the moment of melting*.  This ensures idempotency of the melting/commit
    145 request and that caries over to the reveal phase.
    146 
    147 Also note, as described above, the master seed ``r`` for the refresh operation
    148 is a **public** value and can be considered a commitment ("I'm going to sign
    149 this value") made by the dirty coin.  The actual *secrets* are the signatures
    150 which are reveal in the second phase.
    151 
    152 Note that for the Linking protocol, given the dirty coin's public key, the
    153 Exchange simply returns the master seed ``r`` and the dirty coins' signature
    154 ``σ_c`` over the original refresh request.  The owner of the private key of
    155 the dirty coin can then replay the refresh protocol and can be sure that the
    156 master seed was of its own origin.  Furthermore, the coin history endpoint
    157 must already return this information (so that clients can verify the old coin
    158 history about the refresh), thus obsoleting the need for a separate "/link"
    159 endpoint.
    160 
    161 
    162 Database Changes
    163 ^^^^^^^^^^^^^^^^
    164 
    165 Not taking sharding and coinstraints into account, the table layout will look
    166 basically like this (names might change):
    167 
    168 .. table:: SQL table layout for refresh
    169    :align: left
    170 
    171    ==============  ============  ================================================
    172    Field           Type          Description
    173    ==============  ============  ================================================
    174    refresh_id      BIGINT        autoincremented identity of the record
    175    rc              BYTEA         refresh commitment ``h_m``, serving as primary key
    176    timestamp       INT8          execution date of the refresh
    177    amount          taler_amount  amount with fee of the refresh
    178    old_coin_pub    BYTEA         old coin's public key ``Cp``
    179    old_coin_sig    BYTEA         old coin's signature over the refresh request
    180    old_age_com_h   BYTEA         old coin's hash of age commitment, if applicable
    181    noreveal_index  SMALLINT      the ``γ`` for cut-and-choose, chosen by the exchange
    182    planchets_h     BYTEA         the hash over *all* blinded coin envelopes ``m[][]``
    183    selected_h      BYTEA         the hash over only the *selected* blinded envelopes ``m[γ][]``
    184    refresh_seed    BYTEA         the master seed for the refresh, the ``r`` above
    185    blinding_seed   BYTEA         the master seed for CS nonces
    186    cs_r_values     BYTEA[]       the pairs of R-Values for CS signatures
    187    cs_r_choices    INT8          the bitvector representing the chosen public R-Values
    188    denom_serials   INT8[]        the row ID's of the denominations in the DB
    189    denom_sigs      BYTEA[]       the (blinded) denom signatures ``σ[γ][]``
    190    ==============  ============  ================================================
    191 
    192 
    193 API endpoints
    194 ^^^^^^^^^^^^^^
    195 
    196 A new ``/melt`` request takes a `NewMeltRequest` as request body, see below.
    197 As in the existing melting/commit phase, it updates the old coin balance and
    198 chooses a random ``γ`` for the cut-and-choose protocol.  Taler uses a global
    199 parameter ``κ`` for the cut-and-choose component of the protocol, for which
    200 this request is the commitment.  Thus, various arguments are given ``κ``-times
    201 in this step.  At present ``κ`` is always 3.
    202 
    203   :http:statuscode:`200 OK`:
    204     The request was successful.  The response body is `MeltResponse` in this case.
    205   :http:statuscode:`403 Forbidden`:
    206     One of the signatures (by the old coin or the denomination signature over
    207     the old coin) is invalid.
    208   :http:statuscode:`404 Not found`:
    209     The exchange does not recognize the denomination key as belonging to the exchange,
    210     or it is past the legal expiration time (and thus forgotten entirely).
    211     If the denomination key is unknown, the response will be
    212     a `DenominationUnknownMessage`.
    213   :http:statuscode:`409 Conflict`:
    214     The operation is not allowed as the coin has insufficient
    215     residual value, or because the same public key of the coin has been
    216     previously used with a different denomination.  Which case it is
    217     can be decided by looking at the error code
    218     (``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` or
    219     ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``).
    220     The response is `MeltForbiddenResponse` in both cases.
    221   :http:statuscode:`410 Gone`:
    222     The requested denomination key is not yet or no longer valid.
    223     It either before the validity start, past the expiration or was revoked.
    224     The response is a `DenominationGoneMessage`.
    225     Clients must evaluate the error code provided to understand which of the
    226     cases this is and handle it accordingly.
    227 
    228 
    229 Wire Formats
    230 ^^^^^^^^^^^^
    231 
    232 Modified melt request structure:
    233 
    234 .. ts:def:: NewMeltRequest
    235 
    236     interface NewMeltRequest {
    237        // The old coin's public key
    238        old_coin_pub: CoinPublicKey;
    239 
    240        // Hash of the denomination public key of the old coin, to determine total coin value.
    241        old_denom_pub_h: HashCode;
    242 
    243        // The hash of the age-commitment for the old coin. Only present
    244        // if the denomination has support for age restriction.
    245        old_age_commitment_h?: AgeCommitmentHash;
    246 
    247        // Signature over the old `coin public key <eddsa-coin-pub>` by the denomination.
    248        old_denom_sig: DenominationSignature;
    249 
    250        // Amount of the value of the old coin that should be melted as part of
    251        // this refresh operation, including melting fee.
    252        value_with_fee: Amount;
    253 
    254        // Array of ``n`` new hash codes of denomination public keys
    255        // for the new coins to order.
    256        denoms_h: HashCode[];
    257 
    258        // Seed from which the nonces for the ``n*kappa`` coin candidates are derived
    259        // from.
    260        refresh_seed: HashCode;
    261 
    262        // Master seed for the Clause-Schnorr R-value
    263        // creation. Must match the /blinding-prepare request.
    264        // Must not have been used in any prior melt request.
    265        // Must be present if and only if one of the fresh coin's
    266        // denominations is of type Clause-Schnorr.
    267        blinding_seed?: BlindingMasterSeed;
    268 
    269        // kappa arrays of ``n`` entries for blinded coin candidates,
    270        // each matching the respective entries in ``denoms_h``.
    271        //
    272        // Note: These are essentially the m_i values in the RefreshDeriveBatch
    273        // function.
    274        coin_evs: CoinEnvelope[kappa][];
    275 
    276        // Signature by the old `coin <coin-priv>` over `TALER_RefreshMeltCoinAffirmationPS`.
    277        confirm_sig: CoinSignature;
    278 
    279     }
    280 
    281 
    282 TODO: definition of ``CoinSignature``
    283 
    284 .. ts:def:: CoinSignature
    285 
    286    // TODO: this needs to be fully expanded into a new interface
    287    type CoinSignature = string;
    288 
    289 
    290 TODO: explain /reveal-melt endpoint.
    291 
    292 .. ts:def:: NewMeltRevealRequest
    293 
    294    interface NewMeltRevealRequest {
    295       // The refresh commitment corresponding to the previous call to /melt
    296       // This is the Hash over:
    297       //   1. refresh_seed
    298       //   2. hash of all pairs of R-values, if applicable, skip otherwise
    299       //   3. denominations in order
    300       //   4. amount_with_fee
    301       //   5. kappa*n blinded planchet hashes (which include denomination information),
    302       //      depths first: [0..n)[0..n)[0..n)
    303       rc: RefreshCommitmentHash;
    304 
    305       // The disclosed kappaκ-1 signatures by the old coin's private key,
    306       // over Hash1a("Refresh", Cp, r, i), where Cp is the melted coin's public key,
    307       // r is the public refresh nonce from the metling step and i runs over the
    308       // _disclosed_ kappaκ-1 indices.
    309       signatures: CoinSignature[kappa-1];
    310 
    311       // IFF the denomination of the old coin had support for age restriction,
    312       // the client MUST provide the original age commitment, i. e. the
    313       // vector of public keys, or omitted otherwise.
    314       // The size of the vector MUST be the number of age groups as defined by the
    315       // Exchange in the field ``.age_groups`` of the extension ``age_restriction``.
    316       age_commitment?: Edx25519PublicKey[];
    317 
    318       // NOTE: THIS FIELD IS WORK-IN-PROGRESS
    319       // The old coin signs H(rc, noreveal_index).  This value MUST be stored by the
    320       // exchange and provided as part of the coin's history.  That way, the wallet
    321       // can be sure that the exchange hasn't altered the nonreveal_index when the
    322       // wallet does an idempotent melt request.
    323       noreveal_sig: CoinSignature;
    324    }
    325 
    326 Security Analysis
    327 =================
    328 
    329 TODO
    330 
    331 Drawbacks
    332 =========
    333 
    334 TODO
    335 
    336 Migration Strategy
    337 ==================
    338 
    339 TODO
    340 
    341 Discussion/Q&A
    342 ==============
    343 
    344 TODO
    345 
    346 
    347 References
    348 ==========
    349 .. [1] Work by Jonathan Levin et al., TU Eindhoven. Reference be added here once published