├── .gitattributes ├── .gitignore ├── README.md └── contracts ├── BLSValidators.sol └── BN256G2.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | build 4 | *.pyc 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLS signature verification 2 | 3 | **Warning: This is toy POC and an academic review is required.** 4 | 5 | Validate multiple signatures on Ethereum chain using BLS. 6 | 7 | ### Rogue key attack 8 | 9 | Proofs-of-possession(POP) at registration to address the rogue public key attack [see here](https://eprint.iacr.org/2007/264.pdf) 10 | 11 | - This code does not check for POP. 12 | - We can test POP at validator/user registration time. We can get any data signed and verify using pubkey. However, we will add this later. 13 | 14 | ## Curve points 15 | 16 | - We are currently using [py_ecc](https://github.com/ethereum/py_ecc) [py_ecc/fork](https://github.com/0xAshish/py_ecc) for testing/poc purpose 17 | - In the future we will use rust [code](https://github.com/zkcrypto/pairing) for faster results 18 | 19 | [BN256G2]() is used for solidity G2 point operations and pre-compiles ECMUL, ECADD and ECPARING 20 | 21 | - Test data/points generated using [BLSSmall.py](https://github.com/0xAshish/py_ecc/blob/master/tests/BLSsmall.py) 22 | 23 | ### G1 points 24 | 25 | Generator for curve over FQ 26 | 27 | ``` 28 | G1 = (FQ(1), FQ(2)) 29 | ``` 30 | 31 | ### G2 points 32 | 33 | Generator for twisted curve over FQ2 34 | 35 | ``` 36 | G2 = ( 37 | FQ2([11559732032986387107991004021392285783925812861821192530917403151452391805634, 38 | 10857046999023057135944570762232829481370756359578518086990519993285655852781]), 39 | FQ2([4082367875863433681332203403145435568316851327593401208105741076214120093531, 40 | 8495653923123431417604973247489272438418190587263600148770280649306958101930])) 41 | ) 42 | ``` 43 | 44 | ### Private key `sk` 45 | 46 | Securely generated number 47 | 48 | ### Public keys `pk` 49 | 50 | Public keys are G1 points on the curve. 51 | `pk = mul(G1, sk)` 52 | 53 | ### `hashToG2` 54 | 55 | - Message m = data to be signed 56 | - h = Hash of Message m or some numeric deterministic representation 57 | - H is Point on G2 58 | - Currently, for toy version we are using `hashToG2` => `mul(G2, h)` 59 | - Some better methods for `hashToG1/G2` [`try-and-increment`, [hashingToBNCurves](https://www.di.ens.fr/~fouque/pub/latincrypt12.pdf)] can be used for real use cases. 60 | 61 | ### BLS signing `sig` 62 | 63 | - sig is a valid G2 point 64 | - `sig = mul(H, sk)` 65 | 66 | ## Aggregation operations 67 | 68 | ### `aggregate pubkeys` 69 | 70 | ``` 71 | aggPubkey 72 | for each pubkey in pubkeys: 73 | aggPubkey = add(aggPubkey, pubkey) 74 | 75 | ``` 76 | 77 | where `add` is the elliptic curve addition operation over the G1 curve and the empty aggSigs is the G1 point at infinity. 78 | 79 | ### `Aggregate signatures` 80 | 81 | ``` 82 | aggSigs 83 | for each sig in sigs: 84 | aggSigs = add(aggSig, sig) 85 | 86 | ``` 87 | 88 | where `add` is the elliptic curve addition operation over the G2 curve and the empty aggSigs is the G2 point at infinity. 89 | 90 | ## Signature verification 91 | 92 | In the following, `e` is the pairing function with the following coordinates (see [here](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381#g1)): 93 | 94 | ### `BLS Sig verify` 95 | 96 | aggPubkey = `aggregated pubkeys` 97 | aggSig is aggregation of n sigs from n privkeys/validators 98 | 99 | - Each `pubkey` in `pubkeys` is a valid G1 point. 100 | - `signature` is a valid G2 point. 101 | - Verify pairing `e(add(pubkeys[0]...[n]), hashtoG2(message)) == e(G1, aggSig)`. 102 | 103 | where `add` is the elliptic curve addition operation over the G1 curve and the empty aggSigs is the G1 point at infinity. 104 | 105 | ### Try-and-increment 106 | 107 | ``` 108 | def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]: 109 | # Initial candidate x coordinate 110 | x_re = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x01'), 'big') 111 | x_im = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x02'), 'big') 112 | x_coordinate = Fq2([x_re, x_im]) # x = x_re + i * x_im 113 | 114 | # Test candidate y coordinates until a one is found 115 | while 1: 116 | y_coordinate_squared = x_coordinate ** 3 + Fq2([4, 4]) # The curve is y^2 = x^3 + 4(i + 1) 117 | y_coordinate = modular_squareroot(y_coordinate_squared) 118 | if y_coordinate is not None: # Check if quadratic residue found 119 | return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor) 120 | x_coordinate += Fq2([1, 0]) # Add 1 and try again 121 | ``` 122 | 123 | Above algorithm from [ETH2.0](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/bls_signature.md#hash_to_g2) can be used for `hashToG2`. 124 | 125 | #### References 126 | 127 | 1. https://www.di.ens.fr/~fouque/pub/latincrypt12.pdf 128 | 2. https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html 129 | 3. https://crypto.stanford.edu/pbc/thesis.pdf 130 | 4. http://www.craigcostello.com.au/pairings/PairingsForBeginners.pdf 131 | 5. https://eprint.iacr.org/2007/264.pdf 132 | 6. https://eprint.iacr.org/2008/530.pdf 133 | -------------------------------------------------------------------------------- /contracts/BLSValidators.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import { BN256G2 } from "./BN256G2.sol"; 4 | 5 | /* 6 | Toy working POC on BLS Sig and aggregation in Ethereum. 7 | 8 | Signatures are generated using https://github.com/0xAshish/py_ecc/blob/master/tests/BLSsmall.py 9 | 10 | Code is based on https://github.com/jstoxrocky/zksnarks_example 11 | 12 | */ 13 | 14 | contract BLSValidators { 15 | 16 | struct G1Point { 17 | uint X; 18 | uint Y; 19 | } 20 | 21 | // Encoding of field elements is: X[0] * z + X[1] 22 | struct G2Point { 23 | uint[2] X; 24 | uint[2] Y; 25 | } 26 | 27 | struct Validator { 28 | address user; 29 | uint256 amount; 30 | G1Point pubkey; 31 | } 32 | 33 | uint256 public vCount = 0; 34 | mapping (uint256 => Validator) public validators; 35 | 36 | event newValidator(uint256 indexed validatorId); 37 | 38 | function addValidator(uint256 pkX, uint256 pkY, uint256 amount) public { 39 | vCount++; 40 | validators[vCount] = Validator(msg.sender, amount, G1Point(pkX, pkY)); 41 | emit newValidator(vCount); 42 | } 43 | 44 | function addValidatorTest(uint256 amount, uint256 _pk,uint256 n) public { 45 | for(uint256 i = 0 ;i < n; i++) { 46 | vCount++; 47 | // Temporary 48 | G1Point memory pk = mul(P1(), _pk+i); 49 | validators[vCount] = Validator(msg.sender,amount, pk); 50 | } 51 | } 52 | 53 | function getValidatorDetails(uint256 id) public view 54 | returns( 55 | address, 56 | uint256, 57 | uint256, 58 | uint256 59 | ) { 60 | return (validators[id].user, validators[id].amount, validators[id].pubkey.X, validators[id].pubkey.Y); 61 | } 62 | 63 | function checkSigAGG(uint256 bitmask, uint256 sigs0, uint256 sigs1, uint256 sigs2, uint256 sigs3, uint256 message) public returns(bool) { 64 | G1Point memory pubkey; 65 | for(uint256 i = 0; i < vCount; i++) { 66 | // if((bitmask >> i) & 1 > 0) { 67 | Validator v = validators[i+1]; 68 | pubkey = add(pubkey, v.pubkey); 69 | // } 70 | } 71 | 72 | G2Point memory H = hashToG2(message); 73 | G2Point memory signature = G2Point([sigs1,sigs0],[sigs3,sigs2]); 74 | return pairing2(P1(), H, negate(pubkey), signature); 75 | } 76 | 77 | 78 | function testCheckSigAGG() public { 79 | 80 | G1Point memory pubkey = G1Point( 81 | 17380323886581056473092238415087178747833394266216426706118377188344506669132, 82 | 8264330258127714892906603723635360533223500611780692134587255146148491007336); 83 | // hash on G2 point 84 | G2Point memory H = G2Point( 85 | [7806540115951598708068323537226325143489341620121102987168061034219723055482, 86 | 16102053849180588443131133900438094849149715436625045469236991987039241848240], 87 | [6718946360417026759307173704450430250787528919693688413464546568151449945362, 88 | 15085587210032391178752839157819905008772577581989468040951987143794090031385]); 89 | 90 | G2Point memory signature = G2Point( 91 | [20510297253563043906240734487189027213933976667621835319448331165769997484335, 92 | 17039283792713629953217756598150981109636679343767085841835508695942368202923], 93 | [1985362097212581787757922254110217851026070065076532109495179805548055991837, 94 | 7135647869386222135872517926452623520408611489591663660104271578165118400268]); 95 | 96 | require(pairing2(P1(), H, negate(pubkey), signature), "Something went wrong"); 97 | } 98 | 99 | /// @return the generator of G1 100 | function P1() internal returns (G1Point) { 101 | return G1Point(1, 2); 102 | } 103 | 104 | /// @return the generator of G2 105 | function P2() internal returns (G2Point) { 106 | return G2Point( 107 | [11559732032986387107991004021392285783925812861821192530917403151452391805634, 108 | 10857046999023057135944570762232829481370756359578518086990519993285655852781], 109 | 110 | [4082367875863433681332203403145435568316851327593401208105741076214120093531, 111 | 8495653923123431417604973247489272438418190587263600148770280649306958101930] 112 | ); 113 | } 114 | 115 | /// @return the result of computing the pairing check 116 | /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 117 | /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should 118 | /// return true. 119 | function pairing(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) { 120 | require(p1.length == p2.length); 121 | uint elements = p1.length; 122 | uint inputSize = elements * 6; 123 | uint[] memory input = new uint[](inputSize); 124 | 125 | for (uint i = 0; i < elements; i++) 126 | { 127 | input[i * 6 + 0] = p1[i].X; 128 | input[i * 6 + 1] = p1[i].Y; 129 | input[i * 6 + 2] = p2[i].X[0]; 130 | input[i * 6 + 3] = p2[i].X[1]; 131 | input[i * 6 + 4] = p2[i].Y[0]; 132 | input[i * 6 + 5] = p2[i].Y[1]; 133 | } 134 | 135 | uint[1] memory out; 136 | bool success; 137 | 138 | assembly { 139 | success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) 140 | // Use "invalid" to make gas estimation work 141 | switch success case 0 {invalid} 142 | } 143 | require(success); 144 | return out[0] != 0; 145 | } 146 | 147 | /// Convenience method for a pairing check for two pairs. 148 | function pairing2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) internal returns (bool) { 149 | G1Point[] memory p1 = new G1Point[](2); 150 | G2Point[] memory p2 = new G2Point[](2); 151 | p1[0] = a1; 152 | p1[1] = b1; 153 | p2[0] = a2; 154 | p2[1] = b2; 155 | return pairing(p1, p2); 156 | } 157 | 158 | function hashToG1(uint256 h) internal returns (G1Point) { 159 | return mul(P1(), h); 160 | } 161 | 162 | function hashToG2(uint256 h) internal returns (G2Point memory) { 163 | G2Point memory p2 = P2(); 164 | uint256 x1; 165 | uint256 x2; 166 | uint256 y1; 167 | uint256 y2; 168 | (x1,x2,y1,y2) = BN256G2.ECTwistMul(h, p2.X[1], p2.X[0], p2.Y[1], p2.Y[0]); 169 | return G2Point([x2,x1],[y2,y1]); 170 | } 171 | 172 | function modPow(uint256 base, uint256 exponent, uint256 modulus) internal returns (uint256) { 173 | uint256[6] memory input = [32, 32, 32, base, exponent, modulus]; 174 | uint256[1] memory result; 175 | assembly { 176 | if iszero(call(not(0), 0x05, 0, input, 0xc0, result, 0x20)) { 177 | revert(0, 0) 178 | } 179 | } 180 | return result[0]; 181 | } 182 | 183 | /// @return the negation of p, i.e. p.add(p.negate()) should be zero. 184 | function negate(G1Point p) internal returns (G1Point) { 185 | // The prime q in the base field F_q for G1 186 | uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; 187 | if (p.X == 0 && p.Y == 0) 188 | return G1Point(0, 0); 189 | return G1Point(p.X, q - (p.Y % q)); 190 | } 191 | 192 | /// @return the sum of two points of G1 193 | function add(G1Point p1, G1Point p2) internal returns (G1Point r) { 194 | uint[4] memory input; 195 | input[0] = p1.X; 196 | input[1] = p1.Y; 197 | input[2] = p2.X; 198 | input[3] = p2.Y; 199 | bool success; 200 | assembly { 201 | success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60) 202 | // Use "invalid" to make gas estimation work 203 | switch success case 0 {invalid} 204 | } 205 | require(success); 206 | } 207 | 208 | /// @return the product of a point on G1 and a scalar, i.e. 209 | /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. 210 | function mul(G1Point p, uint s) internal returns (G1Point r) { 211 | uint[3] memory input; 212 | input[0] = p.X; 213 | input[1] = p.Y; 214 | input[2] = s; 215 | bool success; 216 | assembly { 217 | success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60) 218 | // Use "invalid" to make gas estimation work 219 | switch success case 0 {invalid} 220 | } 221 | require(success); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /contracts/BN256G2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title Elliptic curve operations on twist points for alt_bn128 5 | * @author Mustafa Al-Bassam (mus@musalbas.com) 6 | * https://github.com/musalbas/solidity-BN256G2 7 | */ 8 | 9 | library BN256G2 { 10 | uint256 internal constant FIELD_MODULUS = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; 11 | uint256 internal constant TWISTBX = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; 12 | uint256 internal constant TWISTBY = 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2; 13 | uint internal constant PTXX = 0; 14 | uint internal constant PTXY = 1; 15 | uint internal constant PTYX = 2; 16 | uint internal constant PTYY = 3; 17 | uint internal constant PTZX = 4; 18 | uint internal constant PTZY = 5; 19 | 20 | /** 21 | * @notice Add two twist points 22 | * @param pt1xx Coefficient 1 of x on point 1 23 | * @param pt1xy Coefficient 2 of x on point 1 24 | * @param pt1yx Coefficient 1 of y on point 1 25 | * @param pt1yy Coefficient 2 of y on point 1 26 | * @param pt2xx Coefficient 1 of x on point 2 27 | * @param pt2xy Coefficient 2 of x on point 2 28 | * @param pt2yx Coefficient 1 of y on point 2 29 | * @param pt2yy Coefficient 2 of y on point 2 30 | * @return (pt3xx, pt3xy, pt3yx, pt3yy) 31 | */ 32 | function ECTwistAdd( 33 | uint256 pt1xx, uint256 pt1xy, 34 | uint256 pt1yx, uint256 pt1yy, 35 | uint256 pt2xx, uint256 pt2xy, 36 | uint256 pt2yx, uint256 pt2yy 37 | ) public pure returns ( 38 | uint256, uint256, 39 | uint256, uint256 40 | ) { 41 | if ( 42 | pt1xx == 0 && pt1xy == 0 && 43 | pt1yx == 0 && pt1yy == 0 44 | ) { 45 | if (!( 46 | pt2xx == 0 && pt2xy == 0 && 47 | pt2yx == 0 && pt2yy == 0 48 | )) { 49 | assert(_isOnCurve( 50 | pt2xx, pt2xy, 51 | pt2yx, pt2yy 52 | )); 53 | } 54 | return ( 55 | pt2xx, pt2xy, 56 | pt2yx, pt2yy 57 | ); 58 | } else if ( 59 | pt2xx == 0 && pt2xy == 0 && 60 | pt2yx == 0 && pt2yy == 0 61 | ) { 62 | assert(_isOnCurve( 63 | pt1xx, pt1xy, 64 | pt1yx, pt1yy 65 | )); 66 | return ( 67 | pt1xx, pt1xy, 68 | pt1yx, pt1yy 69 | ); 70 | } 71 | 72 | assert(_isOnCurve( 73 | pt1xx, pt1xy, 74 | pt1yx, pt1yy 75 | )); 76 | assert(_isOnCurve( 77 | pt2xx, pt2xy, 78 | pt2yx, pt2yy 79 | )); 80 | 81 | uint256[6] memory pt3 = _ECTwistAddJacobian( 82 | pt1xx, pt1xy, 83 | pt1yx, pt1yy, 84 | 1, 0, 85 | pt2xx, pt2xy, 86 | pt2yx, pt2yy, 87 | 1, 0 88 | ); 89 | 90 | return _fromJacobian( 91 | pt3[PTXX], pt3[PTXY], 92 | pt3[PTYX], pt3[PTYY], 93 | pt3[PTZX], pt3[PTZY] 94 | ); 95 | } 96 | 97 | /** 98 | * @notice Multiply a twist point by a scalar 99 | * @param s Scalar to multiply by 100 | * @param pt1xx Coefficient 1 of x 101 | * @param pt1xy Coefficient 2 of x 102 | * @param pt1yx Coefficient 1 of y 103 | * @param pt1yy Coefficient 2 of y 104 | * @return (pt2xx, pt2xy, pt2yx, pt2yy) 105 | */ 106 | function ECTwistMul( 107 | uint256 s, 108 | uint256 pt1xx, uint256 pt1xy, 109 | uint256 pt1yx, uint256 pt1yy 110 | ) public pure returns ( 111 | uint256, uint256, 112 | uint256, uint256 113 | ) { 114 | uint256 pt1zx = 1; 115 | if ( 116 | pt1xx == 0 && pt1xy == 0 && 117 | pt1yx == 0 && pt1yy == 0 118 | ) { 119 | pt1xx = 1; 120 | pt1yx = 1; 121 | pt1zx = 0; 122 | } else { 123 | assert(_isOnCurve( 124 | pt1xx, pt1xy, 125 | pt1yx, pt1yy 126 | )); 127 | } 128 | 129 | uint256[6] memory pt2 = _ECTwistMulJacobian( 130 | s, 131 | pt1xx, pt1xy, 132 | pt1yx, pt1yy, 133 | pt1zx, 0 134 | ); 135 | 136 | return _fromJacobian( 137 | pt2[PTXX], pt2[PTXY], 138 | pt2[PTYX], pt2[PTYY], 139 | pt2[PTZX], pt2[PTZY] 140 | ); 141 | } 142 | 143 | /** 144 | * @notice Get the field modulus 145 | * @return The field modulus 146 | */ 147 | function GetFieldModulus() public pure returns (uint256) { 148 | return FIELD_MODULUS; 149 | } 150 | 151 | function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) { 152 | return addmod(a, n - b, n); 153 | } 154 | 155 | function _FQ2Mul( 156 | uint256 xx, uint256 xy, 157 | uint256 yx, uint256 yy 158 | ) internal pure returns(uint256, uint256) { 159 | return ( 160 | submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS), 161 | addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS) 162 | ); 163 | } 164 | 165 | function _FQ2Muc( 166 | uint256 xx, uint256 xy, 167 | uint256 c 168 | ) internal pure returns(uint256, uint256) { 169 | return ( 170 | mulmod(xx, c, FIELD_MODULUS), 171 | mulmod(xy, c, FIELD_MODULUS) 172 | ); 173 | } 174 | 175 | function _FQ2Add( 176 | uint256 xx, uint256 xy, 177 | uint256 yx, uint256 yy 178 | ) internal pure returns(uint256, uint256) { 179 | return ( 180 | addmod(xx, yx, FIELD_MODULUS), 181 | addmod(xy, yy, FIELD_MODULUS) 182 | ); 183 | } 184 | 185 | function _FQ2Sub( 186 | uint256 xx, uint256 xy, 187 | uint256 yx, uint256 yy 188 | ) internal pure returns(uint256 rx, uint256 ry) { 189 | return ( 190 | submod(xx, yx, FIELD_MODULUS), 191 | submod(xy, yy, FIELD_MODULUS) 192 | ); 193 | } 194 | 195 | function _FQ2Div( 196 | uint256 xx, uint256 xy, 197 | uint256 yx, uint256 yy 198 | ) internal pure returns(uint256, uint256) { 199 | (yx, yy) = _FQ2Inv(yx, yy); 200 | return _FQ2Mul(xx, xy, yx, yy); 201 | } 202 | 203 | function _FQ2Inv(uint256 x, uint256 y) internal pure returns(uint256, uint256) { 204 | uint256 inv = _modInv(addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), FIELD_MODULUS); 205 | return ( 206 | mulmod(x, inv, FIELD_MODULUS), 207 | FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS) 208 | ); 209 | } 210 | 211 | function _isOnCurve( 212 | uint256 xx, uint256 xy, 213 | uint256 yx, uint256 yy 214 | ) internal pure returns (bool) { 215 | uint256 yyx; 216 | uint256 yyy; 217 | uint256 xxxx; 218 | uint256 xxxy; 219 | (yyx, yyy) = _FQ2Mul(yx, yy, yx, yy); 220 | (xxxx, xxxy) = _FQ2Mul(xx, xy, xx, xy); 221 | (xxxx, xxxy) = _FQ2Mul(xxxx, xxxy, xx, xy); 222 | (yyx, yyy) = _FQ2Sub(yyx, yyy, xxxx, xxxy); 223 | (yyx, yyy) = _FQ2Sub(yyx, yyy, TWISTBX, TWISTBY); 224 | return yyx == 0 && yyy == 0; 225 | } 226 | 227 | function _modInv(uint256 a, uint256 n) internal pure returns(uint256 t) { 228 | t = 0; 229 | uint256 newT = 1; 230 | uint256 r = n; 231 | uint256 newR = a; 232 | uint256 q; 233 | while (newR != 0) { 234 | q = r / newR; 235 | (t, newT) = (newT, submod(t, mulmod(q, newT, n), n)); 236 | (r, newR) = (newR, r - q * newR); 237 | } 238 | } 239 | 240 | function _fromJacobian( 241 | uint256 pt1xx, uint256 pt1xy, 242 | uint256 pt1yx, uint256 pt1yy, 243 | uint256 pt1zx, uint256 pt1zy 244 | ) internal pure returns ( 245 | uint256 pt2xx, uint256 pt2xy, 246 | uint256 pt2yx, uint256 pt2yy 247 | ) { 248 | uint256 invzx; 249 | uint256 invzy; 250 | (invzx, invzy) = _FQ2Inv(pt1zx, pt1zy); 251 | (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, invzx, invzy); 252 | (pt2yx, pt2yy) = _FQ2Mul(pt1yx, pt1yy, invzx, invzy); 253 | } 254 | 255 | function _ECTwistAddJacobian( 256 | uint256 pt1xx, uint256 pt1xy, 257 | uint256 pt1yx, uint256 pt1yy, 258 | uint256 pt1zx, uint256 pt1zy, 259 | uint256 pt2xx, uint256 pt2xy, 260 | uint256 pt2yx, uint256 pt2yy, 261 | uint256 pt2zx, uint256 pt2zy) internal pure returns (uint256[6] memory pt3) { 262 | if (pt1zx == 0 && pt1zy == 0) { 263 | ( 264 | pt3[PTXX], pt3[PTXY], 265 | pt3[PTYX], pt3[PTYY], 266 | pt3[PTZX], pt3[PTZY] 267 | ) = ( 268 | pt2xx, pt2xy, 269 | pt2yx, pt2yy, 270 | pt2zx, pt2zy 271 | ); 272 | return pt3; 273 | } else if (pt2zx == 0 && pt2zy == 0) { 274 | ( 275 | pt3[PTXX], pt3[PTXY], 276 | pt3[PTYX], pt3[PTYY], 277 | pt3[PTZX], pt3[PTZY] 278 | ) = ( 279 | pt1xx, pt1xy, 280 | pt1yx, pt1yy, 281 | pt1zx, pt1zy 282 | ); 283 | return pt3; 284 | } 285 | 286 | (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1 287 | (pt3[PTYX], pt3[PTYY]) = _FQ2Mul(pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2 288 | (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1 289 | (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2 290 | 291 | if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) { 292 | if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) { 293 | ( 294 | pt3[PTXX], pt3[PTXY], 295 | pt3[PTYX], pt3[PTYY], 296 | pt3[PTZX], pt3[PTZY] 297 | ) = _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); 298 | return pt3; 299 | } 300 | ( 301 | pt3[PTXX], pt3[PTXY], 302 | pt3[PTYX], pt3[PTYY], 303 | pt3[PTZX], pt3[PTZY] 304 | ) = ( 305 | 1, 0, 306 | 1, 0, 307 | 0, 0 308 | ); 309 | return pt3; 310 | } 311 | 312 | (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // W = z1 * z2 313 | (pt1xx, pt1xy) = _FQ2Sub(pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2 314 | (pt1yx, pt1yy) = _FQ2Sub(pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2 315 | (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1yx, pt1yy); // V_squared = V * V 316 | (pt2yx, pt2yy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2 317 | (pt1zx, pt1zy) = _FQ2Mul(pt1zx, pt1zy, pt1yx, pt1yy); // V_cubed = V * V_squared 318 | (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // newz = V_cubed * W 319 | (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, pt1xx, pt1xy); // U * U 320 | (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // U * U * W 321 | (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt1zx, pt1zy); // U * U * W - V_cubed 322 | (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 2); // 2 * V_squared_times_V2 323 | (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt2zx, pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2 324 | (pt3[PTXX], pt3[PTXY]) = _FQ2Mul(pt1yx, pt1yy, pt2xx, pt2xy); // newx = V * A 325 | (pt1yx, pt1yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // V_squared_times_V2 - A 326 | (pt1yx, pt1yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // U * (V_squared_times_V2 - A) 327 | (pt1xx, pt1xy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2 328 | (pt3[PTYX], pt3[PTYY]) = _FQ2Sub(pt1yx, pt1yy, pt1xx, pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2 329 | } 330 | 331 | function _ECTwistDoubleJacobian( 332 | uint256 pt1xx, uint256 pt1xy, 333 | uint256 pt1yx, uint256 pt1yy, 334 | uint256 pt1zx, uint256 pt1zy 335 | ) internal pure returns( 336 | uint256 pt2xx, uint256 pt2xy, 337 | uint256 pt2yx, uint256 pt2yy, 338 | uint256 pt2zx, uint256 pt2zy 339 | ) { 340 | (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3); // 3 * x 341 | (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x 342 | (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z 343 | (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y 344 | (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S 345 | (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W 346 | (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8); // 8 * B 347 | (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B 348 | (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S 349 | (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4); // 4 * B 350 | (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H 351 | (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H) 352 | (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8); // 8 * y 353 | (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y 354 | (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared 355 | (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared 356 | (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2); // 2 * H 357 | (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S 358 | (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared 359 | (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8); // newz = 8 * S * S_squared 360 | } 361 | 362 | function _ECTwistMulJacobian( 363 | uint256 d, 364 | uint256 pt1xx, uint256 pt1xy, 365 | uint256 pt1yx, uint256 pt1yy, 366 | uint256 pt1zx, uint256 pt1zy 367 | ) internal pure returns(uint256[6] memory pt2) { 368 | while (d != 0) { 369 | if ((d & 1) != 0) { 370 | pt2 = _ECTwistAddJacobian( 371 | pt2[PTXX], pt2[PTXY], 372 | pt2[PTYX], pt2[PTYY], 373 | pt2[PTZX], pt2[PTZY], 374 | pt1xx, pt1xy, 375 | pt1yx, pt1yy, 376 | pt1zx, pt1zy); 377 | } 378 | ( 379 | pt1xx, pt1xy, 380 | pt1yx, pt1yy, 381 | pt1zx, pt1zy 382 | ) = _ECTwistDoubleJacobian( 383 | pt1xx, pt1xy, 384 | pt1yx, pt1yy, 385 | pt1zx, pt1zy 386 | ); 387 | 388 | d = d / 2; 389 | } 390 | } 391 | } --------------------------------------------------------------------------------