Skip to content

Commit f5b9c54

Browse files
authored
Update EIP-8052: Improvements and test vectors
Merged by EIP-Bot.
1 parent 187d1ce commit f5b9c54

4 files changed

Lines changed: 1442 additions & 944 deletions

File tree

EIPS/eip-8052.md

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ Quantum computers pose a long-term risk to classical cryptographic algorithms. I
3232

3333
Integrating post-quantum signature schemes is crucial to future-proof Ethereum and other EVM-based environments. It shall be noted that infrastructure for post-quantum signatures should be deployed *before* quantum adversaries are known to be practical because it takes on the order of years for existing applications to integrate.
3434

35-
Falcon, a lattice-based scheme standardized by NIST, offers high security against both classical and quantum adversaries. Its compact signature size (~666 bytes for Falcon-512) and its efficient verification algorithm make it well-suited for blockchain applications, where gas usage and transaction throughput are critical considerations.
36-
<!-- When the public key is also stored together with the signature, it leads to 666 + 897 = 1563 bytes.
35+
Falcon, a lattice-based scheme standardized by NIST, offers high security against both classical and quantum adversaries. Its compact signature size (666 bytes for Falcon-512) and its efficient verification algorithm make it well-suited for blockchain applications, where gas usage and transaction throughput are critical considerations.
36+
<!-- When the public key is also stored together with the signature, it leads to 666 + 896 = 1562 bytes.
3737
Using the recovery mode, it can be reduced to 1292 + 32 = 1324 bytes. -->
3838
<!--
3939
For Falcon,
4040
σ=666 (40 bytes of salt, ~626 bytes for s2)
41-
pk = 897,
42-
TOTAL=1563
41+
pk = 896,
42+
TOTAL=1562
4343
For FalconRec,
4444
σ=1292 (40 bytes of salt, ~626 bytes for s1, ~626 bytes for s2),
4545
pk = 32,
@@ -67,7 +67,7 @@ This setting leads to the following size for signatures and keys:
6767
| **n** (lattice dimension) | `512` |
6868
| **q** (modulus) | `12289` |
6969
| Padded signature size | `666` bytes |
70-
| Public Key size | `897` bytes |
70+
| Public Key size | `896` bytes |
7171
| Private key size | `1281` bytes |
7272

7373

@@ -79,14 +79,12 @@ From a high level, a signature verification can be decomposed in two steps:
7979
The following pseudo-code highlights how these two algorithms are involved in a Falcon verification, and how the modularity of the challenge computation opens up to two version of Falcon:
8080

8181
```python
82-
def falcon512_verify(message: bytes, signature: Tuple[bytes, bytes], pubkey: bytes) -> bool:
82+
def falcon512_verify(message: bytes, signature: bytes, pubkey: bytes) -> bool:
8383
"""
8484
Verify a Falcon Signature following NIST standard
8585
Args:
8686
message (bytes): The message to sign.
87-
signature (Tuple[bytes, bytes]): A tuple (r, s), where:
88-
- r (bytes): The salt.
89-
- s (bytes): The signature vector.
87+
signature (666 bytes): The signature containing 40 bytes of salt and 626 bytes of compressed signature.
9088
pubkey (bytes): The Falcon public key.
9189
Returns:
9290
bool: True if the signature is valid, False otherwise.
@@ -100,14 +98,12 @@ def falcon512_verify(message: bytes, signature: Tuple[bytes, bytes], pubkey: byt
10098
```
10199

102100
```python
103-
def ethfalcon512_verify(message: bytes, signature: Tuple[bytes, bytes], pubkey: bytes) -> bool:
101+
def ethfalcon512_verify(message: bytes, signature: bytes, pubkey: bytes) -> bool:
104102
"""
105103
Verify a Falcon Signature (EVM-friendly mode)
106104
Args:
107105
message (bytes): The message to sign.
108-
signature (Tuple[bytes, bytes]): A tuple (r, s), where:
109-
- r (bytes): The salt.
110-
- s (bytes): The signature vector.
106+
signature (666 bytes): The signature containing 40 bytes of salt and 626 bytes of compressed signature.
111107
pubkey (bytes): The Falcon public key.
112108
Returns:
113109
bool: True if the signature is valid, False otherwise.
@@ -180,14 +176,14 @@ The hash-to-point function computes extendable Output from a hash Function (XOF)
180176
Using one of the XOFs above (SHAKE256 or Keccak-PRNG), it is possible to instantiate two (precompiles) algorithms for hashing into points:
181177

182178
```python
183-
def FALCON_HASH_TO_POINT_SHAKE256(message: bytes32, signature: Tuple[bytes, bytes]) -> bool:
179+
def FALCON_HASH_TO_POINT_SHAKE256(message: bytes32, signature: bytes) -> bool:
184180
"""
185181
Compute the Hash To Point Falcon Challenge.
186182
Args:
187183
message (bytes32): The original message (hash).
188-
signature (Tuple[bytes, bytes]): A tuple (r, s), where:
189-
- r (bytes): The salt.
190-
- s (bytes): The signature vector.
184+
signature (666 bytes): containing:
185+
- r (40 bytes): The salt.
186+
- s (626 bytes): The compressed signature vector.
191187
Returns:
192188
c: The Hash To Point polynomial challenge as a vector.
193189
"""
@@ -196,7 +192,7 @@ def FALCON_HASH_TO_POINT_SHAKE256(message: bytes32, signature: Tuple[bytes, byte
196192
q = 12289 # Falcon modulus
197193

198194
# Step 1: Parse Input Data
199-
r, s2_compressed = signature # Extract salt and compressed signature vector
195+
r, s2_compressed = signature[:40], signature[40:] # Extract salt and compressed signature vector
200196

201197
# Step 2: Verify Well-formedness
202198
if not is_valid_signature_format(s2_compressed, pubkey):
@@ -216,14 +212,14 @@ def FALCON_HASH_TO_POINT_SHAKE256(message: bytes32, signature: Tuple[bytes, byte
216212
```
217213

218214
```python
219-
def FALCON_HASH_TO_POINT_KECCAKPRNG(message: bytes32, signature: Tuple[bytes, bytes]) -> bool:
215+
def FALCON_HASH_TO_POINT_KECCAKPRNG(message: bytes32, signature: bytes) -> bool:
220216
"""
221217
Compute the Hash To Point Falcon Challenge using Keccak-PRNG.
222218
Args:
223219
message (bytes32): The original message (hash).
224-
signature (Tuple[bytes, bytes]): A tuple (r, s), where:
225-
- r (bytes): The salt.
226-
- s (bytes): The signature vector.
220+
signature (666 bytes): containing:
221+
- r (40 bytes): The salt.
222+
- s (626 bytes): The compressed signature vector.
227223
Returns:
228224
c: The Hash To Point polynomial challenge as a vector.
229225
"""
@@ -232,7 +228,7 @@ def FALCON_HASH_TO_POINT_KECCAKPRNG(message: bytes32, signature: Tuple[bytes, by
232228
q = 12289 # Falcon modulus
233229

234230
# Step 1: Parse Input Data
235-
r, s2_compressed = signature # Extract salt and compressed signature vector
231+
r, s2_compressed = signature[:40], signature[40:] # Extract salt and compressed signature vector
236232

237233
# Step 2: Verify Well-formedness
238234
if not is_valid_signature_format(s2_compressed, pubkey):
@@ -251,11 +247,11 @@ def FALCON_HASH_TO_POINT_KECCAKPRNG(message: bytes32, signature: Tuple[bytes, by
251247
return c
252248
```
253249

254-
#### Encoding of the challenge polynomial `c` (897 bytes, normative)
250+
#### Encoding of the challenge polynomial `c` (896 bytes, normative)
255251

256252
* Domain: degree-512 polynomial with coefficients in `[0, q)`, `q = 12289`.
257253
* Order: coefficients are serialized in index order `c[0], c[1], …, c[511]`.
258-
* Bit-packing: each coefficient is a 14-bit **big-endian** unsigned integer. Concatenate all 512 encodings into a bitstring, then left-pad the final byte with zero bits to reach exactly **897 bytes**. Consumers MUST reject encodings that are not exactly 897 bytes.
254+
* Bit-packing: each coefficient is a 14-bit **big-endian** unsigned integer. Concatenate all 512 encodings into a bitstring of exactly **896 bytes**. Consumers MUST reject encodings that are not exactly 896 bytes.
259255

260256
### Core algorithm
261257

@@ -278,15 +274,15 @@ def FALCON_HASH_TO_POINT_KECCAKPRNG(message: bytes32, signature: Tuple[bytes, by
278274
The following code illustrates Falcon core algorithm:
279275

280276
```python
281-
def FALCON_CORE(signature: Tuple[bytes, bytes], h: bytes, challenge: bytes) -> bool:
277+
def FALCON_CORE(signature: bytes, h: bytes, challenge: bytes) -> bool:
282278
"""
283279
Verify Falcon Core Algorithm
284280
Args:
285-
signature (Tuple[bytes, bytes]): A tuple (r, s), where:
286-
- r (bytes): The salt.
287-
- s (bytes): The signature vector.
288-
- h (bytes): The Falcon public key in the ntt domain, computed as h=ntt(pubkey) externally.
289-
- challenge (bytes): The Falcon Hash To Point Challenge.
281+
- signature (666 bytes): containing:
282+
- r (40 bytes): The salt.
283+
- s (626 bytes): The compressed signature vector.
284+
- h (bytes): The Falcon public key in the ntt domain, computed as h=ntt(pubkey) externally.
285+
- challenge (bytes): The Falcon Hash To Point Challenge.
290286
Returns:
291287
bool: True if the signature is valid, False otherwise.
292288
"""
@@ -296,7 +292,7 @@ def FALCON_CORE(signature: Tuple[bytes, bytes], h: bytes, challenge: bytes) -> b
296292
ACCEPTANCE_BOUND = 34034726 # Falcon-512 acceptance bound
297293

298294
# Step 1: Parse Input Data
299-
r, s2_compressed = signature # Extract salt and compressed signature vector
295+
r, s2_compressed = signature[:40], signature[40:] # Extract salt and compressed signature vector
300296

301297
# Step 2: Verify Well-formedness
302298
if not is_valid_signature_format(s2_compressed, pubkey):
@@ -336,7 +332,7 @@ The following requirements MUST be checked by the precompiled contract to verify
336332
**Raw Input data**
337333

338334
* Verify that the message is 32-bytes long,
339-
* Verify that the public key is 897-bytes long,
335+
* Verify that the public key is 896-bytes long,
340336
* Verify that the signature is 666-bytes long.
341337

342338
**Parsed Input data**
@@ -358,12 +354,12 @@ The precompiled contracts are proposed with the following input and outputs, whi
358354

359355
**Input**
360356

361-
* 32 bytes: message hash `m`
362-
* 666 bytes: Falcon-512 compressed signature `(r || s2_compressed)`
357+
* 32 bytes: message hash `m`,
358+
* 666 bytes: salt `r` (40 bytes), Falcon-512 compressed signature `s2_compressed` (626 bytes) concatenated: `r || s2_compressed`.
363359

364360
**Output**
365361

366-
* 897 bytes: packed challenge polynomial `c` (see §3.1 encoding)
362+
* 896 bytes: packed challenge polynomial `c` (see §3.1 encoding)
367363

368364
#### `FALCON_HASH_TO_POINT_KECCAKPRNG`
369365

@@ -372,9 +368,9 @@ Same I/O as §4.1, but the XOF is Keccak-PRNG. The construction is normative and
372368
#### `FALCON_CORE`
373369

374370
* **Input data**
375-
* 666 bytes for Falcon-512 compressed signature
376-
* 897 bytes for Falcon-512 public key
377-
* 897 bytes for the Hash To Point Challenge
371+
* 666 bytes for Falcon-512 signature
372+
* 896 bytes for Falcon-512 public key
373+
* 896 bytes for the Hash To Point Challenge
378374
* **Output data**:
379375
* If the core algorithm process succeeds, it returns 1 in 32 bytes format.
380376
* If the core algorithm process fails, it does not return any output data.
@@ -383,8 +379,9 @@ Same I/O as §4.1, but the XOF is Keccak-PRNG. The construction is normative and
383379

384380
* Invalid input length (not compliant to described input)
385381
* Invalid field element encoding (≥ q)
386-
* Invalid norm bound
387-
* Signature verification failure
382+
* Invalid signature decompression
383+
384+
Note that a well-formed but invalid signature does not produce an error, but `FALCON_CORE` invalidates the signature (and it does not return any output data).
388385

389386
#### Precompiled Contract Gas Usage
390387

@@ -475,7 +472,7 @@ In the format of [EIP-7932](./eip-7932.md):
475472

476473
## Test Cases
477474

478-
A set of test vectors for verifying implementations is located in a separate file (to be provided for each opcode). For the NIST compliant version, KATS are reproduced.
475+
A set of test vectors for verifying implementations is located in a separate file (to be provided for each opcode). For the NIST compliant version, KATs are reproduced for HashToPoint (with a provided salt `r`) as well as for the compressed signature vector s2 (with a provided `HashToPoint` input). Note that `s2` is not padded with zeroes, leading to a maximum size of 626 bytes.
479476

480477

481478
## Reference Implementation

0 commit comments

Comments
 (0)