Skip to content
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The purpose of this library is to provide a gas-efficient implementation of secp

Allowing transactions to be signed and validated using the `secp256r1` curve enables great end-user experience in the form of signing transactions using the biometrics on the user's device.

On top of great UX, this also have the added benefit of moving away from seed phrases to better security in modern mobile-devices / laptops, and superior security when the user's device supports a dedicated security chip (e.g. Android's Secure Element and Apple's Secure Enclave etc.).
On top of great UX, this also has the added benefit of moving away from seed phrases to better security in modern mobile-devices / laptops, and superior security when the user's device supports a dedicated security chip (e.g. Android's Secure Element and Apple's Secure Enclave etc.).

Since `secp256r1` ECDSA is not native to Cairo (i.e. it does not have a dedicated Builtin), the Gas cost incurred in validation of the signature is very high. In this library we aim to optimize that as much as possible.

Expand All @@ -16,7 +16,7 @@ We've adapted `cairo-lang`'s `secp256k1` ECDSA validation implementation. We had
handling of `BigInt3` limbs as `secp256r1`'s operations only very tightly fit into the `BigInt3` representation.
Also, `cairo-lang` uses the public-key recovery algorithm for ECDSA validation while this library uses straight-forward validation since in a secure-hardware signing setup, we don't have `v` which is necessary for correct public-key recovery.

Some hints were modified to accomodate the above, these will be introduced as part of `cairo-lang` version `0.11.0`. To use this prior to that you will have to apply the patch at `cairo-lang-secp256r1-hints.patch` on your python virtual env.
Some hints were modified to accommodate the above, these will be introduced as part of `cairo-lang` version `0.11.0`. To use this prior to that you will have to apply the patch at `cairo-lang-secp256r1-hints.patch` on your python virtual env.

## API

Expand Down
52 changes: 35 additions & 17 deletions cairo-lang-secp256r1-hints.patch
Original file line number Diff line number Diff line change
@@ -1,34 +1,57 @@
diff --git a/starkware/cairo/common/cairo_secp/secp_utils.py b/starkware/cairo/common/cairo_secp/secp_utils.py
diff --git a/src/starkware/cairo/common/cairo_secp/secp_utils.py b/src/starkware/cairo/common/cairo_secp/secp_utils.py
index 9d0b005..f434372 100644
--- a/starkware/cairo/common/cairo_secp/secp_utils.py
+++ b/starkware/cairo/common/cairo_secp/secp_utils.py
--- a/src/starkware/cairo/common/cairo_secp/secp_utils.py
+++ b/src/starkware/cairo/common/cairo_secp/secp_utils.py
@@ -3,11 +3,16 @@ from typing import List
from starkware.cairo.common.math_utils import as_int

BASE = 2**86
+
SECP_P = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
ALPHA = 0
BETA = 7

+SECP256R1_P = 2**256 - 2**224 + 2**192 + 2**96 - 1
+SECP256R1_N = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
+SECP256R1_ALPHA = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
+SECP256R1_BETA = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b

def split(num: int) -> List[int]:
"""
diff --git a/starkware/starknet/security/whitelists/cairo_secp.json b/starkware/starknet/security/whitelists/cairo_secp.json
index ac77942..b45200f 100644
--- a/starkware/starknet/security/whitelists/cairo_secp.json
+++ b/starkware/starknet/security/whitelists/cairo_secp.json
@@ -1,5 +1,127 @@
diff --git a/src/starkware/starknet/security/whitelists/cairo_secp.json b/src/starkware/starknet/security/whitelists/cairo_secp.json
index ac77942..6e6c9c9 100644
--- a/src/starkware/starknet/security/whitelists/cairo_secp.json
+++ b/src/starkware/starknet/security/whitelists/cairo_secp.json
@@ -1,5 +1,145 @@
{
"allowed_reference_expressions_for_hint": [
+ {
+ "allowed_expressions": [],
+ "hint_lines": [
+ "ids.quad_bit = (",
+ " 8 * ((ids.scalar_v >> ids.m) & 1)",
+ " + 4 * ((ids.scalar_u >> ids.m) & 1)",
+ " + 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)",
+ " + ((ids.scalar_u >> (ids.m - 1)) & 1)",
+ ")"
+ ]
+ },
+ {
+ "allowed_expressions": [],
+ "hint_lines": [
+ "ids.len_hi = max(ids.scalar_u.d2.bit_length(), ids.scalar_v.d2.bit_length())-1"
+ ]
+ },
+ {
+ "allowed_expressions": [],
+ "hint_lines": [
+ "ids.dibit = ((ids.scalar_u >> ids.m) & 1) + 2 * ((ids.scalar_v >> ids.m) & 1)"
+ ]
+ },
+ {
+ "allowed_expressions": [],
+ "hint_lines": [
+ "from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1_P as SECP_P"
+ ]
+ },
Expand Down Expand Up @@ -128,12 +151,7 @@ index ac77942..b45200f 100644
+ "",
+ "q, r = divmod(pack(ids.val, PRIME), SECP_P)",
+ "assert r == 0, f\"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}.\"",
+ "if q >= 0:",
+ " ids.q = q % PRIME",
+ " ids.q_sign = 1",
+ "else:",
+ " ids.q = (0-q) % PRIME",
+ " ids.q_sign = -1 % PRIME"
+ "ids.q = q % PRIME"
+ ]
+ },
+ {
Expand Down
11 changes: 6 additions & 5 deletions src/secp256r1/constants.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
from starkware.cairo.common.cairo_secp.bigint import (
BigInt3,
)
from starkware.cairo.common.cairo_secp.ec import EcPoint
// Basic definitions for the secp25r1 elliptic curve.
// The curve is given by the equation:
// y^2 = x^3 + ax + b
Expand All @@ -16,6 +12,11 @@ const SECP_REM = 2**224 - 2**192 - 2**96 + 1;

const BASE = 2 ** 86;

// SECP_REM = 2**224 - 2**192 - 2**96 + 1
const SECP_REM0 = 0x1;
const SECP_REM1 = 0x3ffffffffffffffffffc00;
const SECP_REM2 = 0xfffffffefffff;

// P = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
const P0 = 0x3fffffffffffffffffffff;
const P1 = 0x3ff;
Expand Down Expand Up @@ -54,4 +55,4 @@ const s0 = 2**56;

const r2 = 2**54 - 2**22;
const r1 = -2**12;
const r0 = 4;
const r0 = 4;
205 changes: 205 additions & 0 deletions src/secp256r1/ec_mulmuladd.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
//*************************************************************************************/
///* Copyright (C) 2022 - Renaud Dubois - This file is part of Cairo_musig2 project */
///* License: This software is licensed under a dual BSD and GPL v2 license. */
///* See LICENSE file at the root folder of the project. */
///* FILE: multipoint.cairo */
///* */
///* */
///* DESCRIPTION: optimization of dual base multiplication */
///* the algorithm combines the so called Shamir's trick with Windowing method */
//*************************************************************************************/
from starkware.cairo.common.cairo_secp.bigint import BigInt3

from src.secp256r1.ec import EcPoint, ec_add, ec_mul, ec_double


//Structure storing all aP+b.Q for (a,b) in [0..3]x[0..3]
struct Window {
G: EcPoint,
Q: EcPoint,
W3: EcPoint,
W4: EcPoint,
W5: EcPoint,
W6: EcPoint,
W7: EcPoint,
W8: EcPoint,
W9: EcPoint,
W10: EcPoint,
W11: EcPoint,
W12: EcPoint,
W13: EcPoint,
W14: EcPoint,
W15: EcPoint,
}


//https://crypto.stackexchange.com/questions/99975/strauss-shamir-trick-on-ec-multiplication-by-scalar,
//* Internal call for recursion of point multiplication via Shamir's trick */
func ec_mulmuladd_inner{range_check_ptr}(
R: EcPoint, G: EcPoint, Q: EcPoint, H: EcPoint,
scalar_u: felt, scalar_v: felt, m: felt
) -> (res: EcPoint){
alloc_locals;

// this means if m=-1, beware if felt definition changes
if (m == -1) {
return(res=R);
}

let (double_point) = ec_double(R);

let mm1 = m-1;
local dibit;
//extract MSB values of both exponents
%{ ids.dibit = ((ids.scalar_u >> ids.m) & 1) + 2 * ((ids.scalar_v >> ids.m) & 1) %}

//set R:=R+R
if (dibit==0){
let (res)= ec_mulmuladd_inner(double_point,G,Q,H,scalar_u,scalar_v,mm1);
return(res=res);
}
//if ui=1 and vi=0, set R:=R+G
if (dibit==1){
let (res10)=ec_add(double_point,G);
let (res)= ec_mulmuladd_inner(res10,G,Q,H,scalar_u,scalar_v,mm1);
return(res=res);
}
//(else) if ui=0 and vi=1, set R:=R+Q
if (dibit==2){
let (res01)=ec_add(double_point,Q);
let (res)= ec_mulmuladd_inner(res01,G,Q,H,scalar_u,scalar_v,mm1);
return(res=res);
}
//(else) if ui=1 and vi=1, set R:=R+Q
if (dibit==3){
let (res11)=ec_add(double_point,H);
let (res)= ec_mulmuladd_inner(res11,G,Q,H,scalar_u,scalar_v,mm1);
return(res=res);
}

//you shall never end up here
return(res=R);
}



//https://crypto.stackexchange.com/questions/99975/strauss-shamir-trick-on-ec-multiplication-by-scalar,
//* Internal call for recursion of point multiplication via Shamir's trick+Windowed method */
func ec_mulmuladd_W_inner{range_check_ptr}(
R: EcPoint, Prec:Window,
scalar_u: felt, scalar_v: felt, m: felt
) -> (res: EcPoint){
alloc_locals;
let mm2 = m-2;

//(8*v1 4*u1+ 2*v0 + u0), where (u1,u0) represents two bit at index m of scalar u, (resp for v)
local quad_bit;

if (m == -1) {
return (res=R);
}

let (double_point) = ec_double(R);

//still have to make the last addition over 1 bit (initial length was odd)
if(m == 0){
let (res)=ec_mulmuladd_inner(R, Prec.G, Prec.Q, Prec.W3, scalar_u, scalar_v, m);
return (res=res);
}

let (quadruple_point) = ec_double(double_point);

//compute quadruple (8*v1 4*u1+ 2*v0 + u0)
%{
ids.quad_bit = (
8 * ((ids.scalar_v >> ids.m) & 1)
+ 4 * ((ids.scalar_u >> ids.m) & 1)
+ 2 * ((ids.scalar_v >> (ids.m - 1)) & 1)
+ ((ids.scalar_u >> (ids.m - 1)) & 1)
)
%}

if (quad_bit == 0) {
let (res) = ec_mulmuladd_W_inner(quadruple_point, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 1) {
let (ecTemp) = ec_add(quadruple_point,Prec.G);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 2) {
let (ecTemp) = ec_add(quadruple_point,Prec.Q);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}

if (quad_bit == 3) {
let (ecTemp) = ec_add(quadruple_point,Prec.W3);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 4) {
let (ecTemp) = ec_add(quadruple_point,Prec.W4);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 5) {
let (ecTemp) = ec_add(quadruple_point,Prec.W5);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 6) {
let (ecTemp) = ec_add(quadruple_point,Prec.W6);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 7) {
let (ecTemp) = ec_add(quadruple_point,Prec.W7);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 8) {
let (ecTemp) = ec_add(quadruple_point,Prec.W8);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 9) {
let (ecTemp) = ec_add(quadruple_point,Prec.W9);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 10) {
let (ecTemp) = ec_add(quadruple_point,Prec.W10);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 11) {
let (ecTemp) = ec_add(quadruple_point,Prec.W11);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 12) {
let (ecTemp) = ec_add(quadruple_point,Prec.W12);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 13) {
let (ecTemp) = ec_add(quadruple_point,Prec.W13);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 14) {
let (ecTemp) = ec_add(quadruple_point,Prec.W14);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}
if (quad_bit == 15) {
let (ecTemp) = ec_add(quadruple_point,Prec.W15);
let (res) = ec_mulmuladd_W_inner(ecTemp, Prec, scalar_u, scalar_v, mm2);
return (res=res);
}

//shall not be reach
return (res=R);
}
Loading