|
| 1 | +# RSA Verifier Circuits — Reference |
| 2 | + |
| 3 | +Standalone RSA-2048 and RSA-4096 signature verification circuits for RS256 JWT verification. This is a **proof-of-concept** on the [`feat/rsa-verifier-circuit`](https://github.com/moven0831/zkID/tree/feat/rsa-verifier-circuit) branch, supplementary to the primary ES256 (JWT + Show) construction. |
| 4 | + |
| 5 | +> **Note:** The main zkID pipeline uses ES256 with shared witness commitments and reblinding for unlinkability. These RSA circuits are independent — no shared witness, no reblinding — and are provided as a reference for teams using RS256 instead of ES256. |
| 6 | +
|
| 7 | +## Overview |
| 8 | + |
| 9 | +The RSA verifier circuit performs standalone RSA signature verification in three steps: |
| 10 | + |
| 11 | +1. **SHA-256** — hash the padded message using `@zk-email/circuits` |
| 12 | +2. **Bits2Limbs** — convert the 256-bit big-endian hash to little-endian `n`-bit RSA limbs |
| 13 | +3. **RSAVerifier65537** — verify PKCS#1 v1.5 RSA-SHA256 signature against the hash |
| 14 | + |
| 15 | +Unlike the main JWT/Show pipeline, these circuits operate independently with no shared witness commitments (`comm_w_shared`) and no reblinding support. |
| 16 | + |
| 17 | +## Circuit Specification |
| 18 | + |
| 19 | +### `RSAVerify(maxByteLength, n, k)` |
| 20 | + |
| 21 | +| Parameter | Description | |
| 22 | +|-----------|-------------| |
| 23 | +| `maxByteLength` | Max SHA-256 padded message length in bytes (must be multiple of 64) | |
| 24 | +| `n` | Bits per RSA limb (recommended: 121) | |
| 25 | +| `k` | Number of RSA limbs (17 for RSA-2048, 34 for RSA-4096) | |
| 26 | + |
| 27 | +**Inputs:** |
| 28 | + |
| 29 | +| Signal | Type | Description | |
| 30 | +|--------|------|-------------| |
| 31 | +| `message[maxByteLength]` | private | SHA-256 padded message bytes | |
| 32 | +| `messageLength` | private | Actual message length | |
| 33 | +| `signature[k]` | private | RSA signature as `k` limbs | |
| 34 | +| `modulus[k]` | **public** | RSA public modulus as `k` limbs | |
| 35 | + |
| 36 | +### Helper: `Bits2Limbs(n, k, totalBits)` |
| 37 | + |
| 38 | +Converts big-endian SHA-256 output bits to little-endian `n`-bit limbs for the RSA verifier. Maps LSB-first limb bit positions to big-endian SHA output indices. |
| 39 | + |
| 40 | +### Concrete Instantiations |
| 41 | + |
| 42 | +| Circuit | Template | File | |
| 43 | +|---------|----------|------| |
| 44 | +| RSA-2048 | `RSAVerify(64, 121, 17)` | `circuits/main/rsa_verify_2048.circom` | |
| 45 | +| RSA-4096 | `RSAVerify(64, 121, 34)` | `circuits/main/rsa_verify_4096.circom` | |
| 46 | + |
| 47 | +## Build & Run Commands |
| 48 | + |
| 49 | +### Circom Compilation |
| 50 | + |
| 51 | +From `wallet-unit-poc/circom/`: |
| 52 | + |
| 53 | +```sh |
| 54 | +yarn # install deps (includes @zk-email/circuits) |
| 55 | +yarn generate:rsa # generate test inputs (RSA key pair + signature) |
| 56 | +yarn compile:rsa2048 # compile RSA-2048 circuit |
| 57 | +yarn compile:rsa4096 # compile RSA-4096 circuit |
| 58 | +``` |
| 59 | + |
| 60 | +### Rust CLI (Spartan2) |
| 61 | + |
| 62 | +From `wallet-unit-poc/ecdsa-spartan2/`: |
| 63 | + |
| 64 | +```sh |
| 65 | +# RSA circuits require the rsa-circuits feature flag |
| 66 | +cargo run --release --features rsa-circuits -- rsa2048 setup --input ../circom/inputs/rsa_verify_2048/default.json |
| 67 | +cargo run --release --features rsa-circuits -- rsa2048 prove --input ../circom/inputs/rsa_verify_2048/default.json |
| 68 | +cargo run --release --features rsa-circuits -- rsa2048 verify |
| 69 | + |
| 70 | +cargo run --release --features rsa-circuits -- rsa4096 setup --input ../circom/inputs/rsa_verify_4096/default.json |
| 71 | +cargo run --release --features rsa-circuits -- rsa4096 prove --input ../circom/inputs/rsa_verify_4096/default.json |
| 72 | +cargo run --release --features rsa-circuits -- rsa4096 verify |
| 73 | + |
| 74 | +# Full benchmark for a single circuit |
| 75 | +cargo run --release --features rsa-circuits -- rsa2048 benchmark --input ../circom/inputs/rsa_verify_2048/default.json |
| 76 | +cargo run --release --features rsa-circuits -- rsa4096 benchmark --input ../circom/inputs/rsa_verify_4096/default.json |
| 77 | +``` |
| 78 | + |
| 79 | +### Feature Flags |
| 80 | + |
| 81 | +```toml |
| 82 | +[features] |
| 83 | +default = ["jwt-circuit", "show-circuit"] |
| 84 | +jwt-circuit = [] |
| 85 | +show-circuit = [] |
| 86 | +rsa-circuits = [] # opt-in, not built by default |
| 87 | +``` |
| 88 | + |
| 89 | +**Reblind is not supported** for RSA circuits — the `shared()` and `precommitted()` trait methods return empty vectors. |
| 90 | + |
| 91 | +## Benchmarks |
| 92 | + |
| 93 | +**Test Device:** MacBook Pro, M4, 14-core GPU, 24GB RAM |
| 94 | + |
| 95 | +### Circuit Characteristics |
| 96 | + |
| 97 | +| Metric | RSA-2048 | RSA-4096 | |
| 98 | +|--------|----------|----------| |
| 99 | +| Wires | 215,439 | 405,962 | |
| 100 | +| Constraints | 216,400 | 407,824 | |
| 101 | +| Private Inputs | 82 | 99 | |
| 102 | +| Public Inputs | 17 | 34 | |
| 103 | + |
| 104 | +### Timing |
| 105 | + |
| 106 | +| Operation | RSA-2048 | RSA-4096 | |
| 107 | +|-----------|----------|----------| |
| 108 | +| Setup | 713 ms | 2,042 ms | |
| 109 | +| Prove | 345 ms | 767 ms | |
| 110 | +| Verify | 33 ms | 68 ms | |
| 111 | + |
| 112 | +### Artifact Sizes |
| 113 | + |
| 114 | +| Artifact | RSA-2048 | RSA-4096 | |
| 115 | +|----------|----------|----------| |
| 116 | +| Proving Key | 65.92 MB | 174.91 MB | |
| 117 | +| Verifying Key | 65.92 MB | 174.91 MB | |
| 118 | +| Proof | 50.61 KB | 59.87 KB | |
| 119 | +| Witness | 8.01 MB | 16.02 MB | |
| 120 | + |
| 121 | +Source: [ZK-based-Human-Verification #14](https://github.com/zkmopro/ZK-based-Human-Verification/issues/14#issuecomment-3883009179) |
| 122 | + |
| 123 | +## Key Files on Branch |
| 124 | + |
| 125 | +All files are on the [`feat/rsa-verifier-circuit`](https://github.com/moven0831/zkID/tree/feat/rsa-verifier-circuit) branch. |
| 126 | + |
| 127 | +| File | Description | |
| 128 | +|------|-------------| |
| 129 | +| `circom/circuits/rsa_verify.circom` | `RSAVerify` and `Bits2Limbs` circuit templates | |
| 130 | +| `circom/circuits/main/rsa_verify_2048.circom` | RSA-2048 instantiation (`k=17`) | |
| 131 | +| `circom/circuits/main/rsa_verify_4096.circom` | RSA-4096 instantiation (`k=34`) | |
| 132 | +| `circom/inputs/rsa_verify_2048/default.json` | Test inputs for RSA-2048 | |
| 133 | +| `circom/inputs/rsa_verify_4096/default.json` | Test inputs for RSA-4096 | |
| 134 | +| `circom/src/generate_rsa_inputs.ts` | Input generator (RSA key pair + PKCS#1 v1.5 signature) | |
| 135 | +| `ecdsa-spartan2/src/circuits/rsa_verify_circuit.rs` | Rust `SpartanCircuit<E>` implementation | |
| 136 | +| `ecdsa-spartan2/Cargo.toml` | Feature flags (`rsa-circuits`) | |
| 137 | +| `ecdsa-spartan2/src/main.rs` | CLI commands for `rsa2048` / `rsa4096` | |
0 commit comments