Fix: Boltcard would get bricked during reset from the balance view with wrong card (#6400)

This commit is contained in:
Nicolas Dorier
2024-11-14 13:14:36 +09:00
committed by GitHub
parent 777748d79e
commit 31b73f0d82
2 changed files with 57 additions and 6 deletions

View File

@@ -1416,7 +1416,8 @@ namespace BTCPayServer.Tests
Assert.DoesNotContain(null, card1keys);
var card2 = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
{
UID = uid
UID = uid,
OnExisting = OnExistingBehavior.UpdateVersion
});
Assert.Equal(1, card2.Version);
Assert.StartsWith("lnurlw://", card2.LNURLW);

View File

@@ -189,6 +189,7 @@ namespace BTCPayServer.Controllers.Greenfield
if (pp is null)
return PullPaymentNotFound();
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
BoltcardPICCData? picc = null;
// LNURLW is used by deeplinks
if (request?.LNURLW is not null)
@@ -204,11 +205,12 @@ namespace BTCPayServer.Controllers.Greenfield
ModelState.AddModelError(nameof(request.LNURLW), "The LNURLW should contains a 'p=' parameter");
return this.CreateValidationError(ModelState);
}
if (issuerKey.TryDecrypt(p) is not BoltcardPICCData picc)
if (issuerKey.TryDecrypt(p) is not BoltcardPICCData o)
{
ModelState.AddModelError(nameof(request.LNURLW), "The LNURLW 'p=' parameter cannot be decrypted");
return this.CreateValidationError(ModelState);
}
picc = o;
request.UID = picc.Uid;
}
@@ -230,8 +232,54 @@ namespace BTCPayServer.Controllers.Greenfield
_ => request.OnExisting
};
var version = await _dbContextFactory.LinkBoltcardToPullPayment(pullPaymentId, issuerKey, request.UID, request.OnExisting);
var keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, pullPaymentId).DeriveBoltcardKeys(issuerKey);
BoltcardKeys keys;
int version;
var registration = await _dbContextFactory.GetBoltcardRegistration(issuerKey, request.UID);
if (request.OnExisting is OnExistingBehavior.UpdateVersion ||
(request.OnExisting is null && registration?.PullPaymentId is null))
{
version = await _dbContextFactory.LinkBoltcardToPullPayment(pullPaymentId, issuerKey, request.UID, request.OnExisting);
keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, pullPaymentId).DeriveBoltcardKeys(issuerKey);
}
else
{
if (registration?.PullPaymentId is null)
{
ModelState.AddModelError(nameof(request.UID), "This card isn't registered");
return this.CreateValidationError(ModelState);
}
if (pullPaymentId != registration.PullPaymentId)
{
ModelState.AddModelError(nameof(request.UID), "This card is registered on a different pull payment");
return this.CreateValidationError(ModelState);
}
var ppId = registration.PullPaymentId;
version = registration.Version;
int retryCount = 0;
retry:
keys = issuerKey.CreatePullPaymentCardKey(request!.UID, version, ppId).DeriveBoltcardKeys(issuerKey);
// The server version may be higher than the card.
// If that is the case, let's try a few versions until we find the right one
// by checking c.
if (request?.LNURLW is { } lnurlw &&
ExtractC(lnurlw) is string c &&
picc is not null)
{
if (!keys.AuthenticationKey.CheckSunMac(c, picc))
{
retryCount++;
version--;
if (version < 0 || retryCount > 5)
{
ModelState.AddModelError(nameof(request.UID), "Unable to get keys of this card, it might be caused by a version mismatch");
return this.CreateValidationError(ModelState);
}
goto retry;
}
}
}
var boltcardUrl = Url.Action(nameof(UIBoltcardController.GetWithdrawRequest), "UIBoltcard");
boltcardUrl = Request.GetAbsoluteUri(boltcardUrl);
@@ -250,7 +298,7 @@ namespace BTCPayServer.Controllers.Greenfield
return Ok(resp);
}
private string? ExtractP(string? url)
public static string? Extract(string? url, string param, int size)
{
if (url is null || !Uri.TryCreate(url, UriKind.Absolute, out var uri))
return null;
@@ -258,11 +306,13 @@ namespace BTCPayServer.Controllers.Greenfield
if (num == -1)
return null;
string input = uri.AbsoluteUri.Substring(num);
Match match = Regex.Match(input, "p=([a-f0-9A-F]{32})");
Match match = Regex.Match(input, param + "=([a-f0-9A-F]{" + size + "})");
if (!match.Success)
return null;
return match.Groups[1].Value;
}
public static string? ExtractP(string? url) => Extract(url, "p", 32);
public static string? ExtractC(string? url) => Extract(url, "c", 16);
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}")]
[AllowAnonymous]