taler-docs

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

097-challenge-confirmations.rst (9411B)


      1 .. _dd-97:
      2 
      3 DD 97: Challenge-Signature Payment Confirmations
      4 ################################################
      5 
      6 :Design status: Draft
      7 :Implementation status: Partial
      8 :DD shepherd: TBD
      9 :Historical contributors: Bohdan Potuzhnyi
     10 :First published: 2026-07-23
     11 :Last substantive change: 2026-07-23
     12 :Implementation evidence: ``exchange`` (2026-08-01); ``merchant`` (2026-08-02, not merged into the reviewed HEAD)
     13 :Normative references: ``core/api-merchant.rst`` (upcoming version) and ``core/merchant/post-orders-ORDER_ID-pay.rst``
     14 
     15 Summary
     16 =======
     17 
     18 This document proposes challenge-signature payment confirmations for
     19 unattended devices that cannot contact the merchant backend themselves.
     20 The device generates a fresh challenge, the merchant backend signs it after
     21 payment, and the device verifies the signature before performing its local
     22 action.
     23 
     24 .. note::
     25 
     26   This design document proposes an extension for discussion.  The protocol
     27   version name ``vChallengeConfirmation`` is a placeholder.
     28 
     29 Motivation
     30 ==========
     31 
     32 Some unattended devices must verify that a Taler payment succeeded without
     33 being online themselves.  Existing point-of-sale confirmations use a
     34 time-based one-time password (TOTP).  A TOTP proves knowledge of a shared
     35 secret, but it does not bind the confirmation to a challenge freshly chosen
     36 by the device.
     37 
     38 Two examples motivate a challenge-bound confirmation:
     39 
     40 * A coffee machine generates a fresh challenge and displays or transmits it
     41   to the customer's wallet.  After payment, the machine dispenses the
     42   selected product only if it receives a valid signature for that challenge.
     43 
     44 * An electronic farmer tag attached to an unattended farm stand or collection
     45   point generates a fresh challenge before a local action.  It can verify the
     46   payment confirmation even when the tag itself has no network connection.
     47 
     48 The merchant backend remains online and processes the payment normally.  The
     49 offline device only needs a public key and a source of unpredictable
     50 challenges.
     51 
     52 Requirements
     53 ============
     54 
     55 * The confirmation must be bound to a fresh challenge generated by the
     56   offline device.
     57 
     58 * The merchant backend must generate and retain the signing private key.
     59 
     60 * The offline device must only require the corresponding public key.
     61 
     62 * The wallet must be able to transport the challenge and confirmation without
     63   interpreting device-specific behavior.
     64 
     65 * Replaying an accepted confirmation must not authorize another local action.
     66 
     67 * Existing TOTP confirmation algorithms must remain unchanged.
     68 
     69 Proposed Solution
     70 =================
     71 
     72 The merchant backend's OTP algorithm enumeration is extended with two
     73 challenge-signature algorithms:
     74 
     75 * ``ECDSA_CHALLENGE`` (numeric value 3) uses ECDSA with NIST P-256.
     76 
     77 * ``EDDSA_CHALLENGE`` (numeric value 4) uses EdDSA with Ed25519.
     78 
     79 When a challenge-signature OTP device is created, the merchant backend
     80 generates the signing key pair.  It stores the private key and returns the
     81 public key to the merchant application.  The public key is then configured
     82 in the offline verifier.  The private key is never exposed through the API.
     83 
     84 For these algorithms, the client supplies a fresh 32-byte challenge while
     85 instantiating a template.  The challenge is encoded using Crockford Base32
     86 and becomes part of the order.  After successful payment, the backend signs
     87 the protocol-defined hash of the challenge with the key associated with the
     88 template's OTP device.  The signature is returned in
     89 ``PaymentResponse.pos_confirmation``.
     90 
     91 The wallet only transports the challenge to the merchant and the resulting
     92 signature back to the device.  It does not possess the signing key and does
     93 not decide whether the local action is allowed.
     94 
     95 Payment Flow
     96 ============
     97 
     98 1. The offline device generates a cryptographically unpredictable challenge.
     99 
    100 2. The wallet obtains the challenge from the device and includes it as
    101    ``challenge`` when instantiating the merchant template.
    102 
    103 3. The merchant backend creates the order and the wallet pays it using the
    104    normal Taler payment protocol.
    105 
    106 4. After accepting the payment, the backend signs the order's challenge and
    107    returns the encoded signature as ``pos_confirmation``.
    108 
    109 5. The wallet relays the confirmation to the device.
    110 
    111 6. The device verifies the signature using its configured public key and
    112    performs the local action only after successful verification.
    113 
    114 The device must reject a challenge after it has accepted a confirmation for
    115 that challenge.  This prevents replaying a previous payment confirmation to
    116 obtain another product or action.
    117 
    118 Protocol Changes
    119 ================
    120 
    121 OTP Device Creation
    122 -------------------
    123 
    124 The existing :ref:`merchant OTP device API <merchant-otp-device-api>` accepts
    125 values 3 and 4 for ``OtpDeviceAddDetails.otp_algorithm`` when creating a
    126 device.  For these values, ``OtpDeviceAddDetails.otp_key`` must be omitted
    127 because the backend generates the key pair.
    128 
    129 Successful creation returns:
    130 
    131 .. ts:def:: OtpDeviceCreateResponse
    132 
    133   interface OtpDeviceCreateResponse {
    134     // Public key generated for the challenge-signature device.
    135     // For ECDSA this is a compressed NIST P-256 point.
    136     // For EdDSA this is an Ed25519 public key.
    137     // Crockford Base32 encoded.
    138     otp_device_pub: string;
    139   }
    140 
    141 The response for reading an OTP device exposes the same public key as the
    142 optional ``OtpDeviceDetails.otp_device_pub`` field.
    143 
    144 Template Instantiation
    145 ----------------------
    146 
    147 The common template request gains:
    148 
    149 .. ts:def:: UsingTemplateCommonRequestChallenge
    150 
    151   interface UsingTemplateCommonRequestChallenge {
    152     // Fresh challenge generated by the offline verifier.
    153     // Exactly 32 bytes, Crockford Base32 encoded.
    154     challenge?: string;
    155   }
    156 
    157 The field is required when the template references an OTP device using
    158 ``ECDSA_CHALLENGE`` or ``EDDSA_CHALLENGE`` and must otherwise be rejected.
    159 
    160 Payment Response
    161 ----------------
    162 
    163 For a challenge-signature OTP device, the existing
    164 ``PaymentResponse.pos_confirmation`` field contains the Crockford Base32
    165 encoded signature over the protocol-defined hash of the order's challenge.
    166 
    167 Error Handling
    168 ==============
    169 
    170 Template instantiation fails if:
    171 
    172 * the challenge is absent for a challenge-signature OTP device;
    173 
    174 * the challenge is present for an OTP algorithm that does not use it;
    175 
    176 * the challenge is not valid Crockford Base32; or
    177 
    178 * the decoded challenge does not have the required length.
    179 
    180 Payment processing fails closed if the backend cannot load the device's
    181 private key or cannot produce the signature.  It must not return a successful
    182 confirmation with an empty or malformed ``pos_confirmation``.
    183 
    184 Security Considerations
    185 =======================
    186 
    187 Challenges must be generated with a cryptographically secure random number
    188 generator.  Predictable challenges would permit an attacker to prepare a
    189 confirmation before the device requests it.
    190 
    191 An offline verifier must remember challenges that have already been accepted,
    192 at least for as long as replaying them could cause a second local action.  A
    193 device with volatile state should generate challenges from a sufficiently
    194 large random space and invalidate the current challenge immediately after
    195 successful verification.
    196 
    197 The verifier's configured public key is security-sensitive configuration.
    198 Replacing it changes which merchant backend can authorize the device.
    199 
    200 The signature construction must use domain separation defined by the merchant
    201 protocol.  It should bind the signature to the challenge and the relevant
    202 device or order context so that signatures cannot be reused across protocol
    203 purposes.
    204 
    205 Privacy Considerations
    206 ======================
    207 
    208 The challenge is random and carries no customer identity.  The offline device
    209 does not learn the customer's Taler payment credentials.  The wallet learns
    210 that the payment is associated with a local challenge, which is inherent in
    211 relaying the confirmation.
    212 
    213 Drawbacks
    214 =========
    215 
    216 The device must implement public-key signature verification and secure replay
    217 handling, which is more demanding than displaying or checking a short TOTP.
    218 
    219 If the payment succeeds but the wallet cannot relay the confirmation, the
    220 merchant has received the payment while the local action has not happened.
    221 Applications need a recovery or refund procedure appropriate to the product.
    222 
    223 Test Plan
    224 =========
    225 
    226 * Test OTP device creation for both challenge-signature algorithms and verify
    227   that the private key is never returned.
    228 
    229 * Test rejection of supplied ``otp_key`` values for challenge-signature
    230   devices.
    231 
    232 * Test template instantiation with missing, malformed, short and unexpected
    233   challenges.
    234 
    235 * Test that a successful payment returns a signature accepted by the
    236   corresponding public key and rejected by another key.
    237 
    238 * Test that modifying the challenge invalidates the confirmation.
    239 
    240 * Test coffee-machine and farmer-tag integrations with repeated and replayed
    241   challenges.
    242 
    243 Definition of Done
    244 ==================
    245 
    246 * The merchant backend supports OTP algorithms 3 and 4, generates their key
    247   pairs and exposes only their public keys.
    248 
    249 * Template instantiation validates and stores a 32-byte challenge for these
    250   algorithms.
    251 
    252 * Successful payment returns a challenge-bound signature in
    253   ``pos_confirmation``.
    254 
    255 * Wallet Core can transport the challenge and relay the confirmation without
    256   interpreting device-specific behavior.
    257 
    258 * At least one offline verifier implementation validates confirmations and
    259   rejects replayed challenges.