Skip to content

Commit

Permalink
Rename blinding to pathKey (#735)
Browse files Browse the repository at this point in the history
Rename some variable to align with the spec.
  • Loading branch information
thomash-acinq authored Nov 29, 2024
1 parent 97b1525 commit 7cfdb96
Show file tree
Hide file tree
Showing 21 changed files with 213 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,13 @@ data class NodeParams(
* @return the default offer and the private key that will sign invoices for this offer.
*/
fun defaultOffer(trampolineNodeId: PublicKey): Pair<OfferTypes.Offer, PrivateKey> {
// We generate a deterministic blindingSecret based on:
// We generate a deterministic session key based on:
// - a custom tag indicating that this is used in the Bolt 12 context
// - our trampoline node, which is used as an introduction node for the offer's blinded path
// - our private key, which ensures that nobody else can generate the same blindingSecret
val blindingSecret = PrivateKey(Crypto.sha256("bolt 12 default offer".toByteArray(Charsets.UTF_8).byteVector() + trampolineNodeId.value + nodePrivateKey.value).byteVector32())
// - our private key, which ensures that nobody else can generate the same path key secret
val sessionKey = PrivateKey(Crypto.sha256("bolt 12 default offer".toByteArray(Charsets.UTF_8).byteVector() + trampolineNodeId.value + nodePrivateKey.value).byteVector32())
// We don't use our currently activated features, otherwise the offer would change when we add support for new features.
// If we add a new feature that we would like to use by default, we will need to explicitly create a new offer.
return OfferTypes.Offer.createBlindedOffer(amount = null, description = null, this, trampolineNodeId, Features.empty, blindingSecret)
return OfferTypes.Offer.createBlindedOffer(amount = null, description = null, this, trampolineNodeId, Features.empty, sessionKey)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,68 +14,68 @@ import fr.acinq.lightning.wire.OnionPaymentPayloadTlv
object RouteBlinding {

/**
* @param nodeId introduction node's id (which cannot be blinded since the sender need to find a route to it).
* @param nodeId first node's id (which cannot be blinded since the sender need to find a route to it).
* @param blindedPublicKey blinded public key, which hides the real public key.
* @param blindingEphemeralKey blinding tweak that can be used by the receiving node to derive the private key that matches the blinded public key.
* @param encryptedPayload encrypted payload that can be decrypted with the introduction node's private key and the blinding ephemeral key.
* @param pathKey blinding tweak that can be used by the receiving node to derive the private key that matches the blinded public key.
* @param encryptedPayload encrypted payload that can be decrypted with the introduction node's private key and the path key.
*/
data class IntroductionNode(
data class FirstHop(
val nodeId: EncodedNodeId,
val blindedPublicKey: PublicKey,
val blindingEphemeralKey: PublicKey,
val pathKey: PublicKey,
val encryptedPayload: ByteVector
)

/**
* @param blindedPublicKey blinded public key, which hides the real public key.
* @param encryptedPayload encrypted payload that can be decrypted with the receiving node's private key and the blinding ephemeral key.
* @param encryptedPayload encrypted payload that can be decrypted with the receiving node's private key and the path key.
*/
data class BlindedNode(val blindedPublicKey: PublicKey, val encryptedPayload: ByteVector)
data class BlindedHop(val blindedPublicKey: PublicKey, val encryptedPayload: ByteVector)

/**
* @param introductionNodeId the first node, not blinded so that the sender can locate it.
* @param blindingKey blinding tweak that can be used by the introduction node to derive the private key that matches the blinded public key.
* @param blindedNodes blinded nodes (including the introduction node).
* @param firstNodeId the first node, not blinded so that the sender can locate it.
* @param firstPathKey blinding tweak that can be used by the introduction node to derive the private key that matches the blinded public key.
* @param blindedHops blinded nodes (including the introduction node).
*/
data class BlindedRoute(
val introductionNodeId: EncodedNodeId,
val blindingKey: PublicKey,
val blindedNodes: List<BlindedNode>
val firstNodeId: EncodedNodeId,
val firstPathKey: PublicKey,
val blindedHops: List<BlindedHop>
) {
val introductionNode: IntroductionNode = IntroductionNode(
introductionNodeId,
blindedNodes.first().blindedPublicKey,
blindingKey,
blindedNodes.first().encryptedPayload
val firstHop: FirstHop = FirstHop(
firstNodeId,
blindedHops.first().blindedPublicKey,
firstPathKey,
blindedHops.first().encryptedPayload
)
val subsequentNodes: List<BlindedNode> = blindedNodes.drop(1)
val blindedNodeIds: List<PublicKey> = blindedNodes.map { it.blindedPublicKey }
val encryptedPayloads: List<ByteVector> = blindedNodes.map { it.encryptedPayload }
val subsequentHops: List<BlindedHop> = blindedHops.drop(1)
val blindedNodeIds: List<PublicKey> = blindedHops.map { it.blindedPublicKey }
val encryptedPayloads: List<ByteVector> = blindedHops.map { it.encryptedPayload }
}

/**
* @param route blinded route.
* @param lastBlinding blinding point for the last node, which can be used to derive the blinded private key.
* @param lastPathKey path key for the last node, which can be used to derive the blinded private key.
*/
data class BlindedRouteDetails(val route: BlindedRoute, val lastBlinding: PublicKey) {
data class BlindedRouteDetails(val route: BlindedRoute, val lastPathKey: PublicKey) {
/** @param nodeKey private key associated with our non-blinded node_id. */
fun blindedPrivateKey(nodeKey: PrivateKey): PrivateKey = derivePrivateKey(nodeKey, lastBlinding)
fun blindedPrivateKey(nodeKey: PrivateKey): PrivateKey = derivePrivateKey(nodeKey, lastPathKey)
}

/**
* Blind the provided route and encrypt intermediate nodes' payloads.
*
* @param sessionKey this node's session key.
* @param sessionKey session key of the blinded path, the corresponding public key will be the first path key.
* @param publicKeys public keys of each node on the route, starting from the introduction point.
* @param payloads payloads that should be encrypted for each node on the route.
* @return a blinded route.
*/
fun create(sessionKey: PrivateKey, publicKeys: List<PublicKey>, payloads: List<ByteVector>): BlindedRouteDetails {
require(publicKeys.size == payloads.size) { "a payload must be provided for each node in the blinded path" }
var e = sessionKey
val (blindedHops, blindingKeys) = publicKeys.zip(payloads).map { pair ->
val (blindedHops, pathKeys) = publicKeys.zip(payloads).map { pair ->
val (publicKey, payload) = pair
val blindingKey = e.publicKey()
val pathKey = e.publicKey()
val sharedSecret = Sphinx.computeSharedSecret(publicKey, e)
val blindedPublicKey = Sphinx.blind(publicKey, Sphinx.generateKey("blinded_node_id", sharedSecret))
val rho = Sphinx.generateKey("rho", sharedSecret)
Expand All @@ -85,38 +85,38 @@ object RouteBlinding {
payload.toByteArray(),
byteArrayOf()
)
e *= PrivateKey(Crypto.sha256(blindingKey.value.toByteArray() + sharedSecret.toByteArray()))
Pair(BlindedNode(blindedPublicKey, ByteVector(encryptedPayload + mac)), blindingKey)
e *= PrivateKey(Crypto.sha256(pathKey.value.toByteArray() + sharedSecret.toByteArray()))
Pair(BlindedHop(blindedPublicKey, ByteVector(encryptedPayload + mac)), pathKey)
}.unzip()
return BlindedRouteDetails(BlindedRoute(EncodedNodeId(publicKeys.first()), blindingKeys.first(), blindedHops), blindingKeys.last())
return BlindedRouteDetails(BlindedRoute(EncodedNodeId(publicKeys.first()), pathKeys.first(), blindedHops), pathKeys.last())
}

/**
* Compute the blinded private key that must be used to decrypt an incoming blinded onion.
*
* @param privateKey this node's private key.
* @param blindingEphemeralKey unblinding ephemeral key.
* @param pathKey unblinding ephemeral key.
* @return this node's blinded private key.
*/
fun derivePrivateKey(privateKey: PrivateKey, blindingEphemeralKey: PublicKey): PrivateKey {
val sharedSecret = Sphinx.computeSharedSecret(blindingEphemeralKey, privateKey)
fun derivePrivateKey(privateKey: PrivateKey, pathKey: PublicKey): PrivateKey {
val sharedSecret = Sphinx.computeSharedSecret(pathKey, privateKey)
return privateKey * PrivateKey(Sphinx.generateKey("blinded_node_id", sharedSecret))
}

/**
* Decrypt the encrypted payload (usually found in the onion) that contains instructions to locate the next node.
*
* @param privateKey this node's private key.
* @param blindingEphemeralKey unblinding ephemeral key.
* @param pathKey unblinding ephemeral key.
* @param encryptedPayload encrypted payload for this node.
* @return a tuple (decrypted payload, unblinding ephemeral key for the next node)
*/
fun decryptPayload(
privateKey: PrivateKey,
blindingEphemeralKey: PublicKey,
pathKey: PublicKey,
encryptedPayload: ByteVector
): Either<InvalidTlvPayload, Pair<ByteVector, PublicKey>> {
val sharedSecret = Sphinx.computeSharedSecret(blindingEphemeralKey, privateKey)
val sharedSecret = Sphinx.computeSharedSecret(pathKey, privateKey)
val rho = Sphinx.generateKey("rho", sharedSecret)
return try {
val decrypted = ChaCha20Poly1305.decrypt(
Expand All @@ -126,8 +126,8 @@ object RouteBlinding {
byteArrayOf(),
encryptedPayload.takeRight(16).toByteArray()
)
val nextBlindingEphemeralKey = Sphinx.blind(blindingEphemeralKey, Sphinx.computeBlindingFactor(blindingEphemeralKey, sharedSecret))
Either.Right(Pair(ByteVector(decrypted), nextBlindingEphemeralKey))
val nextPathKey = Sphinx.blind(pathKey, Sphinx.computeBlindingFactor(pathKey, sharedSecret))
Either.Right(Pair(ByteVector(decrypted), nextPathKey))
} catch (_: Throwable) {
Either.Left(CannotDecodeTlv(OnionPaymentPayloadTlv.EncryptedRecipientData.tag))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
JsonSerializers.FundingSignedSerializer::class,
JsonSerializers.UpdateAddHtlcSerializer::class,
JsonSerializers.UpdateAddHtlcTlvSerializer::class,
JsonSerializers.UpdateAddHtlcTlvBlindingSerializer::class,
JsonSerializers.UpdateAddHtlcTlvPathKeySerializer::class,
JsonSerializers.UpdateFulfillHtlcSerializer::class,
JsonSerializers.UpdateFailHtlcSerializer::class,
JsonSerializers.UpdateFailMalformedHtlcSerializer::class,
Expand Down Expand Up @@ -98,7 +98,7 @@
JsonSerializers.Bolt11UnknownTagSerializer::class,
JsonSerializers.Bolt11InvalidTagSerializer::class,
JsonSerializers.EncodedNodeIdSerializer::class,
JsonSerializers.BlindedNodeSerializer::class,
JsonSerializers.BlindedHopSerializer::class,
JsonSerializers.BlindedRouteSerializer::class,
)
@file:UseContextualSerialization(
Expand Down Expand Up @@ -209,7 +209,7 @@ object JsonSerializers {
subclass(CommitSigTlv.Batch::class, CommitSigTlvBatchSerializer)
subclass(ShutdownTlv.ChannelData::class, ShutdownTlvChannelDataSerializer)
subclass(ClosingSignedTlv.FeeRange::class, ClosingSignedTlvFeeRangeSerializer)
subclass(UpdateAddHtlcTlv.Blinding::class, UpdateAddHtlcTlvBlindingSerializer)
subclass(UpdateAddHtlcTlv.PathKey::class, UpdateAddHtlcTlvPathKeySerializer)
}
// TODO The following declarations are required because serializers for [TransactionWithInputInfo]
// depend themselves on @Contextual serializers. Once we get rid of v2/v3 serialization and we
Expand Down Expand Up @@ -476,8 +476,8 @@ object JsonSerializers {
@Serializer(forClass = EncryptedChannelData::class)
object EncryptedChannelDataSerializer

@Serializer(forClass = UpdateAddHtlcTlv.Blinding::class)
object UpdateAddHtlcTlvBlindingSerializer
@Serializer(forClass = UpdateAddHtlcTlv.PathKey::class)
object UpdateAddHtlcTlvPathKeySerializer

@Serializer(forClass = UpdateAddHtlcTlv::class)
object UpdateAddHtlcTlvSerializer
Expand Down Expand Up @@ -649,8 +649,8 @@ object JsonSerializers {
delegateSerializer = EncodedNodeIdSurrogate.serializer()
)

@Serializer(forClass = RouteBlinding.BlindedNode::class)
object BlindedNodeSerializer
@Serializer(forClass = RouteBlinding.BlindedHop::class)
object BlindedHopSerializer

@Serializer(forClass = RouteBlinding.BlindedRoute::class)
object BlindedRouteSerializer
Expand Down
Loading

0 comments on commit 7cfdb96

Please sign in to comment.