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 asignmessage
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
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).
- There exists a public
hashingKey
which is derived by the user's HARDWARE WALLET usingm/138'/0
path. LN SERVICE
full domain name is extracted from login LNURL and then hashed usinghmacSha256(hashingKey, full service domain name)
. Full domain name here means FQDN with last comma omitted (Example: forhttps://x.y.z.com/...
it would bex.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-specificlinkingKey
usingm/138'/<long1>/<long2>/<long3>/<long4>
path.
We can imagine two distinct ways of logging in to a web service:
- directly on the web using Web USD/HID (using libraries like
@ledgerhq/hw-transport-webhid
) - 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.
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
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.
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".
derivated -> derived
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".