Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 44 additions & 27 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,53 @@

## Supported Versions

We have a 3 month release cycle, and the last two versions are supported.
Only the latest `-blake2b.N` release receives security fixes. Upgrade to it before reporting, if you can.

## Reporting a Vulnerability

To report security vulnerabilities, please send an email to one of the following addresses:
- `rusty@rustcorp.com.au`
- `security@blockstream.com`

Note: These email addresses are exclusively for vulnerability reporting.

For all other inquiries/communication, please refer to the [Reach Out to Us](https://github.com/ElementsProject/lightning?tab=readme-ov-file#reach-out-to-us) section in our README.
Report vulnerabilities in this repository to: security@privkey.io

A vulnerability in upstream Core Lightning that is not specific to this version belongs with its maintainers, as described in [their security policy](https://github.com/ElementsProject/lightning/blob/master/SECURITY.md).

**PGP Key:**
```
-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEaGVRQBYJKwYBBAHaRw8BAQdAF9dwAiS2eOxTwDNy/1LvnTfqP6m8h4rY
BZxx1v30tJjNKXNlY3VyaXR5QHByaXZrZXkuaW8gPHNlY3VyaXR5QHByaXZr
ZXkuaW8+wsARBBMWCgCDBYJoZVFAAwsJBwmQuDUnCJWoCwtFFAAAAAAAHAAg
c2FsdEBub3RhdGlvbnMub3BlbnBncGpzLm9yZ1633ld0W07KI/fGiqv/RPdn
rKNn456SSIdAiJXTdN5bAxUKCAQWAAIBAhkBApsDAh4BFiEE50kamFXBudeZ
trSRuDUnCJWoCwsAAELLAQD8gmp8ClfdlOXbOEeFGuvz4LoDlAktfN4L28Wl
EeedvQD/VrR64FFB0ZsJ4eW0axdjcT3ph4xv96Lqn6tNO0WmUgbOOARoZVFA
EgorBgEEAZdVAQUBAQdANUQ4xZ3hZzlCsOAJeVN7PkZwEF/Q9DdTZNaUkFXT
8T8DAQgHwr4EGBYKAHAFgmhlUUAJkLg1JwiVqAsLRRQAAAAAABwAIHNhbHRA
bm90YXRpb25zLm9wZW5wZ3Bqcy5vcme2RcuuIdqCuXe6p0nzXLc6RICA0iVC
/6RhJxujpAdrdQKbDBYhBOdJGphVwbnXmba0kbg1JwiVqAsLAABrEwEA1Y9e
BF6SXFgvOtu+iRdD6e+a1E1l0j3N8qyqb1tJ39MBAMT4UzjZ9IQ2Brz3ZYmV
kyew0MAIis6DCtVkNduBlBYA
=3LT9
-----END PGP PUBLIC KEY BLOCK-----
```

**Fingerprint:** `E749 1A98 55C1 B9D7 99B6 B491 B835 2708 95A8 0B0B`

**Key Servers:**
- [keys.openpgp.org](https://keys.openpgp.org/search?q=E7491A9855C1B9D799B6B491B835270895A80B0B)
- [keyserver.ubuntu.com](https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xE7491A9855C1B9D799B6B491B835270895A80B0B)

**Process:**
1. Encrypt report with PGP key above
2. Send to security@privkey.io
3. Expect acknowledgment within 48 hours
4. Coordinate disclosure timeline (default: 90 days)

## Signatures For Releases

The following keys may be used to communicate sensitive information to
developers, and to validate signatures on releases:

| Name | Email | Fingerprint |
|------|-------|-------------|
| Blockstream Security Reporting | `security@blockstream.com` | 1176 542D A98E 71E1 3372 2EF7 4AC8 CC88 6844 A2D6 |
| Rusty Russell | `rusty@rustcorp.com.au` | 15EE 8D6C AB0E 7F0C F999 BFCB D920 0E6C D1AD B8F1 |
| Christian Decker | `decker@blockstream.com` | B731 AAC5 21B0 1385 9313 F674 A26D 6D9F E088 ED58 |
| Lisa Neigut | `niftynei@gmail.com` | 30DE 693A E0DE 9E37 B3E7 EB6B BFF0 F678 10C1 EED1 |
| Alex Myers | `alex@endothermic.dev` | 0437 4E42 789B BBA9 462E 4767 F3BF 63F2 7474 36AB |
| Peter Neuroth | `pet.v.ne@gmail.com` | 653B 19F3 3DF7 EFF3 E9D1 C94C C3F2 1EE3 87FF 4CD2 |
| Shahana Farooqui | `sfarooqui@blockstream.com` | 0CCA 8183 C13A 2389 A9C5 FD29 BFB0 1536 0049 CB56 |
| Madeline Paech | `madeline@blockstream.com` | 7169 D262 72B5 0A3F 531A A1C2 A57A FC23 1B58 0804 |
| Blockstream CLN Release | `cln@blockstream.com` | 616C 52F9 9D06 12B2 A151 B107 4129 A994 AA7E 9852 |
| Sangbida Chaudhuri | `sangbidac@gmail.com` | 1A37 1C2C 3064 5FAA 91AA 6B7D B643 E612 8422 1961 |

You can import a key by running the following command with that individual’s fingerprint:
`gpg --keyserver hkps://keys.openpgp.org --recv-keys "<fingerprint>"`.
Ensure that you put quotes around fingerprints containing spaces.
Releases are signed with `A47D 99B6 DB0D 715D 40C5 9A20 23AE 8A8E A7E2 4E38` (Kyle Santiago, `kyle@privkey.io`), attached to each release as `privkeyio-signing-key.asc`. Verify a download with:

```
gpg --import privkeyio-signing-key.asc
gpg --verify SHA256SUMS-<version>.asc SHA256SUMS-<version>
sha256sum -c --ignore-missing SHA256SUMS-<version>
```
14 changes: 8 additions & 6 deletions doc/blake2b-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,11 @@ If you implemented the old version:
it is unreachable. If you were relying on it to catch a wallet carried
between chains, it will not.

The check that does still catch a node pointed at the wrong chain is not a
Lightning check at all: read the block header at the activation height and
refuse it if it is 80 bytes rather than 164. That touches `chain_hash`
nowhere and came through this change unaltered.
The check that does catch a node pointed at the wrong chain is not a
Lightning check at all: refuse any block at or above the activation height
whose header is 80 bytes rather than 164. Core Lightning does this for every
block it adds, so a node whose backend never switched to BLAKE2b stops
rather than following it. It touches `chain_hash` nowhere.

### 8b. The distinct BOLT 11 prefix, withdrawn 2026-09-19

Expand Down Expand Up @@ -388,8 +389,9 @@ block 961,640, block headers are 164 bytes (the 80-byte layout plus a second
section), the block id is a BLAKE2b digest of the header rather than SHA256d,
and the header's time field is offset. A node that reads block headers itself
(rather than through a node's RPC) needs the Knots definition. Core
Lightning reads blocks through the backend and does not parse headers itself;
Lightning Fork's parser is in its btcd fork's `wire` package.
Lightning fetches blocks through the backend and parses the header in
`bitcoin/block.c`; Lightning Fork's parser is in its btcd fork's `wire`
package.

## 10. What is deliberately unchanged

Expand Down
10 changes: 10 additions & 0 deletions lightningd/chaintopology.c
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,16 @@ static struct block *new_block(struct chain_topology *topo,
unsigned int height)
{
struct block *b = tal(topo, struct block);
u32 activation = topo->ld->dev_blake2b_activation_height;

/* bitcoind reports the same chain name either way, so this is what
* tells a backend that never switched to BLAKE2b from one that did. */
if (activation == 0)
activation = chainparams->blake2b_activation_height;
if (activation != 0 && height >= activation && !blk->hdr.header_v2)
fatal("Block %u is at or above the BLAKE2b activation height %u"
" but has an 80-byte header: our Bitcoin backend is not"
" following BLAKE2b proof of work", height, activation);

bitcoin_block_blkid(blk, &b->blkid);
log_debug(topo->log, "Adding block %u: %s",
Expand Down
45 changes: 44 additions & 1 deletion tests/test_blake2b_differentiation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Mandatory peer feature without invoice modifications."""
from fixtures import * # noqa: F401,F403
from pyln.client import RpcError
from utils import TEST_NETWORK, wait_for
from utils import TEST_NETWORK, sync_blockheight, wait_for
import pytest

pytestmark = pytest.mark.skipif(TEST_NETWORK != 'regtest', reason='Blake2b peer features do not apply to Elements')
Expand Down Expand Up @@ -93,3 +93,46 @@ def test_blake2b_invreq_without_bit_refused(node_factory):

with pytest.raises(RpcError, match=r'option_blake2b'):
l2.rpc.fetchinvoice(offer, 1000)


NOT_BLAKE2B = 'is at or above the BLAKE2b activation height 110 but has an 80-byte header'


@pytest.mark.parametrize('bitcoind', [False], indirect=True)
def test_backend_without_blake2b_at_startup(node_factory, bitcoind):
"""A backend that never switched to BLAKE2b must stop us before we sync."""
bitcoind.env['BLAKE2B_ACTIVATION_HEIGHT'] = '100000'
bitcoind.start()
bitcoind.generate_block(120)
l1 = node_factory.get_node(start=False, may_fail=True, broken_log='80-byte header',
options={'dev-blake2b-activation-height': 110})
l1.daemon.start(wait_for_initialized=False, stderr_redir=True)
assert l1.daemon.wait() != 0
assert l1.daemon.is_in_stderr(NOT_BLAKE2B)


@pytest.mark.parametrize('bitcoind', [False], indirect=True)
def test_backend_without_blake2b_at_activation(node_factory, bitcoind):
"""A backend that stays on 80-byte headers past the activation stops us there."""
bitcoind.env['BLAKE2B_ACTIVATION_HEIGHT'] = '100000'
bitcoind.start()
bitcoind.generate_block(101)
# Past startup, fatal() aborts, so the crash report is expected too.
l1 = node_factory.get_node(start=False, may_fail=True,
broken_log='80-byte header|FATAL SIGNAL|backtrace',
options={'dev-blake2b-activation-height': 110})
l1.daemon.start(stderr_redir=True)
l1.daemon.wait_for_log('Adding block 101')
bitcoind.generate_block(10)
assert l1.daemon.wait() != 0
assert l1.daemon.is_in_stderr('Block 110 ' + NOT_BLAKE2B)
assert l1.daemon.is_in_log('Adding block 109')
assert not l1.daemon.is_in_log('Adding block 110')


def test_backend_with_blake2b_passes_activation(node_factory, bitcoind):
"""A BLAKE2b backend crosses the activation without complaint."""
l1 = node_factory.get_node(options={'dev-blake2b-activation-height': 110})
bitcoind.generate_block(20)
sync_blockheight(bitcoind, [l1])
assert l1.rpc.getinfo()['blockheight'] >= 120
Loading