firewall: obfuscate PendingChannels

Only obfuscate pending open channels for now.
This commit is contained in:
bitromortac 2023-06-01 09:06:06 +02:00
parent ef84753f6b
commit e797abd2a8
No known key found for this signature in database
GPG key ID: 1965063FC13BEBE2
2 changed files with 465 additions and 0 deletions

View file

@ -290,6 +290,12 @@ func (p *PrivacyMapper) checkers(db firewalldb.PrivacyMapDB,
handleClosedChannelsResponse(db, flags, p.randIntn),
mid.PassThroughErrorHandler,
),
"/lnrpc.Lightning/PendingChannels": mid.NewResponseRewriter(
&lnrpc.PendingChannelsRequest{},
&lnrpc.PendingChannelsResponse{},
handlePendingChannelsResponse(db, flags, p.randIntn),
mid.PassThroughErrorHandler,
),
}
}
@ -1006,6 +1012,311 @@ func handleClosedChannelsResponse(db firewalldb.PrivacyMapDB,
}
}
// obfuscatePendingChannel is a helper to obfuscate the fields of a pending
// channel.
func obfuscatePendingChannel(c *lnrpc.PendingChannelsResponse_PendingChannel,
tx firewalldb.PrivacyMapTx, randIntn func(int) (int, error),
flags session.PrivacyFlags) (
*lnrpc.PendingChannelsResponse_PendingChannel, error) {
var err error
remotePub := c.RemoteNodePub
if !flags.Contains(session.ClearPubkeys) {
remotePub, err = firewalldb.HideString(
tx, remotePub,
)
if err != nil {
return nil, err
}
}
capacity, err := maybeHideAmount(
flags, randIntn, c.Capacity,
)
if err != nil {
return nil, err
}
// We randomize local/remote balances.
localBalance, err := maybeHideAmount(
flags, randIntn, c.LocalBalance,
)
if err != nil {
return nil, err
}
// We may have a too large value for the local
// balance, restrict it to the capacity.
if localBalance > capacity {
localBalance = capacity
}
// The remote balance is set constently to the local balance.
remoteBalance := c.RemoteBalance
if !flags.Contains(session.ClearAmounts) {
remoteBalance = capacity - localBalance
}
chanPoint := c.ChannelPoint
if !flags.Contains(session.ClearChanIDs) {
chanPoint, err = firewalldb.HideChanPointStr(
tx, c.ChannelPoint,
)
if err != nil {
return nil, err
}
}
return &lnrpc.PendingChannelsResponse_PendingChannel{
// Obfuscated fields.
ChannelPoint: chanPoint,
RemoteNodePub: remotePub,
Capacity: capacity,
LocalBalance: localBalance,
RemoteBalance: remoteBalance,
// Non-obfuscated fields.
ChanStatusFlags: c.ChanStatusFlags,
Private: c.Private,
CommitmentType: c.CommitmentType,
Initiator: c.Initiator,
NumForwardingPackages: c.NumForwardingPackages,
Memo: c.Memo,
// Omitted fields.
// LocalChanReserveSat
// RemoteChanReserveSat
}, nil
}
func handlePendingChannelsResponse(db firewalldb.PrivacyMapDB,
flags session.PrivacyFlags,
randIntn func(int) (int, error)) func(ctx context.Context,
r *lnrpc.PendingChannelsResponse) (proto.Message, error) {
return func(_ context.Context, r *lnrpc.PendingChannelsResponse) (
proto.Message, error) {
pendingOpens := make(
[]*lnrpc.PendingChannelsResponse_PendingOpenChannel,
len(r.PendingOpenChannels),
)
pendingCloses := make(
[]*lnrpc.PendingChannelsResponse_ClosedChannel,
len(r.PendingClosingChannels),
)
pendingForceCloses := make(
[]*lnrpc.PendingChannelsResponse_ForceClosedChannel,
len(r.PendingForceClosingChannels),
)
waitingCloses := make(
[]*lnrpc.PendingChannelsResponse_WaitingCloseChannel,
len(r.WaitingCloseChannels),
)
err := db.Update(func(tx firewalldb.PrivacyMapTx) error {
for i, c := range r.PendingOpenChannels {
var err error
pendingChannel, err := obfuscatePendingChannel(
c.Channel, tx, randIntn, flags,
)
if err != nil {
return err
}
pendingOpen := lnrpc.PendingChannelsResponse_PendingOpenChannel{
// Non-obfuscated fields.
CommitFee: c.CommitFee,
CommitWeight: c.CommitWeight,
FeePerKw: c.FeePerKw,
FundingExpiryBlocks: c.FundingExpiryBlocks,
// Obfuscated fields.
Channel: pendingChannel,
}
pendingOpens[i] = &pendingOpen
}
for i, c := range r.PendingClosingChannels {
var err error
pendingChannel, err := obfuscatePendingChannel(
c.Channel, tx, randIntn, flags,
)
if err != nil {
return err
}
closingTxid := c.ClosingTxid
if !flags.Contains(session.ClearClosingTxIds) {
closingTxid, err = firewalldb.HideString(
tx, c.ClosingTxid,
)
if err != nil {
return err
}
}
pendingClose := lnrpc.PendingChannelsResponse_ClosedChannel{
// Obfuscated fields.
ClosingTxid: closingTxid,
Channel: pendingChannel,
}
pendingCloses[i] = &pendingClose
}
for i, c := range r.PendingForceClosingChannels {
var err error
pendingChannel, err := obfuscatePendingChannel(
c.Channel, tx, randIntn, flags,
)
if err != nil {
return err
}
closingTxid := c.ClosingTxid
if !flags.Contains(session.ClearClosingTxIds) {
closingTxid, err = firewalldb.HideString(
tx, c.ClosingTxid,
)
if err != nil {
return err
}
}
limboBalance, err := maybeHideAmount(
flags, randIntn, c.LimboBalance,
)
if err != nil {
return err
}
if limboBalance > pendingChannel.Capacity {
limboBalance = pendingChannel.Capacity
}
recoveredBalance, err := maybeHideAmount(
flags, randIntn, c.RecoveredBalance,
)
if err != nil {
return err
}
if recoveredBalance > pendingChannel.Capacity {
limboBalance = pendingChannel.Capacity
}
pendingForceClose := lnrpc.PendingChannelsResponse_ForceClosedChannel{
// Obfuscated fields.
ClosingTxid: closingTxid,
LimboBalance: limboBalance,
RecoveredBalance: recoveredBalance,
Channel: pendingChannel,
// Non-obfuscated fields.
MaturityHeight: c.MaturityHeight,
BlocksTilMaturity: c.BlocksTilMaturity,
Anchor: c.Anchor,
// Omitted fields.
PendingHtlcs: []*lnrpc.PendingHTLC{},
}
pendingForceCloses[i] = &pendingForceClose
}
for i, c := range r.WaitingCloseChannels {
var err error
pendingChannel, err := obfuscatePendingChannel(
c.Channel, tx, randIntn, flags,
)
if err != nil {
return err
}
limboBalance, err := maybeHideAmount(
flags, randIntn, c.LimboBalance,
)
if err != nil {
return err
}
if limboBalance > pendingChannel.Capacity {
limboBalance = pendingChannel.Capacity
}
closingTxid := c.ClosingTxid
if !flags.Contains(session.ClearClosingTxIds) {
closingTxid, err = firewalldb.HideString(
tx, closingTxid,
)
if err != nil {
return err
}
}
// The closing tx hash is constrained by the
// request, see docstring, which is why we only
// obfuscate if a value is set.
closingTxHex := c.ClosingTxHex
if c.ClosingTxHex != "" &&
!flags.Contains(
session.ClearClosingTxIds,
) {
closingTxHex, err = firewalldb.HideString(
tx, closingTxHex,
)
if err != nil {
return err
}
}
waitingCloseChannel := lnrpc.PendingChannelsResponse_WaitingCloseChannel{
Channel: pendingChannel,
LimboBalance: limboBalance,
ClosingTxid: closingTxid,
ClosingTxHex: closingTxHex,
// Omitted.
Commitments: &lnrpc.PendingChannelsResponse_Commitments{},
}
waitingCloses[i] = &waitingCloseChannel
}
return nil
})
if err != nil {
return nil, err
}
totalLimbo, err := maybeHideAmount(
flags, randIntn, r.TotalLimboBalance,
)
if err != nil {
return nil, err
}
return &lnrpc.PendingChannelsResponse{
TotalLimboBalance: totalLimbo,
PendingOpenChannels: pendingOpens,
PendingClosingChannels: pendingCloses,
PendingForceClosingChannels: pendingForceCloses,
WaitingCloseChannels: waitingCloses,
}, nil
}
}
// maybeHideAmount hides an amount if the privacy flag is not set.
func maybeHideAmount(flags session.PrivacyFlags, randIntn func(int) (int,
error), a int64) (int64, error) {

View file

@ -36,6 +36,33 @@ func TestPrivacyMapper(t *testing.T) {
obfusTxID1 := "45ec471bfccb0b7b9a8bc4008248931c59ad994903e07b54f54821ea3ef5cc5c"
obfusOut1 := uint32(1642614131)
clearPending := &lnrpc.PendingChannelsResponse_PendingChannel{
RemoteNodePub: "01020304",
LocalBalance: 100_000,
RemoteBalance: 20_000,
LocalChanReserveSat: 1_000,
RemoteChanReserveSat: 1_000,
Capacity: 120_000,
ChannelPoint: outPoint(
clearTxID, 0,
),
Initiator: lnrpc.Initiator_INITIATOR_LOCAL,
Memo: "something",
}
obfuscatedPending := &lnrpc.PendingChannelsResponse_PendingChannel{
RemoteNodePub: "c8134495",
LocalBalance: 95_100,
RemoteBalance: 19_000,
Capacity: 114_100,
ChannelPoint: outPoint(
obfusTxID0,
obfusOut0,
),
Initiator: lnrpc.Initiator_INITIATOR_LOCAL,
Memo: "something",
}
// Define some preexisting mappings for the privacy mapper.
mapPreloadRealToPseudo := map[string]string{
"Tinker Bell's pub key": "a44ef01c3bff970ef495c",
@ -518,6 +545,133 @@ func TestPrivacyMapper(t *testing.T) {
},
},
},
{
name: "PendingChannels Response",
uri: "/lnrpc.Lightning/PendingChannels",
msgType: rpcperms.TypeResponse,
msg: &lnrpc.PendingChannelsResponse{
PendingOpenChannels: []*lnrpc.PendingChannelsResponse_PendingOpenChannel{
{
CommitFee: 123,
Channel: clearPending,
},
},
PendingClosingChannels: []*lnrpc.PendingChannelsResponse_ClosedChannel{
{
Channel: clearPending,
ClosingTxid: clearTxID,
},
},
PendingForceClosingChannels: []*lnrpc.PendingChannelsResponse_ForceClosedChannel{
{
Channel: clearPending,
ClosingTxid: clearTxID,
LimboBalance: 100_000,
MaturityHeight: 123,
BlocksTilMaturity: 123,
RecoveredBalance: 100_000,
PendingHtlcs: []*lnrpc.PendingHTLC{
{
Incoming: true,
},
},
Anchor: 1,
},
},
WaitingCloseChannels: []*lnrpc.PendingChannelsResponse_WaitingCloseChannel{
{
Channel: clearPending,
LimboBalance: 100_000,
Commitments: &lnrpc.PendingChannelsResponse_Commitments{
LocalTxid: clearTxID,
},
ClosingTxid: clearTxID,
ClosingTxHex: clearTxID,
},
},
},
expectedReplacement: &lnrpc.PendingChannelsResponse{
PendingOpenChannels: []*lnrpc.PendingChannelsResponse_PendingOpenChannel{
{
CommitFee: 123,
Channel: obfuscatedPending,
},
},
PendingClosingChannels: []*lnrpc.PendingChannelsResponse_ClosedChannel{
{
Channel: obfuscatedPending,
ClosingTxid: obfusTxID0,
},
},
PendingForceClosingChannels: []*lnrpc.PendingChannelsResponse_ForceClosedChannel{
{
Channel: obfuscatedPending,
ClosingTxid: obfusTxID0,
LimboBalance: 95_100,
MaturityHeight: 123,
BlocksTilMaturity: 123,
RecoveredBalance: 95_100,
PendingHtlcs: []*lnrpc.PendingHTLC{},
Anchor: 1,
},
},
WaitingCloseChannels: []*lnrpc.PendingChannelsResponse_WaitingCloseChannel{
{
Channel: obfuscatedPending,
LimboBalance: 95_100,
Commitments: &lnrpc.PendingChannelsResponse_Commitments{},
ClosingTxid: obfusTxID0,
ClosingTxHex: obfusTxID0,
},
},
},
},
{
name: "PendingChannels Response clear",
uri: "/lnrpc.Lightning/PendingChannels",
msgType: rpcperms.TypeResponse,
msg: &lnrpc.PendingChannelsResponse{
PendingOpenChannels: []*lnrpc.PendingChannelsResponse_PendingOpenChannel{
{
CommitFee: 123,
Channel: &lnrpc.PendingChannelsResponse_PendingChannel{
RemoteNodePub: "01020304",
LocalBalance: 100_000,
RemoteBalance: 100_000,
Capacity: 120_000,
ChannelPoint: outPoint(
clearTxID, 0,
),
Initiator: lnrpc.Initiator_INITIATOR_LOCAL,
Memo: "something",
},
},
},
},
privacyFlags: []session.PrivacyFlag{
session.ClearPubkeys,
session.ClearAmounts,
session.ClearChanIDs,
},
expectedReplacement: &lnrpc.PendingChannelsResponse{
PendingOpenChannels: []*lnrpc.PendingChannelsResponse_PendingOpenChannel{
{
CommitFee: 123,
Channel: &lnrpc.PendingChannelsResponse_PendingChannel{
RemoteNodePub: "01020304",
LocalBalance: 100_000,
RemoteBalance: 100_000,
Capacity: 120_000,
ChannelPoint: outPoint(
clearTxID, 0,
),
Initiator: lnrpc.Initiator_INITIATOR_LOCAL,
Memo: "something",
},
},
},
},
},
}
decodedID := &lnrpc.MacaroonId{