Compare commits

..

No commits in common. "main" and "v1.1.1-aio.1" have entirely different histories.

2 changed files with 1 additions and 39 deletions

View file

@ -2,7 +2,7 @@
"name": "Bolt Cards", "name": "Bolt Cards",
"short_description": "Self custody Bolt Cards with one time LNURLw", "short_description": "Self custody Bolt Cards with one time LNURLw",
"tile": "/boltcards/static/image/boltcard.png", "tile": "/boltcards/static/image/boltcard.png",
"version": "1.1.1-aio.2", "version": "1.1.1-aio.1",
"min_lnbits_version": "1.3.0", "min_lnbits_version": "1.3.0",
"contributors": [ "contributors": [
{ {

View file

@ -394,41 +394,3 @@ async def pay_callback(
action = MessageAction(message=Max144Str("Topped up!")) action = MessageAction(message=Max144Str("Topped up!"))
invoice = parse_obj_as(LightningInvoice, payment.bolt11) invoice = parse_obj_as(LightningInvoice, payment.bolt11)
return LnurlPayActionResponse(pr=invoice, successAction=action) return LnurlPayActionResponse(pr=invoice, successAction=action)
###############ACCESS-CONTROL VERIFY (doors)#################
# /boltcards/api/v1/verify/{external_id}?p=<32-hex>&c=<16-hex>
# Side-effect-light SUN check for access control (e.g. doors via the `access`
# extension): confirm the tap is a genuine, non-replayed card and return its
# identity — WITHOUT /scan's spend semantics (no withdrawRequest, no daily-limit
# check, no "hit" record). It DOES advance the SUN counter, exactly like /scan,
# so a captured p/c can't be replayed. Named `verify` because `/auth` is already
# the card-programming OTP endpoint.
@boltcards_lnurl_router.get("/api/v1/verify/{external_id}")
async def api_verify(p, c, external_id: str):
p = p.upper()
c = c.upper()
card = await get_card_by_external_id(external_id)
if not card:
return {"authenticated": False, "reason": "Card not found."}
if not card.enable:
return {"authenticated": False, "reason": "Card is disabled."}
try:
card_uid, counter = decrypt_sun(bytes.fromhex(p), bytes.fromhex(card.k1))
if card.uid.upper() != card_uid.hex().upper():
return {"authenticated": False, "reason": "Card UID mis-match."}
if c != get_sun_mac(card_uid, counter, bytes.fromhex(card.k2)).hex().upper():
return {"authenticated": False, "reason": "CMAC does not check."}
except Exception:
return {"authenticated": False, "reason": "Error decrypting card."}
ctr_int = int.from_bytes(counter, "little")
if ctr_int <= card.counter:
return {"authenticated": False, "reason": "This link is already used."}
await update_card_counter(ctr_int, card.id)
return {
"authenticated": True,
"external_id": card.external_id,
"card_name": card.card_name,
}