Skip to content

Instantly share code, notes, and snippets.

@louneskmt
Last active November 29, 2022 16:15
Show Gist options
  • Save louneskmt/ea90062a99ef1ae517d2c0084298e03f to your computer and use it in GitHub Desktop.
Save louneskmt/ea90062a99ef1ae517d2c0084298e03f to your computer and use it in GitHub Desktop.
BIP-32 based seed generation for auth protocol, hardware wallet compatible version

BIP-32 based seed generation for auth protocol, hardware wallet compatible version

Context

There exists currently two ways of generating the seed for the LUD-04 auth protocol (LNURL-auth, key-auth):

  • LUD-05: BIP32-based seed generation, for regular BIP32 hot wallets
  • LUD-13: signmessage-based seed generation, mainly dedicated for Lightning nodes that offer a signmessage API. This is the most used seed generation.

Both are not suitable for hardware wallets (HW) for several reasons:

  • HW don't expose the device private keys, necessary for LUD-05
  • HW don't provide a default derivation path for a signmessage API (in LUD-13, the Lightning node private key is used here)
  • there is no point of using a hardware wallet if the private keys necessary to authenticate to a service don't remain on the device only

We need a new standard compatible with hardware wallets that:

  • doesn't require the extraction of private keys from the device
  • doesn't require any changes to the device's firmware
  • is compatible with the existing auth protocol

New seed generation LUD proposal

A suitable derivation scheme would be very similar to LUD-05. We would only need to use the public key derived from m/138'/0 as hashingKey instead of its private key (as it is inaccessible from outside the device).

  1. There exists a public hashingKey which is derived by the user's HARDWARE WALLET using m/138'/0 path.
  2. 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 comma omitted (Example: for https://x.y.z.com/... it would be x.y.z.com).
  3. 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.

Usage

We can imagine two distinct ways of logging in to a web service:

  1. directly on the web using Web USD/HID (using libraries like @ledgerhq/hw-transport-webhid)
  2. using a software wallet (like Sparrow) connected to the device, that takes the LNURL as input

The web page or software gets the hashingPubKey from the device, using m/138'/0 derivation path and computes the pathSuffix from the service domain name (parsed from the LNURL). It then asks the device to sign the k1 challenge with the computed derivation path's private key, and uses the resulting signature to login the user following the regular auth protocol.

Identified problems

Leak of the m/138' path extended public key

Anyone possessing this extended public key can derive the public keys for every service. This isn't considered a big issue as:

  • a user's public key is generally internal to a service, so there is no way to correlate them with real accounts
  • it is very unlikely that a user would manually export the extended private key and expose it (but god only knows what users are capable of)
  • at least it doesn't compromise the account

Malicious web page

The user believes they are logging in into maliciouswebpage.com, when in fact the web page makes them sign some other website's challenge with the derivation path corresponding to this other website, thus allowing the malicious web page to get into the user's account on the other website. This is possible as the user doesn't know with which public key their hardware wallet is signing the challenge.

This is not an issue in the current protocol nor in this gist software-version (not web), as the linkingKey derivation is handled by the wallet, so we are sure that the linkingKey is derived from the correct domain name parsed from the LNURL (of course making the assumption that we use open-source wallets and that we don't trust, we verify).

A simple way to mitigate this would be for the challenge to be the concatenation of the service's domain name parsed from the LNURL and k1, instead of k1 only: challenge = domain name || k1. That way, we make sure that the challenge is associated with the service's backend and not any other domain. It would also allow us to not have to verify each wallet's code for LNURL auth (to make sure it derives the correct public key) :)

For the web part, it doesn't fully solve the problem, as the device doesn't show the message being signed (only a hash for Ledger). But with the domain name included in the challenge, the web page can offer the possibility to display the challenge and its hash, and let the user verify by themself that the correct domain name is being signed by computing the challenge hash and checking with the hash on the device.

@fanismichalakis
Copy link

Both are not suitable for hardware wallets for several reasons:

  • they don't expose the device private keys, necessary for LUD-05
  • they don't provide a default derivation path for a signmessage API (in LUD-13, the Lightning node private key is used here)

At first read, one may understand that "they" refers to "both". I'd replace they with "hardware wallets", or "HW" after specifying this abbreviation between parenthesis after first occurence of "hardware wallet".

We would only need to use the public key derivated from...

derivated -> derived

with which public key is their hardware wallet signing the challenge

with which public key their hardware wallet is signing the challenge

For the malicious web page example, maybe write down explicitly what this attack allows: "The user believes they are login into maliciouswebpage.com, when in fact the web page make them sign some other website's challenge with the derivation path corresponding to this other website, thus allowing the malicious web page to get into the user's account on the other website".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment