|
| 1 | +%builtins range_check |
| 2 | + |
| 3 | +// Source: https://github.com/rdubois-crypto/efficient-secp256r1/blob/4b74807c5e91f1ed4cb00a1c973be05c63986e61/src/secp256r1/ec.cairo |
| 4 | +from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3 |
| 5 | +from starkware.cairo.common.cairo_secp.ec import EcPoint |
| 6 | + |
| 7 | +// src.secp256r1.constants |
| 8 | +// SECP_REM is defined by the equation: |
| 9 | +// secp256r1_prime = 2 ** 256 - SECP_REM. |
| 10 | +const SECP_REM = 2 ** 224 - 2 ** 192 - 2 ** 96 + 1; |
| 11 | + |
| 12 | +const BASE = 2 ** 86; |
| 13 | + |
| 14 | +// A = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc |
| 15 | +const A0 = 0x3ffffffffffffffffffffc; |
| 16 | +const A1 = 0x3ff; |
| 17 | +const A2 = 0xffffffff0000000100000; |
| 18 | + |
| 19 | +// Constants for unreduced_mul/sqr |
| 20 | +const s2 = (-(2 ** 76)) - 2 ** 12; |
| 21 | +const s1 = (-(2 ** 66)) + 4; |
| 22 | +const s0 = 2 ** 56; |
| 23 | + |
| 24 | +const r2 = 2 ** 54 - 2 ** 22; |
| 25 | +const r1 = -(2 ** 12); |
| 26 | +const r0 = 4; |
| 27 | + |
| 28 | +// src.secp256r1.field |
| 29 | +// Adapt from starkware.cairo.common.math's assert_250_bit |
| 30 | +func assert_165_bit{range_check_ptr}(value) { |
| 31 | + const UPPER_BOUND = 2 ** 165; |
| 32 | + const SHIFT = 2 ** 128; |
| 33 | + const HIGH_BOUND = UPPER_BOUND / SHIFT; |
| 34 | + |
| 35 | + let low = [range_check_ptr]; |
| 36 | + let high = [range_check_ptr + 1]; |
| 37 | + |
| 38 | + %{ |
| 39 | + from starkware.cairo.common.math_utils import as_int |
| 40 | +
|
| 41 | + # Correctness check. |
| 42 | + value = as_int(ids.value, PRIME) % PRIME |
| 43 | + assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**250).' |
| 44 | +
|
| 45 | + # Calculation for the assertion. |
| 46 | + ids.high, ids.low = divmod(ids.value, ids.SHIFT) |
| 47 | + %} |
| 48 | + |
| 49 | + assert [range_check_ptr + 2] = HIGH_BOUND - 1 - high; |
| 50 | + |
| 51 | + assert value = high * SHIFT + low; |
| 52 | + |
| 53 | + let range_check_ptr = range_check_ptr + 3; |
| 54 | + return (); |
| 55 | +} |
| 56 | + |
| 57 | +// src.secp256r1.field |
| 58 | +// Computes the multiplication of two big integers, given in BigInt3 representation, modulo the |
| 59 | +// secp256r1 prime. |
| 60 | +// |
| 61 | +// Arguments: |
| 62 | +// x, y - the two BigInt3 to operate on. |
| 63 | +// |
| 64 | +// Returns: |
| 65 | +// x * y in an UnreducedBigInt3 representation (the returned limbs may be above 3 * BASE). |
| 66 | +// |
| 67 | +// This means that if unreduced_mul is called on the result of nondet_bigint3, or the difference |
| 68 | +// between two such results, we have: |
| 69 | +// Soundness guarantee: the limbs are in the range (). |
| 70 | +// Completeness guarantee: the limbs are in the range (). |
| 71 | +func unreduced_mul(a: BigInt3, b: BigInt3) -> (res_low: UnreducedBigInt3) { |
| 72 | + tempvar twice_d2 = a.d2 * b.d2; |
| 73 | + tempvar d1d2 = a.d2 * b.d1 + a.d1 * b.d2; |
| 74 | + return ( |
| 75 | + UnreducedBigInt3( |
| 76 | + d0=a.d0 * b.d0 + s0 * twice_d2 + r0 * d1d2, |
| 77 | + d1=a.d1 * b.d0 + a.d0 * b.d1 + s1 * twice_d2 + r1 * d1d2, |
| 78 | + d2=a.d2 * b.d0 + a.d1 * b.d1 + a.d0 * b.d2 + s2 * twice_d2 + r2 * d1d2, |
| 79 | + ), |
| 80 | + ); |
| 81 | +} |
| 82 | + |
| 83 | +// src.secp256r1.field |
| 84 | +// Computes the square of a big integer, given in BigInt3 representation, modulo the |
| 85 | +// secp256r1 prime. |
| 86 | +// |
| 87 | +// Has the same guarantees as in unreduced_mul(a, a). |
| 88 | +func unreduced_sqr(a: BigInt3) -> (res_low: UnreducedBigInt3) { |
| 89 | + tempvar twice_d2 = a.d2 * a.d2; |
| 90 | + tempvar twice_d1d2 = a.d2 * a.d1 + a.d1 * a.d2; |
| 91 | + tempvar d1d0 = a.d1 * a.d0; |
| 92 | + return ( |
| 93 | + UnreducedBigInt3( |
| 94 | + d0=a.d0 * a.d0 + s0 * twice_d2 + r0 * twice_d1d2, |
| 95 | + d1=d1d0 + d1d0 + s1 * twice_d2 + r1 * twice_d1d2, |
| 96 | + d2=a.d2 * a.d0 + a.d1 * a.d1 + a.d0 * a.d2 + s2 * twice_d2 + r2 * twice_d1d2, |
| 97 | + ), |
| 98 | + ); |
| 99 | +} |
| 100 | + |
| 101 | +// src.secp256r1.field |
| 102 | +// Verifies that the given unreduced value is equal to zero modulo the secp256r1 prime. |
| 103 | +// |
| 104 | +// Completeness assumption: val's limbs are in the range (-2**210.99, 2**210.99). |
| 105 | +// Soundness assumption: val's limbs are in the range (-2**250, 2**250). |
| 106 | +func verify_zero{range_check_ptr}(val: UnreducedBigInt3) { |
| 107 | + alloc_locals; |
| 108 | + local q; |
| 109 | + // local q_sign; |
| 110 | + let q_sign = 1; |
| 111 | + // original: |
| 112 | + // %{ from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1_P as SECP_P %} |
| 113 | + // %{ |
| 114 | + // from starkware.cairo.common.cairo_secp.secp_utils import pack |
| 115 | + |
| 116 | + // q, r = divmod(pack(ids.val, PRIME), SECP_P) |
| 117 | + // assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." |
| 118 | + // if q >= 0: |
| 119 | + // ids.q = q % PRIME |
| 120 | + // ids.q_sign = 1 |
| 121 | + // else: |
| 122 | + // ids.q = (0-q) % PRIME |
| 123 | + // ids.q_sign = -1 % PRIME |
| 124 | + // %} |
| 125 | + %{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P %} |
| 126 | + %{ |
| 127 | + from starkware.cairo.common.cairo_secp.secp_utils import pack |
| 128 | +
|
| 129 | + q, r = divmod(pack(ids.val, PRIME), SECP_P) |
| 130 | + assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." |
| 131 | + ids.q = q % PRIME |
| 132 | + %} |
| 133 | + // assert_250_bit(q); // 256K steps |
| 134 | + // assert_le_felt(q, 2**165); // 275K steps |
| 135 | + assert_165_bit(q); |
| 136 | + assert q_sign * (val.d2 + val.d1 / BASE + val.d0 / BASE ** 2) = q * ( |
| 137 | + (BASE / 4) - SECP_REM / BASE ** 2 |
| 138 | + ); |
| 139 | + // Multiply by BASE**2 both sides: |
| 140 | + // (q_sign) * val = q * (BASE**3 / 4 - SECP_REM) |
| 141 | + // = q * (2**256 - SECP_REM) = q * secp256r1_prime = 0 mod secp256r1_prime |
| 142 | + return (); |
| 143 | +} |
| 144 | +
|
| 145 | +// Computes the slope of the elliptic curve at a given point. |
| 146 | +// The slope is used to compute point + point. |
| 147 | +// |
| 148 | +// Arguments: |
| 149 | +// point - the point to operate on. |
| 150 | +// |
| 151 | +// Returns: |
| 152 | +// slope - the slope of the curve at point, in BigInt3 representation. |
| 153 | +// |
| 154 | +// Assumption: point != 0. |
| 155 | +func compute_doubling_slope{range_check_ptr}(point: EcPoint) -> (slope: BigInt3) { |
| 156 | + // Note that y cannot be zero: assume that it is, then point = -point, so 2 * point = 0, which |
| 157 | + // contradicts the fact that the size of the curve is odd. |
| 158 | + // originals: |
| 159 | + // %{ from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1_P as SECP_P %} |
| 160 | + // %{ from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1_ALPHA as ALPHA %} |
| 161 | + %{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P %} |
| 162 | + %{ from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA as ALPHA %} |
| 163 | + %{ |
| 164 | + from starkware.cairo.common.cairo_secp.secp_utils import pack |
| 165 | + from starkware.python.math_utils import ec_double_slope |
| 166 | +
|
| 167 | + # Compute the slope. |
| 168 | + x = pack(ids.point.x, PRIME) |
| 169 | + y = pack(ids.point.y, PRIME) |
| 170 | + value = slope = ec_double_slope(point=(x, y), alpha=ALPHA, p=SECP_P) |
| 171 | + %} |
| 172 | + let (slope: BigInt3) = nondet_bigint3(); |
| 173 | +
|
| 174 | + let (x_sqr: UnreducedBigInt3) = unreduced_sqr(point.x); |
| 175 | + let (slope_y: UnreducedBigInt3) = unreduced_mul(slope, point.y); |
| 176 | + verify_zero( |
| 177 | + UnreducedBigInt3( |
| 178 | + d0=3 * x_sqr.d0 + A0 - 2 * slope_y.d0, |
| 179 | + d1=3 * x_sqr.d1 + A1 - 2 * slope_y.d1, |
| 180 | + d2=3 * x_sqr.d2 + A2 - 2 * slope_y.d2, |
| 181 | + ), |
| 182 | + ); |
| 183 | +
|
| 184 | + return (slope=slope); |
| 185 | +} |
| 186 | +
|
| 187 | +func test_doubling_slope{range_check_ptr}() { |
| 188 | + let point = EcPoint(BigInt3(614323, 5456867, 101208), BigInt3(773712524, 77371252, 5298795)); |
| 189 | +
|
| 190 | + let (slope) = compute_doubling_slope(point); |
| 191 | +
|
| 192 | + assert slope = BigInt3( |
| 193 | + 64081873649130491683833713, 34843994309543177837008178, 16548672716077616016846383 |
| 194 | + ); |
| 195 | +
|
| 196 | + let point = EcPoint( |
| 197 | + BigInt3(51215, 36848548548458, 634734734), BigInt3(26362, 263724839599, 901297012) |
| 198 | + ); |
| 199 | +
|
| 200 | + let (slope) = compute_doubling_slope(point); |
| 201 | +
|
| 202 | + assert slope = BigInt3( |
| 203 | + 71848883893335852660776740, 75644451964360469099209675, 547087410329256463669633 |
| 204 | + ); |
| 205 | +
|
| 206 | + return (); |
| 207 | +} |
| 208 | +
|
| 209 | +func main{range_check_ptr}() { |
| 210 | + test_doubling_slope(); |
| 211 | + return (); |
| 212 | +} |
0 commit comments