20.97. DD 97: Challenge-Signature Payment Confirmations#

20.97.1. Summary#

This document proposes challenge-signature payment confirmations for unattended devices that cannot contact the merchant backend themselves. The device generates a fresh challenge, the merchant backend signs it after payment, and the device verifies the signature before performing its local action.

Note

This design document proposes an extension for discussion. The protocol version name vChallengeConfirmation is a placeholder.

20.97.2. Motivation#

Some unattended devices must verify that a Taler payment succeeded without being online themselves. Existing point-of-sale confirmations use a time-based one-time password (TOTP). A TOTP proves knowledge of a shared secret, but it does not bind the confirmation to a challenge freshly chosen by the device.

Two examples motivate a challenge-bound confirmation:

  • A coffee machine generates a fresh challenge and displays or transmits it to the customer’s wallet. After payment, the machine dispenses the selected product only if it receives a valid signature for that challenge.

  • An electronic farmer tag attached to an unattended farm stand or collection point generates a fresh challenge before a local action. It can verify the payment confirmation even when the tag itself has no network connection.

The merchant backend remains online and processes the payment normally. The offline device only needs a public key and a source of unpredictable challenges.

20.97.3. Requirements#

  • The confirmation must be bound to a fresh challenge generated by the offline device.

  • The merchant backend must generate and retain the signing private key.

  • The offline device must only require the corresponding public key.

  • The wallet must be able to transport the challenge and confirmation without interpreting device-specific behavior.

  • Replaying an accepted confirmation must not authorize another local action.

  • Existing TOTP confirmation algorithms must remain unchanged.

20.97.4. Proposed Solution#

The merchant backend’s OTP algorithm enumeration is extended with two challenge-signature algorithms:

  • ECDSA_CHALLENGE (numeric value 3) uses ECDSA with NIST P-256.

  • EDDSA_CHALLENGE (numeric value 4) uses EdDSA with Ed25519.

When a challenge-signature OTP device is created, the merchant backend generates the signing key pair. It stores the private key and returns the public key to the merchant application. The public key is then configured in the offline verifier. The private key is never exposed through the API.

For these algorithms, the client supplies a fresh 32-byte challenge while instantiating a template. The challenge is encoded using Crockford Base32 and becomes part of the order. After successful payment, the backend signs the challenge with the key associated with the template’s OTP device. The signature is returned in PaymentResponse.pos_confirmation.

The wallet only transports the challenge to the merchant and the resulting signature back to the device. It does not possess the signing key and does not decide whether the local action is allowed.

20.97.5. Payment Flow#

  1. The offline device generates a cryptographically unpredictable challenge.

  2. The wallet obtains the challenge from the device and includes it as challenge when instantiating the merchant template.

  3. The merchant backend creates the order and the wallet pays it using the normal Taler payment protocol.

  4. After accepting the payment, the backend signs the order’s challenge and returns the encoded signature as pos_confirmation.

  5. The wallet relays the confirmation to the device.

  6. The device verifies the signature using its configured public key and performs the local action only after successful verification.

The device must reject a challenge after it has accepted a confirmation for that challenge. This prevents replaying a previous payment confirmation to obtain another product or action.

20.97.6. Protocol Changes#

20.97.6.1. OTP Device Creation#

The existing merchant OTP device API accepts values 3 and 4 for OtpDeviceAddDetails.otp_algorithm when creating a device. For these values, OtpDeviceAddDetails.otp_key must be omitted because the backend generates the key pair.

Successful creation returns:

interface OtpDeviceCreateResponse {
  // Public key generated for the challenge-signature device.
  // For ECDSA this is a compressed NIST P-256 point.
  // For EdDSA this is an Ed25519 public key.
  // Crockford Base32 encoded.
  otp_device_pub: string;
}

The response for reading an OTP device exposes the same public key as the optional OtpDeviceDetails.otp_device_pub field.

20.97.6.2. Template Instantiation#

The common template request gains:

interface UsingTemplateCommonRequestChallenge {
  // Fresh challenge generated by the offline verifier.
  // Exactly 32 bytes, Crockford Base32 encoded.
  challenge?: string;
}

The field is required when the template references an OTP device using ECDSA_CHALLENGE or EDDSA_CHALLENGE and must otherwise be rejected.

20.97.6.3. Payment Response#

For a challenge-signature OTP device, the existing PaymentResponse.pos_confirmation field contains the Crockford Base32 encoded signature over the order’s challenge.

20.97.7. Error Handling#

Template instantiation fails if:

  • the challenge is absent for a challenge-signature OTP device;

  • the challenge is present for an OTP algorithm that does not use it;

  • the challenge is not valid Crockford Base32; or

  • the decoded challenge does not have the required length.

Payment processing fails closed if the backend cannot load the device’s private key or cannot produce the signature. It must not return a successful confirmation with an empty or malformed pos_confirmation.

20.97.8. Security Considerations#

Challenges must be generated with a cryptographically secure random number generator. Predictable challenges would permit an attacker to prepare a confirmation before the device requests it.

An offline verifier must remember challenges that have already been accepted, at least for as long as replaying them could cause a second local action. A device with volatile state should generate challenges from a sufficiently large random space and invalidate the current challenge immediately after successful verification.

The verifier’s configured public key is security-sensitive configuration. Replacing it changes which merchant backend can authorize the device.

The signature construction must use domain separation defined by the merchant protocol. It should bind the signature to the challenge and the relevant device or order context so that signatures cannot be reused across protocol purposes.

20.97.9. Privacy Considerations#

The challenge is random and carries no customer identity. The offline device does not learn the customer’s Taler payment credentials. The wallet learns that the payment is associated with a local challenge, which is inherent in relaying the confirmation.

20.97.10. Drawbacks#

The device must implement public-key signature verification and secure replay handling, which is more demanding than displaying or checking a short TOTP.

If the payment succeeds but the wallet cannot relay the confirmation, the merchant has received the payment while the local action has not happened. Applications need a recovery or refund procedure appropriate to the product.

20.97.11. Test Plan#

  • Test OTP device creation for both challenge-signature algorithms and verify that the private key is never returned.

  • Test rejection of supplied otp_key values for challenge-signature devices.

  • Test template instantiation with missing, malformed, short and unexpected challenges.

  • Test that a successful payment returns a signature accepted by the corresponding public key and rejected by another key.

  • Test that modifying the challenge invalidates the confirmation.

  • Test coffee-machine and farmer-tag integrations with repeated and replayed challenges.

20.97.12. Definition of Done#

  • The merchant backend supports OTP algorithms 3 and 4, generates their key pairs and exposes only their public keys.

  • Template instantiation validates and stores a 32-byte challenge for these algorithms.

  • Successful payment returns a challenge-bound signature in pos_confirmation.

  • Wallet Core can transport the challenge and relay the confirmation without interpreting device-specific behavior.

  • At least one offline verifier implementation validates confirmations and rejects replayed challenges.