Generate a linkingKey followed by LN concept
jjhesk opened this issue · 1 comments
linkingKey derivation
linkingKey derivation for BIP-32 based wallets:
There exists a private hashingKey
which is derived by user LN WALLET using m/138'/0 path.
LN SERVICE full domain name is extracted from login LNURL and then hashed using hmacSha256(hashingKey, full service domain name). Full domain name here means FQDN with last full-stop (aka "point") omitted (Example: for https://x.y.z.com/... it would be x.y.z.com).
First 16 bytes are taken from resulting hash and then turned into a sequence of 4 Long values which are in turn used to derive a service-specific linkingKey using m/138'/<long1>/<long2>/<long3>/<long4>
path, a Scala example:
import fr.acinq.bitcoin.crypto
import fr.acinq.bitcoin.Protocol
import java.io.ByteArrayInputStream
import fr.acinq.bitcoin.DeterministicWallet._
val domainName = "site.com"
val hashingPrivKey = derivePrivateKey(walletMasterKey, hardened(138L) :: 0L :: Nil)
val derivationMaterial = hmac256(key = hashingPrivKey.toBin, message = domainName)
val stream = new ByteArrayInputStream(derivationMaterial.slice(0, 16).toArray)
val pathSuffix = Vector.fill(4)(Protocol.uint32(stream, ByteOrder.BIG_ENDIAN)) // each uint32 call consumes next 4 bytes
val linkingPrivKey = derivePrivateKey(walletMasterKey, hardened(138L) +: pathSuffix)
val linkingPubKey = linkingPrivKey.publicKey
found some cool coding block from js
import bech32 from 'bech32';
import bip21 from 'bip21';
import { fromSeed, fromBase58 } from 'bip32';
import bolt11 from 'bolt11';
import validate from 'bitcoin-address-validation';
import { crypto, payments, Psbt } from 'bitcoinjs-lib';
import {
crypto as lqcrypto,
ECPair,
payments as lqpayments,
Psbt as lqPsbt,
} from 'liquidjs-lib';
import { generateMnemonic, mnemonicToSeedSync } from 'bip39';
import cryptojs from 'crypto-js';
import sha256, { HMAC } from 'fast-sha256';
import secp256k1 from 'secp256k1';
import { validate as isUuid, v4 } from 'uuid';
import { fromSeed as slip77FromSeed } from 'slip77';
....
const linkingKey = (domain, seed) => {
const root = fromSeed(mnemonicToSeedSync(seed));
const hashingKey = root.derivePath("m/138'/0");
const hmac = new HMAC(hashingKey.privateKey);
const derivationMaterial = hmac.update(stringToUint8Array(domain)).digest();
const first4 = derivationMaterial.slice(0, 4);
return root.derivePath(
`m/138'/${first4[0]}/${first4[1]}/${first4[2]}/${first4[3]}`
);
};
...
const key = linkingKey(params.domain, seed);
const signedMessage = secp256k1.ecdsaSign(
hexToUint8Array(params.k1),
key.privateKey
);
const signedMessageDER = secp256k1.signatureExport(
signedMessage.signature
);
const linkingKeyPub = secp256k1.publicKeyCreate(
key.privateKey,
true
);
const sig = bytesToHexString(signedMessageDER);
anyone can make that into python with this library?