Skip to content

insufficient error handling in RistrettoRedeemer.redeemWithCounter

        success = result.get(u"success", False)
        if not success:
            reason = result.get(u"reason", None)
            if reason == u"double-spend":
                raise AlreadySpent(voucher)
            elif reason == u"unpaid":
                raise Unpaid(voucher)

        self._log.info(
            "Redeemed: {public_key} {proof} {count}",
            public_key=result[u"public-key"],
            proof=result[u"proof"],
            count=len(result[u"signatures"]),
        )

Notice that the not success case falls through the end if the reason is not one of the two given. Then the success-case code runs. And if it was really not success then the success-case code path will probably break.

This was probably observed in the real world due to disagreement between required token count on client and server. The report from ZKAPAuthorizer was:

2021-09-27T10:08:51-0400 [_zkapauthorizer.controller.PaymentController#error] Redeeming random tokens for a voucher (6kb... failed: KeyError(u'public-key',)
2021-09-27T10:08:51-0400 [_zkapauthorizer.controller.PaymentController#info] Temporarily suspending redemption of 6kb... after non-success result.