mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-02 20:36:44 +00:00
add support for lnurl-auth authentication by registering a platform uri handler
This commit is contained in:
parent
6efe5e4ccc
commit
80fab6df99
10 changed files with 327 additions and 19 deletions
|
@ -218,7 +218,7 @@ jlink {
|
|||
appVersion = "${sparrowVersion}"
|
||||
skipInstaller = os.macOsX || properties.skipInstallers
|
||||
imageOptions = []
|
||||
installerOptions = ['--file-associations', 'src/main/deploy/psbt.properties', '--file-associations', 'src/main/deploy/txn.properties', '--file-associations', 'src/main/deploy/bitcoin.properties', '--file-associations', 'src/main/deploy/auth47.properties', '--license-file', 'LICENSE']
|
||||
installerOptions = ['--file-associations', 'src/main/deploy/psbt.properties', '--file-associations', 'src/main/deploy/txn.properties', '--file-associations', 'src/main/deploy/bitcoin.properties', '--file-associations', 'src/main/deploy/auth47.properties', '--file-associations', 'src/main/deploy/lightning.properties', '--license-file', 'LICENSE']
|
||||
if(os.windows) {
|
||||
installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-menu-group', 'Sparrow', '--win-shortcut', '--resource-dir', 'src/main/deploy/package/windows/']
|
||||
imageOptions += ['--icon', 'src/main/deploy/package/windows/sparrow.ico']
|
||||
|
|
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit ddaf698c1011e5b01c7c3ed1e7693145ba5531ac
|
||||
Subproject commit 8cdea77562643edf9d460a594178c1f44deeb248
|
2
src/main/deploy/lightning.properties
Normal file
2
src/main/deploy/lightning.properties
Normal file
|
@ -0,0 +1,2 @@
|
|||
mime-type=x-scheme-handler/lightning
|
||||
description=LNURL URI
|
|
@ -6,4 +6,4 @@ Icon=/opt/sparrow/lib/Sparrow.png
|
|||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Unknown
|
||||
MimeType=application/psbt;application/bitcoin-transaction;x-scheme-handler/bitcoin;x-scheme-handler/auth47
|
||||
MimeType=application/psbt;application/bitcoin-transaction;x-scheme-handler/bitcoin;x-scheme-handler/auth47;x-scheme-handler/lightning
|
|
@ -53,6 +53,14 @@
|
|||
<string>auth47</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.sparrowwallet.sparrow.lightning</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>lightning</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
|
|
|
@ -97,6 +97,16 @@
|
|||
<RegistryValue Type="string" Value=""[INSTALLDIR]$(var.JpAppName).exe" "%1"" />
|
||||
</RegistryKey>
|
||||
</RegistryKey>
|
||||
<RegistryKey Root="HKCR" Key="lightning" Action="createAndRemoveOnUninstall">
|
||||
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
|
||||
<RegistryValue Type="string" Value="URL:LNURL URI"/>
|
||||
<RegistryKey Key="DefaultIcon">
|
||||
<RegistryValue Type="string" Value="$(var.JpAppName).exe" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Key="shell\open\command">
|
||||
<RegistryValue Type="string" Value=""[INSTALLDIR]$(var.JpAppName).exe" "%1"" />
|
||||
</RegistryKey>
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
|
|
|
@ -3,7 +3,16 @@ package com.sparrowwallet.sparrow;
|
|||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import com.sparrowwallet.drongo.Network;
|
||||
import com.sparrowwallet.drongo.SecureString;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.bip47.PaymentCode;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.crypto.EncryptionType;
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
|
||||
import com.sparrowwallet.drongo.crypto.Key;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.wallet.*;
|
||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import com.sparrowwallet.sparrow.net.Auth47;
|
||||
import com.sparrowwallet.drongo.protocol.BlockHeader;
|
||||
|
@ -11,10 +20,6 @@ import com.sparrowwallet.drongo.protocol.ScriptType;
|
|||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.uri.BitcoinURI;
|
||||
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
|
||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.drongo.wallet.WalletTransaction;
|
||||
import com.sparrowwallet.sparrow.control.TextUtils;
|
||||
import com.sparrowwallet.sparrow.control.TrayManager;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
|
@ -881,6 +886,8 @@ public class AppServices {
|
|||
openBitcoinUri(uri);
|
||||
} else if(("auth47").equals(uri.getScheme())) {
|
||||
openAuth47Uri(uri);
|
||||
} else if(("lightning").equals(uri.getScheme())) {
|
||||
openLnurlAuthUri(uri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -903,11 +910,13 @@ public class AppServices {
|
|||
private static void openBitcoinUri(URI uri) {
|
||||
try {
|
||||
BitcoinURI bitcoinURI = new BitcoinURI(uri.toString());
|
||||
Wallet wallet = selectWallet(null, null, "pay from");
|
||||
List<PolicyType> policyTypes = Arrays.asList(PolicyType.values());
|
||||
List<ScriptType> scriptTypes = Arrays.asList(ScriptType.ADDRESSABLE_TYPES);
|
||||
Wallet wallet = selectWallet(policyTypes, scriptTypes, true, false, "pay from", false);
|
||||
|
||||
if(wallet != null) {
|
||||
final Wallet sendingWallet = wallet;
|
||||
EventManager.get().post(new SendActionEvent(sendingWallet, new ArrayList<>(sendingWallet.getWalletUtxos().keySet())));
|
||||
EventManager.get().post(new SendActionEvent(sendingWallet, new ArrayList<>(sendingWallet.getWalletUtxos().keySet()), true));
|
||||
Platform.runLater(() -> EventManager.get().post(new SendPaymentsEvent(sendingWallet, List.of(bitcoinURI.toPayment()))));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
|
@ -915,31 +924,97 @@ public class AppServices {
|
|||
}
|
||||
}
|
||||
|
||||
public static void openAuth47Uri(URI uri) {
|
||||
private static void openAuth47Uri(URI uri) {
|
||||
try {
|
||||
Auth47 auth47 = new Auth47(uri);
|
||||
Wallet wallet = selectWallet(null, Boolean.TRUE, "authenticate using your payment code");
|
||||
List<ScriptType> scriptTypes = PaymentCode.SEGWIT_SCRIPT_TYPES;
|
||||
Wallet wallet = selectWallet(List.of(PolicyType.SINGLE), scriptTypes, false, true, "login to " + auth47.getCallback().getHost(), true);
|
||||
|
||||
if(wallet != null) {
|
||||
try {
|
||||
auth47.sendResponse(wallet);
|
||||
showSuccessDialog("Successful authentication", "Successfully authenticated to " + auth47.getCallback() + ".");
|
||||
EventManager.get().post(new StatusEvent("Successfully authenticated to " + auth47.getCallback().getHost()));
|
||||
} catch(Exception e) {
|
||||
log.error("Error authenticating auth47 URI", e);
|
||||
showErrorDialog("Error authenticating", "Failed to authenticate.\n\n" + e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
log.error("Not a valid auth47 URI", e);
|
||||
showErrorDialog("Not a valid auth47 URI", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static Wallet selectWallet(ScriptType scriptType, Boolean hasPaymentCode, String actionDescription) {
|
||||
private static void openLnurlAuthUri(URI uri) {
|
||||
try {
|
||||
LnurlAuth lnurlAuth = new LnurlAuth(uri);
|
||||
List<ScriptType> scriptTypes = ScriptType.getAddressableScriptTypes(PolicyType.SINGLE);
|
||||
Wallet wallet = selectWallet(List.of(PolicyType.SINGLE), scriptTypes, true, true, lnurlAuth.getLoginMessage(), true);
|
||||
|
||||
if(wallet != null) {
|
||||
if(wallet.isEncrypted()) {
|
||||
Storage storage = AppServices.get().getOpenWallets().get(wallet);
|
||||
Wallet copy = wallet.copy();
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(copy.getMasterName(), WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<SecureString> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
Storage.KeyDerivationService keyDerivationService = new Storage.KeyDerivationService(storage, password.get(), true);
|
||||
keyDerivationService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.END, "Done"));
|
||||
ECKey encryptionFullKey = keyDerivationService.getValue();
|
||||
Key key = new Key(encryptionFullKey.getPrivKeyBytes(), storage.getKeyDeriver().getSalt(), EncryptionType.Deriver.ARGON2);
|
||||
copy.decrypt(key);
|
||||
try {
|
||||
lnurlAuth.sendResponse(copy);
|
||||
EventManager.get().post(new StatusEvent("Successfully authenticated to " + lnurlAuth.getDomain()));
|
||||
} catch(Exception e) {
|
||||
showErrorDialog("Error authenticating", "Failed to authenticate.\n\n" + e.getMessage());
|
||||
} finally {
|
||||
key.clear();
|
||||
encryptionFullKey.clear();
|
||||
password.get().clear();
|
||||
}
|
||||
});
|
||||
keyDerivationService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.END, "Failed"));
|
||||
if(keyDerivationService.getException() instanceof InvalidPasswordException) {
|
||||
Optional<ButtonType> optResponse = showErrorDialog("Invalid Password", "The wallet password was invalid. Try again?", ButtonType.CANCEL, ButtonType.OK);
|
||||
if(optResponse.isPresent() && optResponse.get().equals(ButtonType.OK)) {
|
||||
Platform.runLater(() -> openLnurlAuthUri(uri));
|
||||
}
|
||||
} else {
|
||||
log.error("Error deriving wallet key", keyDerivationService.getException());
|
||||
}
|
||||
});
|
||||
EventManager.get().post(new StorageEvent(storage.getWalletId(wallet), TimedEvent.Action.START, "Decrypting wallet..."));
|
||||
keyDerivationService.start();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
lnurlAuth.sendResponse(wallet);
|
||||
EventManager.get().post(new StatusEvent("Successfully authenticated to " + lnurlAuth.getDomain()));
|
||||
} catch(LnurlAuth.LnurlAuthException e) {
|
||||
showErrorDialog("Error authenticating", "Failed to authenticate.\n\n" + e.getMessage());
|
||||
} catch(Exception e) {
|
||||
log.error("Failed to authenticate using LNURL-auth", e);
|
||||
showErrorDialog("Error authenticating", "Failed to authenticate.\n\n" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
log.error("Not a valid LNURL-auth URI", e);
|
||||
showErrorDialog("Not a valid LNURL-auth URI", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static Wallet selectWallet(List<PolicyType> policyTypes, List<ScriptType> scriptTypes, boolean taprootAllowed, boolean privateKeysRequired, String actionDescription, boolean alwaysAsk) {
|
||||
Wallet wallet = null;
|
||||
List<Wallet> wallets = get().getOpenWallets().keySet().stream().filter(w -> (scriptType == null || w.getScriptType() == scriptType) && (hasPaymentCode == null || w.hasPaymentCode())).collect(Collectors.toList());
|
||||
List<Wallet> wallets = get().getOpenWallets().keySet().stream().filter(w -> w.isValid() && policyTypes.contains(w.getPolicyType()) && scriptTypes.contains(w.getScriptType())
|
||||
&& (!privateKeysRequired || w.getKeystores().stream().allMatch(Keystore::hasPrivateKey))).collect(Collectors.toList());
|
||||
if(wallets.isEmpty()) {
|
||||
showErrorDialog("No wallet available", "Open a" + (hasPaymentCode == null ? "" : " software") + (scriptType == null ? "" : " " + scriptType.getDescription()) + " wallet to " + actionDescription + ".");
|
||||
} else if(wallets.size() == 1) {
|
||||
boolean taprootOpen = get().getOpenWallets().keySet().stream().anyMatch(w -> w.getScriptType() == ScriptType.P2TR);
|
||||
showErrorDialog("No wallet available", "Open a" + (taprootOpen && !taprootAllowed ? " non-Taproot" : "") + (privateKeysRequired ? " software" : "") + " wallet to " + actionDescription + ".");
|
||||
} else if(wallets.size() == 1 && !alwaysAsk) {
|
||||
wallet = wallets.iterator().next();
|
||||
} else {
|
||||
ChoiceDialog<Wallet> walletChoiceDialog = new ChoiceDialog<>(wallets.iterator().next(), wallets);
|
||||
|
|
|
@ -8,10 +8,16 @@ import java.util.List;
|
|||
|
||||
public class SendActionEvent extends FunctionActionEvent {
|
||||
private final List<BlockTransactionHashIndex> utxos;
|
||||
private final boolean selectIfEmpty;
|
||||
|
||||
public SendActionEvent(Wallet wallet, List<BlockTransactionHashIndex> utxos) {
|
||||
this(wallet, utxos, false);
|
||||
}
|
||||
|
||||
public SendActionEvent(Wallet wallet, List<BlockTransactionHashIndex> utxos, boolean selectIfEmpty) {
|
||||
super(Function.SEND, wallet);
|
||||
this.utxos = utxos;
|
||||
this.selectIfEmpty = selectIfEmpty;
|
||||
}
|
||||
|
||||
public List<BlockTransactionHashIndex> getUtxos() {
|
||||
|
@ -20,6 +26,6 @@ public class SendActionEvent extends FunctionActionEvent {
|
|||
|
||||
@Override
|
||||
public boolean selectFunction() {
|
||||
return !getUtxos().isEmpty();
|
||||
return selectIfEmpty || !getUtxos().isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
204
src/main/java/com/sparrowwallet/sparrow/net/LnurlAuth.java
Normal file
204
src/main/java/com/sparrowwallet/sparrow/net/LnurlAuth.java
Normal file
|
@ -0,0 +1,204 @@
|
|||
package com.sparrowwallet.sparrow.net;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||
import com.sparrowwallet.drongo.crypto.ECDSASignature;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.protocol.Bech32;
|
||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class LnurlAuth {
|
||||
private static final Logger log = LoggerFactory.getLogger(LnurlAuth.class);
|
||||
|
||||
public static final ChildNumber LNURL_PURPOSE = new ChildNumber(138, true);
|
||||
|
||||
private final URL url;
|
||||
private final byte[] k1;
|
||||
private final String action;
|
||||
|
||||
public LnurlAuth(URI uri) throws MalformedURLException {
|
||||
String lnurl = uri.getSchemeSpecificPart();
|
||||
Bech32.Bech32Data bech32 = Bech32.decode(lnurl, 2000);
|
||||
byte[] urlBytes = Bech32.convertBits(bech32.data, 0, bech32.data.length, 5, 8, false);
|
||||
String strUrl = new String(urlBytes, StandardCharsets.UTF_8);
|
||||
this.url = new URL(strUrl);
|
||||
|
||||
Map<String, String> parameterMap = new LinkedHashMap<>();
|
||||
String query = url.getQuery();
|
||||
if(query == null) {
|
||||
throw new IllegalArgumentException("No k1 parameter provided.");
|
||||
}
|
||||
|
||||
if(query.startsWith("?")) {
|
||||
query = query.substring(1);
|
||||
}
|
||||
|
||||
String[] pairs = query.split("&");
|
||||
for(String pair : pairs) {
|
||||
int idx = pair.indexOf("=");
|
||||
if(idx < 0) {
|
||||
continue;
|
||||
}
|
||||
parameterMap.put(pair.substring(0, idx), pair.substring(idx + 1));
|
||||
}
|
||||
|
||||
if(parameterMap.get("tag") == null || !parameterMap.get("tag").toLowerCase(Locale.ROOT).equals("login")) {
|
||||
throw new IllegalArgumentException("Parameter tag was not set to login.");
|
||||
}
|
||||
|
||||
if(parameterMap.get("k1") == null || parameterMap.get("k1").length() != 64) {
|
||||
throw new IllegalArgumentException("Parameter k1 was absent or of incorrect length.");
|
||||
}
|
||||
|
||||
try {
|
||||
this.k1 = Utils.hexToBytes(parameterMap.get("k1"));
|
||||
} catch(Exception e) {
|
||||
throw new IllegalArgumentException("Parameter k1 was not a valid hexadecimal value.");
|
||||
}
|
||||
|
||||
this.action = parameterMap.get("action") == null ? "login" : parameterMap.get("action").toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return url.getHost();
|
||||
}
|
||||
|
||||
public String getLoginMessage() {
|
||||
String domain = getDomain();
|
||||
switch(action) {
|
||||
case "register":
|
||||
return "register an account on " + domain;
|
||||
case "link":
|
||||
return "link your existing account on " + domain;
|
||||
case "auth":
|
||||
return "authorise " + domain;
|
||||
case "login":
|
||||
default:
|
||||
return "login to " + domain;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendResponse(Wallet wallet) throws LnurlAuthException, IOException {
|
||||
URL callback = getReturnURL(wallet);
|
||||
|
||||
Proxy proxy = AppServices.getProxy();
|
||||
if(proxy == null && callback.getHost().toLowerCase(Locale.ROOT).endsWith(TorService.TOR_ADDRESS_SUFFIX)) {
|
||||
throw new LnurlAuthException("A Tor proxy must be configured to authenticate this resource.");
|
||||
}
|
||||
|
||||
HttpURLConnection connection = proxy == null ? (HttpURLConnection) callback.openConnection() : (HttpURLConnection) callback.openConnection(proxy);
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
|
||||
StringBuilder res = new StringBuilder();
|
||||
try(BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String responseLine;
|
||||
while((responseLine = br.readLine()) != null) {
|
||||
res.append(responseLine.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug("Received from " + callback + ": " + res);
|
||||
}
|
||||
|
||||
JsonObject result = JsonParser.parseString(res.toString()).getAsJsonObject();
|
||||
String status = result.get("status").getAsString();
|
||||
if("OK".equals(status)) {
|
||||
return;
|
||||
} else if("ERROR".equals(status)) {
|
||||
String reason = result.get("reason").getAsString();
|
||||
throw new LnurlAuthException("Service returned error: " + reason);
|
||||
} else {
|
||||
throw new LnurlAuthException("Service returned unknown response: " + res);
|
||||
}
|
||||
}
|
||||
|
||||
private URL getReturnURL(Wallet wallet) {
|
||||
try {
|
||||
ECKey linkingKey = deriveLinkingKey(wallet);
|
||||
byte[] signature = getSignature(linkingKey);
|
||||
return new URL(url.toString() + "&sig=" + Utils.bytesToHex(signature) + "&key=" + Utils.bytesToHex(linkingKey.getPubKey()));
|
||||
} catch(MalformedURLException e) {
|
||||
throw new IllegalStateException("Malformed return URL", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ECKey deriveLinkingKey(Wallet wallet) {
|
||||
if(wallet.getPolicyType() != PolicyType.SINGLE) {
|
||||
throw new IllegalArgumentException("Only singlesig wallets can authenticate.");
|
||||
}
|
||||
|
||||
if(wallet.isEncrypted()) {
|
||||
throw new IllegalArgumentException("Wallet must be decrypted.");
|
||||
}
|
||||
|
||||
try {
|
||||
ExtendedKey masterPrivateKey = wallet.getKeystores().get(0).getExtendedMasterPrivateKey();
|
||||
ECKey hashingKey = masterPrivateKey.getKey(List.of(LNURL_PURPOSE, ChildNumber.ZERO));
|
||||
byte[] hash = getHmacSha256Hash(hashingKey.getPrivKeyBytes(), getDomain());
|
||||
List<ChildNumber> pathIndexes = IntStream.range(0, 4).mapToLong(i -> ByteBuffer.wrap(hash, i * 4, 4).getInt() & 0xFFFFFFFFL)
|
||||
.mapToObj(i -> new ChildNumber((int)i)).collect(Collectors.toList());
|
||||
|
||||
List<ChildNumber> derivationPath = new ArrayList<>();
|
||||
derivationPath.add(LNURL_PURPOSE);
|
||||
derivationPath.addAll(pathIndexes);
|
||||
return masterPrivateKey.getKey(derivationPath);
|
||||
} catch(Exception e) {
|
||||
throw new IllegalStateException("Could not determine linking key", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getSignature(ECKey linkingKey) {
|
||||
ECDSASignature ecdsaSignature = linkingKey.signEcdsa(Sha256Hash.wrap(k1));
|
||||
return ecdsaSignature.encodeToDER();
|
||||
}
|
||||
|
||||
private static byte[] getHmacSha256Hash(byte[] key, String data) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secret_key = new SecretKeySpec(key, "HmacSHA256");
|
||||
sha256_HMAC.init(secret_key);
|
||||
|
||||
return sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public static final class LnurlAuthException extends Exception {
|
||||
public LnurlAuthException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LnurlAuthException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LnurlAuthException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public LnurlAuthException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,9 @@ import org.slf4j.LoggerFactory;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.sparrowwallet.drongo.bip47.PaymentCode.SEGWIT_SCRIPT_TYPES;
|
||||
import static com.sparrowwallet.drongo.bip47.PaymentCode.V1_SCRIPT_TYPES;
|
||||
|
||||
public class PayNym {
|
||||
private static final Logger log = LoggerFactory.getLogger(PayNym.class);
|
||||
|
||||
|
@ -68,11 +71,11 @@ public class PayNym {
|
|||
}
|
||||
|
||||
public static List<ScriptType> getSegwitScriptTypes() {
|
||||
return List.of(ScriptType.P2PKH, ScriptType.P2SH_P2WPKH, ScriptType.P2WPKH);
|
||||
return SEGWIT_SCRIPT_TYPES;
|
||||
}
|
||||
|
||||
public static List<ScriptType> getV1ScriptTypes() {
|
||||
return List.of(ScriptType.P2PKH);
|
||||
return V1_SCRIPT_TYPES;
|
||||
}
|
||||
|
||||
public static PayNym fromString(String strPaymentCode, String nymId, String nymName, boolean segwit, List<PayNym> following, List<PayNym> followers) {
|
||||
|
|
Loading…
Reference in a new issue