Skip to content

Instantly share code, notes, and snippets.

@kobigurk
Created August 25, 2020 15:51
Show Gist options
  • Save kobigurk/b9142a4755691bb12df59fbe999c2a1f to your computer and use it in GitHub Desktop.
Save kobigurk/b9142a4755691bb12df59fbe999c2a1f to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.17+commit.d19bba13.js&optimize=false&gist=
pragma solidity ^0.5.15;
contract BLS {
// Field order
uint256 constant N = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// Negated genarator of G2
uint256 constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
uint256 constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;
uint256 constant FIELD_MASK = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint256 constant SIGN_MASK = 0x8000000000000000000000000000000000000000000000000000000000000000;
uint256 constant ODD_NUM = 0x8000000000000000000000000000000000000000000000000000000000000000;
function verifySingle(
uint256[2] memory signature,
uint256[4] memory pubkey,
uint256[2] memory message
) public view returns (bool) {
uint256[12] memory input = [
signature[0],
signature[1],
nG2x1,
nG2x0,
nG2y1,
nG2y0,
message[0],
message[1],
pubkey[1],
pubkey[0],
pubkey[3],
pubkey[2]
];
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, 384, out, 0x20)
switch success
case 0 {
invalid()
}
}
require(success, "");
return out[0] != 0;
}
function verifyMultiple(
uint256[2] memory signature,
uint256[4][] memory pubkeys,
uint256[2][] memory messages
) internal view returns (bool) {
uint256 size = pubkeys.length;
require(size > 0, "BLS: number of public key is zero");
require(
size == messages.length,
"BLS: number of public keys and messages must be equal"
);
uint256 inputSize = (size + 1) * 6;
uint256[] memory input = new uint256[](inputSize);
input[0] = signature[0];
input[1] = signature[1];
input[2] = nG2x1;
input[3] = nG2x0;
input[4] = nG2y1;
input[5] = nG2y0;
for (uint256 i = 0; i < size; i++) {
input[i * 6 + 6] = messages[i][0];
input[i * 6 + 7] = messages[i][1];
input[i * 6 + 8] = pubkeys[i][1];
input[i * 6 + 9] = pubkeys[i][0];
input[i * 6 + 10] = pubkeys[i][3];
input[i * 6 + 11] = pubkeys[i][2];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(
sub(gas(), 2000),
8,
add(input, 0x20),
mul(inputSize, 0x20),
out,
0x20
)
switch success
case 0 {
invalid()
}
}
require(success, "");
return out[0] != 0;
}
function hashToPoint(bytes memory data)
internal
view
returns (uint256[2] memory p)
{
return mapToPoint(keccak256(data));
}
function mapToPoint(bytes32 _x)
public
view
returns (uint256[2] memory p)
{
uint256 x = uint256(_x) % N;
uint256 y;
bool found = false;
while (true) {
y = mulmod(x, x, N);
y = mulmod(y, x, N);
y = addmod(y, 3, N);
(y, found) = sqrt(y);
if (found) {
p[0] = x;
p[1] = y;
break;
}
x = addmod(x, 1, N);
}
}
function mapToPointWithHelp(bytes32 _x, uint256[] memory expected_roots)
public
view
returns (uint256[2] memory p)
{
uint256 x = uint256(_x) % N;
uint8 i = 0;
uint256 y;
uint256 m;
while (true) {
y = mulmod(x, x, N);
y = mulmod(y, x, N);
y = addmod(y, 3, N);
m = mulmod(expected_roots[i],expected_roots[i], N);
if (m == y) {
p[0] = x;
p[1] = expected_roots[i];
break;
} else if (N-m == y) {
x = addmod(x, 1, N);
i += 1;
} else {
revert("Wrong expected root.");
}
}
}
function isValidPublicKey(uint256[4] memory publicKey)
internal
pure
returns (bool)
{
if (
(publicKey[0] >= N) ||
(publicKey[1] >= N) ||
(publicKey[2] >= N || (publicKey[3] >= N))
) {
return false;
} else {
return isOnCurveG2(publicKey);
}
}
function isValidSignature(uint256[2] memory signature)
internal
pure
returns (bool)
{
if ((signature[0] >= N) || (signature[1] >= N)) {
return false;
} else {
return isOnCurveG1(signature);
}
}
function pubkeyToUncompresed(
uint256[2] memory compressed,
uint256[2] memory y
) internal pure returns (uint256[4] memory uncompressed) {
uint256 desicion = compressed[0] & SIGN_MASK;
require(
desicion == ODD_NUM || y[0] & 1 != 1,
"BLS: bad y coordinate for uncompressing key"
);
uncompressed[0] = compressed[0] & FIELD_MASK;
uncompressed[1] = compressed[1];
uncompressed[2] = y[0];
uncompressed[3] = y[1];
}
function signatureToUncompresed(uint256 compressed, uint256 y)
internal
pure
returns (uint256[2] memory uncompressed)
{
uint256 desicion = compressed & SIGN_MASK;
require(
desicion == ODD_NUM || y & 1 != 1,
"BLS: bad y coordinate for uncompressing key"
);
return [compressed & FIELD_MASK, y];
}
function isValidCompressedPublicKey(uint256[2] memory publicKey)
internal
view
returns (bool)
{
uint256 x0 = publicKey[0] & FIELD_MASK;
uint256 x1 = publicKey[1];
if ((x0 >= N) || (x1 >= N)) {
return false;
} else if ((x0 == 0) && (x1 == 0)) {
return false;
} else {
return isOnCurveG2([x0, x1]);
}
}
function isValidCompressedSignature(uint256 signature)
internal
view
returns (bool)
{
uint256 x = signature & FIELD_MASK;
if (x >= N) {
return false;
} else if (x == 0) {
return false;
}
return isOnCurveG1(x);
}
function isOnCurveG1(uint256[2] memory point)
internal
pure
returns (bool _isOnCurve)
{
// solium-disable-next-line security/no-inline-assembly
assembly {
let t0 := mload(point)
let t1 := mload(add(point, 32))
let t2 := mulmod(t0, t0, N)
t2 := mulmod(t2, t0, N)
t2 := addmod(t2, 3, N)
t1 := mulmod(t1, t1, N)
_isOnCurve := eq(t1, t2)
}
}
function isOnCurveG1(uint256 x) internal view returns (bool _isOnCurve) {
bool callSuccess;
// solium-disable-next-line security/no-inline-assembly
assembly {
let t0 := x
let t1 := mulmod(t0, t0, N)
t1 := mulmod(t1, t0, N)
t1 := addmod(t1, 3, N)
let freemem := mload(0x40)
mstore(freemem, 0x20)
mstore(add(freemem, 0x20), 0x20)
mstore(add(freemem, 0x40), 0x20)
mstore(add(freemem, 0x60), t1)
// (N - 1) / 2 = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
mstore(
add(freemem, 0x80),
0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
)
// N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
mstore(
add(freemem, 0xA0),
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
)
callSuccess := staticcall(
sub(gas(), 2000),
5,
freemem,
0xC0,
freemem,
0x20
)
_isOnCurve := eq(1, mload(freemem))
}
}
function isOnCurveG2(uint256[4] memory point)
internal
pure
returns (bool _isOnCurve)
{
// solium-disable-next-line security/no-inline-assembly
assembly {
// x0, x1
let t0 := mload(point)
let t1 := mload(add(point, 32))
// x0 ^ 2
let t2 := mulmod(t0, t0, N)
// x1 ^ 2
let t3 := mulmod(t1, t1, N)
// 3 * x0 ^ 2
let t4 := add(add(t2, t2), t2)
// 3 * x1 ^ 2
let t5 := addmod(add(t3, t3), t3, N)
// x0 * (x0 ^ 2 - 3 * x1 ^ 2)
t2 := mulmod(add(t2, sub(N, t5)), t0, N)
// x1 * (3 * x0 ^ 2 - x1 ^ 2)
t3 := mulmod(add(t4, sub(N, t3)), t1, N)
// x ^ 3 + b
t0 := addmod(
t2,
0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5,
N
)
t1 := addmod(
t3,
0x009713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2,
N
)
// y0, y1
t2 := mload(add(point, 64))
t3 := mload(add(point, 96))
// y ^ 2
t4 := mulmod(addmod(t2, t3, N), addmod(t2, sub(N, t3), N), N)
t3 := mulmod(shl(1, t2), t3, N)
// y ^ 2 == x ^ 3 + b
_isOnCurve := and(eq(t0, t4), eq(t1, t3))
}
}
function isOnCurveG2(uint256[2] memory x)
internal
view
returns (bool _isOnCurve)
{
bool callSuccess;
// solium-disable-next-line security/no-inline-assembly
assembly {
// x0, x1
let t0 := mload(add(x, 0))
let t1 := mload(add(x, 32))
// x0 ^ 2
let t2 := mulmod(t0, t0, N)
// x1 ^ 2
let t3 := mulmod(t1, t1, N)
// 3 * x0 ^ 2
let t4 := add(add(t2, t2), t2)
// 3 * x1 ^ 2
let t5 := addmod(add(t3, t3), t3, N)
// x0 * (x0 ^ 2 - 3 * x1 ^ 2)
t2 := mulmod(add(t2, sub(N, t5)), t0, N)
// x1 * (3 * x0 ^ 2 - x1 ^ 2)
t3 := mulmod(add(t4, sub(N, t3)), t1, N)
// x ^ 3 + b
t0 := add(
t2,
0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5
)
t1 := add(
t3,
0x009713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2
)
// is non residue ?
t0 := addmod(mulmod(t0, t0, N), mulmod(t1, t1, N), N)
let freemem := mload(0x40)
mstore(freemem, 0x20)
mstore(add(freemem, 0x20), 0x20)
mstore(add(freemem, 0x40), 0x20)
mstore(add(freemem, 0x60), t0)
// (N - 1) / 2 = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
mstore(
add(freemem, 0x80),
0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
)
// N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
mstore(
add(freemem, 0xA0),
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
)
callSuccess := staticcall(
sub(gas(), 2000),
5,
freemem,
0xC0,
freemem,
0x20
)
_isOnCurve := eq(1, mload(freemem))
}
}
function isNonResidueFP(uint256 e)
internal
view
returns (bool isNonResidue)
{
bool callSuccess;
// solium-disable-next-line security/no-inline-assembly
assembly {
let freemem := mload(0x40)
mstore(freemem, 0x20)
mstore(add(freemem, 0x20), 0x20)
mstore(add(freemem, 0x40), 0x20)
mstore(add(freemem, 0x60), e)
// (N - 1) / 2 = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
mstore(
add(freemem, 0x80),
0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
)
// N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
mstore(
add(freemem, 0xA0),
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
)
callSuccess := staticcall(
sub(gas(), 2000),
5,
freemem,
0xC0,
freemem,
0x20
)
isNonResidue := eq(1, mload(freemem))
}
require(callSuccess, "BLS: isNonResidueFP modexp call failed");
return !isNonResidue;
}
function isNonResidueFP2(uint256[2] memory e)
internal
view
returns (bool isNonResidue)
{
uint256 a = addmod(mulmod(e[0], e[0], N), mulmod(e[1], e[1], N), N);
bool callSuccess;
// solium-disable-next-line security/no-inline-assembly
assembly {
let freemem := mload(0x40)
mstore(freemem, 0x20)
mstore(add(freemem, 0x20), 0x20)
mstore(add(freemem, 0x40), 0x20)
mstore(add(freemem, 0x60), a)
// (N - 1) / 2 = 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
mstore(
add(freemem, 0x80),
0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3
)
// N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
mstore(
add(freemem, 0xA0),
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
)
callSuccess := staticcall(
sub(gas(), 2000),
5,
freemem,
0xC0,
freemem,
0x20
)
isNonResidue := eq(1, mload(freemem))
}
require(callSuccess, "BLS: isNonResidueFP2 modexp call failed");
return !isNonResidue;
}
function sqrt(uint256 xx) internal view returns (uint256 x, bool hasRoot) {
bool callSuccess;
// solium-disable-next-line security/no-inline-assembly
assembly {
let freemem := mload(0x40)
mstore(freemem, 0x20)
mstore(add(freemem, 0x20), 0x20)
mstore(add(freemem, 0x40), 0x20)
mstore(add(freemem, 0x60), xx)
// (N + 1) / 4 = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52
mstore(
add(freemem, 0x80),
0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52
)
// N = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
mstore(
add(freemem, 0xA0),
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
)
callSuccess := staticcall(
sub(gas(), 2000),
5,
freemem,
0xC0,
freemem,
0x20
)
x := mload(freemem)
hasRoot := eq(xx, mulmod(x, x, N))
}
require(callSuccess, "BLS: sqrt modexp call failed");
}
}
@kobigurk
Copy link
Author

Example call to mapToPointWithHelp:

"0x0000000000000000000000000000000000000000000000000000000000000003",["16469166999615883226695964867118064280147127342783597836693979910667010785192", "12018145221906193280709297956955491909525805305027991674545581949749903546958"]

@wdcs-amitkumar
Copy link

Hi @kobigurk

This smart contract is BN254 based or it can perform BLS12 381 signature verification as well ??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment