ElementsProject / ElementsProject/lightning
Invoice preimage (proof-of-payment) should be `hsmd` responsibility
- Dominant language
- C
- Stars
- 3.1k
- Forks
- 1k
- Avg merge
- 4d 10h
- Merged PRs (30d)
- 13
Description
Request (Core Lightning)
========================
Move generation of `payment_preimage` from `lightningd` to `hsmd`.
Motivation
==========
If we want to take seriously "the payment preimage is proof-of-payment",
then we should consider that the signing authority must be the one that
generates payment preimages.
In this point-of-view, the signing authority is separated from the
node software.
If payment preimages are created and issued by the node software, then a
compromised node software can steal money from the signing authority, by
asking the signing authority to sign an invoice, then releasing the
payment preimage without waiting for an incoming HTLC.
In that case, the signing authority, having signed the invoice, can be
presented with a proof-of-payment (in the form of payment preimage, and
the invoice signed by said signing authority), and any real-world
consequences it might have.
For example, automated systems might provide (supposedly-paid) service
if they are given a signed invoice, with the expected description of
the service, with signature from the known signing authority, and the
proof-of-payment (the payment preimage) matching that invoice.
For another example, a forwarding node that is able to corrupt a
payee node or major receiver can acquire the preimages for invoices,
even if the payee is currently using VLS, and then be able to claim
inbound payments destined to the payee without forwarding money to
the payee.
(In a certain point-of-view, forwarding nodes are automated software
handling payment preimages that have real-world consequences.)
In Core Lightning, the "signing authority" is the `hsmd`, while the
"node software" is `lightnignd` and all the other daemons it launches.
For Validating Lightning Signer integration, `hsmd` is replaced with a
proxy for VLS program (which is the signing authority) to communicate
with.
Properly speaking, with this separation, the payment preimage should
be issued by the VLS program.
Mechanics
=========
Interface
---------
We add a `hsmd_generate_invoice_commitments` and corresponding response
to `hsmd/hsmd.csv`.
msgtype,hsmd_generate_invoice_commitments,TBD
msgdata,hsmd_generate_invoice_commitments,amount,amount_msat
msgdata,hsmd_generate_invoice_commitments,expiry,u64
msgdata,hsmd_generate_invoice_commitments,min_final_cltv_expiry_delta,u16
msgtype,hsmd_generate_invoice_commitments_reply,TBD
msgdata,hsmd_generate_invoice_commitments_reply,payment_hash,sha256,
msgdata,hsmd_generate_invoice_commitments_reply,payment_secret,secret,
The `payment_hash` and `payment_secret` returned from the above reply are
then the details for the issued invoice.
The invoice `hsmd_sign_invoice` already provides the information needed,
in the form of the actual invoice.
However, Core Lightning supports invoices where the payment preimage is
provided at invoice creation instead of generated by Core Lightning.
For the Core Lightning `hsmd`, the payment preimage provided by
the user is ignored by the `hsmd`, who trusts Core Lightning
completely.
For Validating Lightning Signer, we would have to add a new RPC
for the VLS daemon to tell it that we intend to give the preimage
to the Core Lightning node.
In that case, the user can simply give the payment preimage or
its hash to that RPC, and VLS can recognize it by parsing the
invoice-to-sign and checking for the payment hash from the new
RPC.
Thus, the existing `hsmd_sign_invoice` should be sufficient
without changes.
Finally, since `hsmd` is now the one who knows the preimage, we add
the following request-response.
This request-response must be invocable on a per-channel connection,
not the main global `hsmd` connection (how does it work for multipath
receives though?):
msgtype,hsmd_get_invoice_preimage,TBD
msgdata,hsmd_get_invoice_preimage,amount,amount_msat
msgdata,hsmd_get_invoice_preimage,expiry,u64
msgdata,hsmd_get_invoice_preimage,min_final_cltv_expiry_delta,u16
msgdata,hsmd_get_invoice_preimage,payment_hash,sha256,
msgdata,hsmd_get_invoice_preimage,payment_secret,secret
msgtype,hsmd_get_invoice_preimage_reply,TBD
msgdata,hsmd_get_invoice_preimage_reply,ok,bool
msgdata,hsmd_get_invoice_preimage_reply,payment_preimage,secret
In the reply the `ok` indicates that the `payment_hash` matches the
`payment_secret`.
If `ok` is true, then `payment_preimage` is valid, otherwise the
`payment_preimage` has no useful data.
`ok` may be false in case of a probe attempt that matches the
`payment_hash` of an existing invoice, such as the
second-to-the-last-hop probing that `payment_secret` was intended
to prevent.
Implementation (Core Lightning)
-------------------------------
In the `invoice` RPC, we branch depending on whether `preimage`
argument is provided or not.
If provided, then `invoice` delegates into the `createinvoice` RPC.
This RPC will then use `hsmd_sign_invoice` with `user_gave_preimage`
as `true` and `user_given_preimage` set to the `preimage` value.
Otherwise, it has to perform the `hsmd_generate_invoice_commitments`
request.
For back-compatibility, we add the following database migration:
{SQL("ALTER TABLE invoices ADD hsmd_issued_hash INT;"), NULL},
{SQL("UPDATE invoices SET hsmd_issued_hash=0;"), NULL},
The `hsmd_issued_hash` flag is then cleared by `createinvoice`, since
it knows the preimage to be used (it came from the user).
The flag `hsmd_issued_hash` is set if `invoice` was invoked without
`preimage` argument, in which case the `hsmd` is the one that can
re-generate the preimage from the information given.
For historical invoices before this upgrade, they will have
`hsmd_issued_hash` as `0`, meaning that the preimage is recorded
in the database (which is what would happen before this change).
The `payment_secret` and `payment_preimage` can be generated this
way:
* Concatenate the following into a 128-bit (16 byte) blob:
* 64 bits of `amount`
* 16 bits of `min_final_cltv_expiry_delta`
* low 48 bits of `expiry`
- we hope we have a different invoice scheme by the
time we get past 48 bits of time.
* Generate 128 bits of randomness.
* Symmetric-encrypt the 128-bit blob using the above
128 bits of randomness as initialization vector, and
the node `secret_key` as encryption key.
* Concatenate the encrypted 128-bit blob with the 128-bit
randomness/IV.
* The resulting 256-bit blob is now the `payment_secret`.
* To generate the payment preimge, HMAC the `payment_secret`
using the `hsmd` secret key.
- Encrypt-then-MAC.
* The resulting `HMAC(secret_key, payment_secret)` is the
`payment_preimage`, and we can get `payment_hash` by SHA256.
- Validating the `payment_hash` is that it is the hash of
the `HMAC` of the `payment_secret`, which is the encrypted
message-to-self, thus also doubles as a validation of the
MAC; the "MAC" sent is the hash of the HMAC (a.k.a. the
`payment_hash`).
- `payment_secret`: encrypted blob + IV
- The 128 bits randomness of the IV should be enough
for 64 bits of uniqueness, we do not expect the
node to have 2^64 invoices.
- `payment_hash`: hash of the HMAC of the encrypted blob
+ IV (a.k.a. just a novel MAC scheme)
- WARNING: The `hsmd_wire.csv` interface includes
`hsmd_derive_secret`, we should use a DIFFERENT exact
hashing for this, such as adding some fixed prefix
when deriving `payment_preimage` or for `derive_secret`,
to make the two incompatible (i.e. "tagged hash"
technique to ensure separation of domains).
We do not want the node to be able to use
`hsmd_derive_secret` with the `payment_secret` to get
the `payment_preimage`.
VLS also adds "derive secret" to the HKDF for its
own implementation of `hsmd_derive_secret`.
When an incoming HTLC is to be terminated at this node, extract
the incoming onion data amount, expiry, and
`min_final_cltv_expiry_delta`, and save into database (or not,
aren't incoming onions already saved?).
Then provide the information when requesting the preimage.
Note that the preimage request must done on the per-channel
`hsmd` connection for the channel where the HTLC came in.
This allows validating signers to easily look up which
channel to validate.
Note that in principle, we do not need to encrypt the
metadata embedded in the payment secret, or have the payment
secret be generated by the signer; however, it allows for
independent validation at the signer without requiring
invoices to also be stored at the signer.
Implementation (Validating Lightning Signer)
--------------------------------------------
For VLS, the above implementation cryptography can be done as
well.
In addition, VLS must validate that the given `payment_secret`
can be decrypted appropriately.
At invoice-signing, VLS validates that the invoice to be
signed has the correct data.
As mentioned, VLS should have an RPC command that tells it
"I will make an invoice with a user-provided `payment_preimage`
soon", which must be invoked before the `lightningd` side
`createinvoice` is invoked.
Then VLS can remember the payment hash for this use-case,
and also secondarily search this set of hashes if it cannot
recognize the payment secret and payment hash during
invoice signing.
At preimage-release, VLS validates that the given
`payment_secret` matches the invoice data, and validates
that the corresponding channel has an incoming HTLC that
is irrevocably committed (need to consider multipath
though?).
If so, only then will it actually release the preimage.
Scope
=====
This does not include `keysend` sends.
For `keysend` sends, the ***sender*** signing authority
needs to encrypt the preimage to the intended
destination.
Otherwise, the ***sender*** node could ask the signer
to sign out an outgoing HTLC for a supposed `keysend`,
but use a *different* payment onion in the actual
`update_add_htlc`, going to a different destination
than what is intended.
In particular, the same point is used for ECDH on
each onion layer (except when changing points
explicitly using `next_path_key_override` if I
understand that part properly; I need more time to
study this).
Knowledge of the secret behind this point is needed
by whoever generates the path, which would be out
of scope for `hsmd`.
Thus, for `keysend` sends, I will make a different
request, as I need to think more about how to make
this work.
(A change is not needed for *receiving* `keysend`s;
the expectation is that the inbound HTLC would be
irrevocably committed first before the onion layer
is unwrapped by the `hsmd`, at which point the
Validating Lightning Signer can drop onchain if the
node does not claim the payment before HTLC timeout;
this is a reasonable expectation as both forwarding
and terminating the inbound should only be resolved
after the inbound HTLC has irrevocably committed.)
In particular, for `keysend`, one possible use for
it is to transfer funds between nodes that a single
owner controls without the overhead of additional
API calls to invoice handling.
In that case, it is very important that the intended
recepient be the only one that can read the preimage.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.