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.