Skip to content

Latest commit

 

History

History
938 lines (768 loc) · 29.9 KB

File metadata and controls

938 lines (768 loc) · 29.9 KB

C/C++ API

Minimum sample

A sample demonstrating BLS12-381 usage:

C

sample/pairing_c.c

cd mcl
make -j4
make bin/pairing_c.exe && bin/pairing_c.exe

C++

sample/pairing.cpp

cd mcl
make -j4
make bin/pairing.exe && bin/pairing.exe

Header and libraries

C

To use BLS12-381, include <mcl/bn_c384_256.h> and link libmcl.{a,so}.

384_256 indicates that the maximum bit size of Fp is 384, and the size of Fr is 256.

C++

Include <mcl/bls12_381.hpp> and link libmcl.{a,so}.

Notation

The elliptic curve equation is E: y^2 = x^3 + b.

  • Fp : a finite field of prime order p, over which the curve is defined.
  • Fr : a finite field of prime order r.
  • Fp2 : field extension over Fp of degree 2. Fp[i] / (i^2 + u).
  • Fp6 : field extension over Fp2 of degree 3. Fp2[v] / (v^3 - Xi) where Xi = i + xi_a.
  • Fp12 : field extension over Fp6 of degree 2. Fp6[w] / (w^2 - v).
  • G1 : the cyclic subgroup of E(Fp).
  • G2 : the cyclic subgroup of E'(Fp^2), where E' is a twist of E.
  • GT : the cyclic subgroup of Fp12.
    • G1, G2, and GT have the order r.

The pairing e: G1 x G2 -> GT is the optimal ate pairing.

mcl treats G1 and G2 as additive groups and GT as a multiplicative group.

  • mclSize ; unsigned int if WebAssembly, otherwise size_t
  • mclInt ; int if WebAssembly, otherwise int64_t

Curve Parameters

r = |G1| = |G2| = |GT|

curveType b u xi_a r and p
BN254 2 1 1 r = 0x2523648240000001ba344d8000000007ff9f800000000010a10000000000000d
p = 0x2523648240000001ba344d80000000086121000000000013a700000000000013
BN_SNARK1 3 1 9 r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
p = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47
BLS12_381 4 1 1 r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
BLS12_377 1 5 0 r = 0x12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001
p = 0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001
BN381 2 1 1 r = 0x240026400f3d82b2e42de125b00158405b710818ac000007e0042f008e3e00000000001080046200000000000000000d
p = 0x240026400f3d82b2e42de125b00158405b710818ac00000840046200950400000000001380052e000000000000000013

C Structures

mclBnFp

This is a struct for Fp. The value is stored in Montgomery representation.

mclBnFr

This is a struct for Fr. The value is stored in Montgomery representation.

mclBnFp2

This is a struct for Fp2 with a member mclBnFp d[2].

An element x of Fp2 is represented as x = d[0] + d[1] i where i^2 = -1.

mclBnG1

This is a struct for G1 with three members x, y, z of type mclBnFp.

An element P of G1 is represented as P = [x:y:z] in Jacobian coordinates.

mclBnG2

This is a struct for G2 with three members x, y, z of type mclBnFp2.

An element Q of G2 is represented as Q = [x:y:z] in Jacobian coordinates.

mclBnGT

This is a struct for GT with a member mclBnFp d[12].

C++ Structures

The namespace is mcl::bn. <mcl/bls12_381.hpp> also provides the namespace mcl::bls12, which imports mcl::bn.

Fp

This is a class for Fp.

Fr

This is a class for Fr.

Fp2

This is a struct for Fp2 with members a and b of type Fp.

An element x of Fp2 is represented as x = a + b i where i^2 = -1.

Fp6

This is a struct for Fp6 with members a, b, and c of type Fp2.

An element x of Fp6 is represented as x = a + b v + c v^2 where v^3 = xi := 1 + i.

Fp12

This is a struct for Fp12 with members a and b of type Fp6.

An element x of Fp12 is represented as x = a + b w where w^2 = v.

G1

This is a struct for G1 with three members x, y, z of type Fp.

An element P of G1 is represented as P = [x:y:z] in Jacobian coordinates.

G2

This is a struct for G2 with three members x, y, z of type Fp2.

An element Q of G2 is represented as Q = [x:y:z] in Jacobian coordinates.

GT

GT is an alias for Fp12. However, it represents the set { x in Fp12 | x^r = 1}.

Parameter Settings and Supported Curves

By default, MCL_FP_BIT=384 and MCL_FR_BIT=256 are set. If you want to change these values, use commands such as make MCL_FP_BIT=256 MCL_FR_BIT=256. You must set the same macro and value to compile your application program.

MCL_FP_BIT MCL_FR_BIT sizeof Fr sizeof Fp supported curves
256 256 32 32 MCL_BN254, MCL_BN_SNARK1
384 256 48 32 +MCL_BLS12_381, MCL_BLS12_377
384 384 48 48 +MCL_BN381_1

Thread safety

All functions except initialization and global setting changes are thread-safe.

Initialization

C

Initialize the mcl library. Call this function first before calling other functions.

int mclBn_init(int curve, int compiledTimeVar);
  • curve ; specify the curve type
  • compiledTimeVar ; set MCLBN_COMPILED_TIME_VAR, a macro used to ensure that the values are consistent between library build time and usage.
  • return 0 on success.
  • This is not thread safe.

C++

void initPairing(bool *pb, const mcl::CurveParam& cp = mcl::BN254);
void initPairing(const mcl::CurveParam& cp = mcl::BN254); // throws an exception on error

The curve parameters are defined in mcl/curve_type.hpp.

  • mcl::BN254
  • mcl::BN_SNARK1
  • mcl::BLS12_381
  • mcl::BLS12_377
  • mcl::BN381_1

Global setting

int mclBn_setMapToMode(int mode);
  • mode = MCL_MAP_TO_MODE_ORIGINAL : the old hash-to-curve (for backward compatibility)
  • mode = MCL_MAP_TO_MODE_HASH_TO_CURVE : the hash-to-curve defined in RFC 9380 (Hashing to Elliptic Curves)

Control to verify that a point of the elliptic curve has the order r.

This function affects setStr() and deserialize() for G1/G2.

C

void mclBn_verifyOrderG1(int doVerify);
void mclBn_verifyOrderG2(int doVerify);

C++

verifyOrderG1(bool doVerify);
verifyOrderG2(bool doVerify);
  • verify if doVerify is 1, otherwise do not verify.
  • The default values set by initialization are as follows: for BLS12 curves, both G1 and G2 are verified (a fast subgroup check is used). For BN curves, G1 is not verified (the cofactor is 1, so the check is unnecessary) and G2 is verified.
  • Set doVerify = 1 if protection against subgroup attacks is necessary.
  • This is not thread-safe.

Set DST for hash functions

C

int mclBnG1_setDst(const char *dst, mclSize dstSize);
int mclBnG2_setDst(const char *dst, mclSize dstSize);

return 0 if success else -1

C++

bool setDstG1(const char *dst, size_t dstSize);
bool setDstG2(const char *dst, size_t dstSize);

return true if success else false

  • set dst[0, dstSize) to the DST for hash functions
  • The default value:
    • G1 : BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_
    • G2 : BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_

Setter / Getter

Clear

Set x to zero.

void mclBnFr_clear(mclBnFr *x);
void mclBnFp_clear(mclBnFp *x);
void mclBnFp2_clear(mclBnFp2 *x);
void mclBnG1_clear(mclBnG1 *x);
void mclBnG2_clear(mclBnG2 *x);
void mclBnGT_clear(mclBnGT *x);

C++

T::clear();

Set an integer x to y.

void mclBnFp_setInt(mclBnFp *y, mclInt x);
void mclBnFr_setInt(mclBnFr *y, mclInt x);
void mclBnGT_setInt(mclBnGT *y, mclInt x);

C++

T x = <integer literal>;

Set bufSize bytes from buf to x with masking according to the following method.

int mclBnFp_setLittleEndian(mclBnFp *x, const void *buf, mclSize bufSize);
int mclBnFr_setLittleEndian(mclBnFr *x, const void *buf, mclSize bufSize);

C++

T::setArrayMask(const uint8_t *buf, size_t n);
  1. set x = buf[0..bufSize-1] as little endian
  2. x &= (1 << bitLen(r)) - 1
  3. if (x >= r), then x &= (1 << (bitLen(r) - 1)) - 1
  • always returns 0

Set bufSize bytes from buf modulo p or r to x.

int mclBnFp_setLittleEndianMod(mclBnFp *x, const void *buf, mclSize bufSize);
int mclBnFr_setLittleEndianMod(mclBnFr *x, const void *buf, mclSize bufSize);
int mclBnFp_setBigEndianMod(mclBnFp *x, const void *buf, mclSize bufSize);
int mclBnFr_setBigEndianMod(mclBnFr *x, const void *buf, mclSize bufSize);

C++

void T::setLittleEndianMod(const uint8_t *buf, size_t bufSize);
void T::setBigEndianMod(const uint8_t *buf, size_t bufSize);
  • return 0 if bufSize <= (sizeof(T) * 2), otherwise -1

Get little-endian byte sequence in buf corresponding to x

mclSize mclBnFr_getLittleEndian(void *buf, mclSize maxBufSize, const mclBnFr *x);
mclSize mclBnFp_getLittleEndian(void *buf, mclSize maxBufSize, const mclBnFp *x);

C++

size_t T::getLittleEndian(uint8_t *buf, size_t maxBufSize) const
  • write x to buf in little-endian format
  • return the written size on success, otherwise 0
  • NOTE: if x is zero, then buf[0] = 0 and the function returns 1.

Serialization

Serialize

mclSize mclBnFr_serialize(void *buf, mclSize maxBufSize, const mclBnFr *x);
mclSize mclBnG1_serialize(void *buf, mclSize maxBufSize, const mclBnG1 *x);
mclSize mclBnG2_serialize(void *buf, mclSize maxBufSize, const mclBnG2 *x);
mclSize mclBnGT_serialize(void *buf, mclSize maxBufSize, const mclBnGT *x);
mclSize mclBnFp_serialize(void *buf, mclSize maxBufSize, const mclBnFp *x);
mclSize mclBnFp2_serialize(void *buf, mclSize maxBufSize, const mclBnFp2 *x);

C++

mclSize T::serialize(void *buf, mclSize maxBufSize) const;
  • serialize x into buf[0..maxBufSize-1]
  • return written byte size on success, otherwise 0

Serialization Format

  • Fp (resp. Fr) ; a little-endian byte sequence with a fixed size
    • the size is the return value of mclBn_getFpByteSize() (resp. mclBn_getFrByteSize()).
  • G1 ; compressed with a fixed size
    • the size is equal to mclBn_getG1ByteSize() (=mclBn_getFpByteSize()).
  • G2 ; compressed with a fixed size
    • the size is equal to mclBn_getG2ByteSize().

A pseudo-code to serialize P of G1 (resp. G2):

size = mclBn_getG1ByteSize() # resp. mclBn_getG2ByteSize()
if P is zero:
  return [0] * size
else:
  P = P.normalize()
  s = P.x.serialize()
  # x in Fp2 is odd <=> x.a is odd
  if Fp is fullBit:
    s = [P.y is odd ? 3 : 2] + s # prepend one byte
  elif P.y is odd: # resp. P.y.d[0] is odd
    s[byte-length(s) - 1] |= 0x80
  return s

Ethereum serialization mode for BLS12-381

void mclBn_setETHserialization(int ETHserialization);
  • Set ETHserialization = 1 for Ethereum compatibility (default 0).
  • See FAQ.

Deserialize

mclSize mclBnFr_deserialize(mclBnFr *x, const void *buf, mclSize bufSize);
mclSize mclBnG1_deserialize(mclBnG1 *x, const void *buf, mclSize bufSize);
mclSize mclBnG2_deserialize(mclBnG2 *x, const void *buf, mclSize bufSize);
mclSize mclBnGT_deserialize(mclBnGT *x, const void *buf, mclSize bufSize);
mclSize mclBnFp_deserialize(mclBnFp *x, const void *buf, mclSize bufSize);
mclSize mclBnFp2_deserialize(mclBnFp2 *x, const void *buf, mclSize bufSize);

C++

mclSize T::deserialize(const void *buf, mclSize bufSize);
  • deserialize x from buf[0..bufSize-1]
  • return read size on success, otherwise 0
    • mclBnG1_deserialize and mclBnG2_deserialize check whether the point has the correct order of G1/G2.
    • mclBnGT_deserialize does not check it. Call mclBnGT_isValid if necessary.

String conversion

Get string

mclSize mclBnFr_getStr(char *buf, mclSize maxBufSize, const mclBnFr *x, int ioMode);
mclSize mclBnG1_getStr(char *buf, mclSize maxBufSize, const mclBnG1 *x, int ioMode);
mclSize mclBnG2_getStr(char *buf, mclSize maxBufSize, const mclBnG2 *x, int ioMode);
mclSize mclBnGT_getStr(char *buf, mclSize maxBufSize, const mclBnGT *x, int ioMode);
mclSize mclBnFp_getStr(char *buf, mclSize maxBufSize, const mclBnFp *x, int ioMode);

C++

size_t T::getStr(char *buf, size_t maxBufSize, int iMode = 0) const
  • write x to buf according to ioMode
  • ioMode
    • 2 ; binary number
    • 10 ; decimal number
    • 16 ; hexadecimal number
    • MCLBN_IO_EC_PROJ ; output in Jacobian coordinates
  • return strlen(buf) on success, otherwise 0.

The meaning of the output of G1:

  • 0 ; infinity
  • 1 <x> <y> ; affine coordinate
  • 4 <x> <y> <z> ; Jacobian coordinates
  • the element <x> of G2 outputs d[0] d[1].

Set string

int mclBnFr_setStr(mclBnFr *x, const char *buf, mclSize bufSize, int ioMode);
int mclBnG1_setStr(mclBnG1 *x, const char *buf, mclSize bufSize, int ioMode);
int mclBnG2_setStr(mclBnG2 *x, const char *buf, mclSize bufSize, int ioMode);
int mclBnGT_setStr(mclBnGT *x, const char *buf, mclSize bufSize, int ioMode);
int mclBnFp_setStr(mclBnFp *x, const char *buf, mclSize bufSize, int ioMode);

C++

void T::setStr(bool *pb, const char *str, int iMode = 0)
void T::setStr(const char *str, int iMode = 0)
  • set buf[0..bufSize-1] to x according to ioMode
    • mask and truncate the value if it is greater than (r or p).
    • See masking
  • A bufSize that is too large is rejected. The maximum length depends on compile options, but it is at least the bit length of the type of x.
  • Setting a string for G1/G2 fails if the point is not on the elliptic curve.
    • By default, it also checks that the point has the correct order of G1/G2.
    • You can disable this check with mclBn_verifyOrderG1(0) / mclBn_verifyOrderG2(0).
  • return 0 if success else -1
    • *pb = result of setStr or throw exception if error (C++)
    • mclBnG1_setStr and mclBnG2_setStr check whether the point has the correct order of G1/G2.
    • mclBnGT_setStr does not check it. Call mclBnGT_isValid if necessary.

If you want to use the same BLS12-381 generator as zkcrypto then,

mclBnG1 P;
const char *g1Str = "1 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1";
mclBnG1_setStr(&P, g1Str, strlen(g1Str), 16);

mclBnG2 Q;
const char *g2Str = "1 0x24aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e 0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801 0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be";
mclBnG2_setStr(&Q, g2Str, strlen(g2Str), 16);

Set a random value

Set x by a cryptographically secure pseudo-random number generator.

int mclBnFr_setByCSPRNG(mclBnFr *x);
int mclBnFp_setByCSPRNG(mclBnFp *x);

C++

void T::setByCSPRNG()

Change random generator function

void mclBn_setRandFunc(
  void *self,
  unsigned int (*readFunc)(void *self, void *buf, unsigned int bufSize)
);
  • self ; user-defined pointer
  • readFunc ; user-defined function, which writes random bufSize bytes to buf and returns bufSize if success else returns 0.
    • readFunc must be thread-safe.
  • Set the default random function if self == 0 and readFunc == 0.
  • This is not thread-safe.

Arithmetic operations

neg / inv / sqr / add / sub / mul / div of Fr, Fp, Fp2, GT.

void mclBnFr_neg(mclBnFr *y, const mclBnFr *x);
void mclBnFr_inv(mclBnFr *y, const mclBnFr *x);
// y[i] = 1/x[i] if x[i] != 0 else 0
// faster than normalizing each one individually
void mclBnFr_invVec(mclBnFr *y, const mclBnFr *x, mclSize n);
void mclBnFr_sqr(mclBnFr *y, const mclBnFr *x);
void mclBnFr_add(mclBnFr *z, const mclBnFr *x, const mclBnFr *y);
void mclBnFr_sub(mclBnFr *z, const mclBnFr *x, const mclBnFr *y);
void mclBnFr_mul(mclBnFr *z, const mclBnFr *x, const mclBnFr *y);
void mclBnFr_div(mclBnFr *z, const mclBnFr *x, const mclBnFr *y);

void mclBnFp_neg(mclBnFp *y, const mclBnFp *x);
void mclBnFp_inv(mclBnFp *y, const mclBnFp *x);
void mclBnFp_invVec(mclBnFp *y, const mclBnFp *x, mclSize n);
void mclBnFp_sqr(mclBnFp *y, const mclBnFp *x);
void mclBnFp_add(mclBnFp *z, const mclBnFp *x, const mclBnFp *y);
void mclBnFp_sub(mclBnFp *z, const mclBnFp *x, const mclBnFp *y);
void mclBnFp_mul(mclBnFp *z, const mclBnFp *x, const mclBnFp *y);
void mclBnFp_div(mclBnFp *z, const mclBnFp *x, const mclBnFp *y);

void mclBnFp2_neg(mclBnFp2 *y, const mclBnFp2 *x);
void mclBnFp2_inv(mclBnFp2 *y, const mclBnFp2 *x);
void mclBnFp2_sqr(mclBnFp2 *y, const mclBnFp2 *x);
void mclBnFp2_add(mclBnFp2 *z, const mclBnFp2 *x, const mclBnFp2 *y);
void mclBnFp2_sub(mclBnFp2 *z, const mclBnFp2 *x, const mclBnFp2 *y);
void mclBnFp2_mul(mclBnFp2 *z, const mclBnFp2 *x, const mclBnFp2 *y);
void mclBnFp2_div(mclBnFp2 *z, const mclBnFp2 *x, const mclBnFp2 *y);

void mclBnGT_inv(mclBnGT *y, const mclBnGT *x); // y = a - bw for x = a + bw where Fp12 = Fp6[w]
void mclBnGT_sqr(mclBnGT *y, const mclBnGT *x);
void mclBnGT_mul(mclBnGT *z, const mclBnGT *x, const mclBnGT *y);
void mclBnGT_div(mclBnGT *z, const mclBnGT *x, const mclBnGT *y);
  • use mclBnGT_invGeneric for an element in Fp12 - GT.

  • NOTE: The result of the following functions is not an element of GT because GT is a multiplicative group.

void mclBnGT_neg(mclBnGT *y, const mclBnGT *x);
void mclBnGT_add(mclBnGT *z, const mclBnGT *x, const mclBnGT *y);
void mclBnGT_sub(mclBnGT *z, const mclBnGT *x, const mclBnGT *y);

C++

  • +, -, *, /
T::add(T& z, const T& x, const T& y);
T::sub(T& z, const T& x, const T& y);
T::mul(T& z, const T& x, const T& y);
T::div(T& z, const T& x, const T& y);
T::neg(T& y, const T& x);
T::inv(T& y, const T& x);
// y[i] = 1/x[i] if x[i] != 0 else 0
mcl::invVec(T y[], const T x[], size_t n);

pow of Fr, Fp

  • pow(z, x, y) means z = x^y.
  • powArray(z, x, y, ySize) means z = x^y where y is a little endian ySize-byte sequence.
// z = x^y
void mclBnFp_pow(mclBnFp *z, const mclBnFp *x, const mclBnFp *y);
void mclBnFr_pow(mclBnFr *z, const mclBnFr *x, const mclBnFr *y);

// return 0 if ySize <= mclBn_getFrByteSize() else -1
int mclBnFr_powArray(mclBnFr *z, const mclBnFr *x, const uint8_t *y, mclSize ySize);
// return 0 if ySize <= mclBn_getFpByteSize() else -1
int mclBnFp_powArray(mclBnFp *z, const mclBnFp *x, const uint8_t *y, mclSize ySize);

Square root of x.

int mclBnFr_squareRoot(mclBnFr *y, const mclBnFr *x);
int mclBnFp_squareRoot(mclBnFp *y, const mclBnFp *x);
int mclBnFp2_squareRoot(mclBnFp2 *y, const mclBnFp2 *x);

C++

bool T::squareRoot(T& y, const T& x);
  • y is one of the square roots of x if it exists.
  • return 0 if success else -1

add / sub / dbl / neg for G1 and G2.

void mclBnG1_neg(mclBnG1 *y, const mclBnG1 *x);
void mclBnG1_dbl(mclBnG1 *y, const mclBnG1 *x);
void mclBnG1_add(mclBnG1 *z, const mclBnG1 *x, const mclBnG1 *y);
void mclBnG1_sub(mclBnG1 *z, const mclBnG1 *x, const mclBnG1 *y);

void mclBnG2_neg(mclBnG2 *y, const mclBnG2 *x);
void mclBnG2_dbl(mclBnG2 *y, const mclBnG2 *x);
void mclBnG2_add(mclBnG2 *z, const mclBnG2 *x, const mclBnG2 *y);
void mclBnG2_sub(mclBnG2 *z, const mclBnG2 *x, const mclBnG2 *y);

C++

  • +, -
    • T::add(T& z, const T& x, const T& y);
    • T::sub(T& z, const T& x, const T& y);
    • T::neg(T& y, const T& x);

Convert a point from Jacobian/projective coordinates to affine.

void mclBnG1_normalize(mclBnG1 *y, const mclBnG1 *x);
void mclBnG2_normalize(mclBnG2 *y, const mclBnG2 *x);

// y[i] = normalize(x[i]) for i = 0, 1, ..., n-1
// faster than normalizing each one individually
void mclBnG1_normalizeVec(mclBnG1 *y, const mclBnG1 *x, mclSize n);
void mclBnG2_normalizeVec(mclBnG2 *y, const mclBnG2 *x, mclSize n);

C++

T::normalize(T& y, const T& x);
T::normalizeVec(T *y, const T *x, size_t n);
  • convert [x:y:z] to [x:y:1] if z != 0 else [*:*:0]

scalar multiplication

void mclBnG1_mul(mclBnG1 *z, const mclBnG1 *x, const mclBnFr *y);
void mclBnG2_mul(mclBnG2 *z, const mclBnG2 *x, const mclBnFr *y);
void mclBnGT_pow(mclBnGT *z, const mclBnGT *x, const mclBnFr *y);

C++

T::mul(T& z, const T& x, const Fr& y);
  • z = x * y for G1 / G2

  • z = pow(x, y) for GT

  • use mclBnGT_powGeneric for an element in Fp12 - GT.

multi-scalar multiplication

void mclBnG1_mulVec(mclBnG1 *z, mclBnG1 *x, const mclBnFr *y, mclSize n);
void mclBnG2_mulVec(mclBnG2 *z, mclBnG2 *x, const mclBnFr *y, mclSize n);
void mclBnGT_powVec(mclBnGT *z, const mclBnGT *x, const mclBnFr *y, mclSize n);

C++

T::mulVec(T& z, T* x, const Fr *y, size_t n);
  • z = sum_{i=0}^{n-1} mul(x[i], y[i]) for G1 / G2.
  • z = prod_{i=0}^{n-1} pow(x[i], y[i]) for GT.
  • x[] is not const because the points may be normalized (the values they represent do not change).

multi-threaded multi-scalar multiplication

void mclBnG1_mulVecMT(mclBnG1 *z, mclBnG1 *x, const mclBnFr *y, mclSize n, mclSize cpuN);
void mclBnG2_mulVecMT(mclBnG2 *z, mclBnG2 *x, const mclBnFr *y, mclSize n, mclSize cpuN);
  • multi-threaded version of mulVec with cpuN threads
  • enabled if the library is built with MCL_USE_OMP=1

scalar multiplication of each point

void mclBnG1_mulEach(mclBnG1 *x, const mclBnFr *y, mclSize n);

C++

G1::mulEach(G1 *xVec, const Fr *yVec, size_t n);
  • xVec[i] *= yVec[i]
  • G1::mulVec and G1::mulEach for BLS12-381 use AVX-512 IFMA if possible

hash-to-curve function

Set hash of buf[0..bufSize-1] to x

int mclBnFr_setHashOf(mclBnFr *x, const void *buf, mclSize bufSize);
int mclBnFp_setHashOf(mclBnFp *x, const void *buf, mclSize bufSize);

C++

T::setHashOf(const void *msg, size_t msgSize);
  • always returns 0
  • uses SHA-256 if the bit size of the field is not greater than 256, otherwise SHA-512
  • sets the value in the same way as setLittleEndian.
  • This function is for backward compatibility only. DON'T use it. Instead, apply setLittleEndianMod to the hashed value.

map x to G1 / G2.

int mclBnFp_mapToG1(mclBnG1 *y, const mclBnFp *x);
int mclBnFp2_mapToG2(mclBnG2 *y, const mclBnFp2 *x);

C++

void mapToG1(G1& P, const Fp& x);
void mapToG2(G2& P, const Fp2& x);
  • See struct MapTo in src/map_impl.hpp for details of the algorithm.
  • return 0 if success else -1

If you want to use the MapTo function defined in RFC 9380 (Hashing to Elliptic Curves), then use mclBn_setMapToMode.

mclBn_setMapToMode(MCL_MAP_TO_MODE_HASH_TO_CURVE);

And if you want to change DST (domain separation tag), then use setDst functions.

int mclBnG1_setDst(const char *dst, mclSize dstSize);
int mclBnG2_setDst(const char *dst, mclSize dstSize);

hash and map to G1 / G2.

int mclBnG1_hashAndMapTo(mclBnG1 *x, const void *buf, mclSize bufSize);
int mclBnG2_hashAndMapTo(mclBnG2 *x, const void *buf, mclSize bufSize);

C++

void hashAndMapToG1(G1& P, const void *buf, size_t bufSize);
void hashAndMapToG2(G2& P, const void *buf, size_t bufSize);
  • Combines the setHashOf and mapTo functions

Pairing operations

The pairing function e(P, Q) consists of two parts:

  • MillerLoop(P, Q)
  • finalExp(x)

finalExp satisfies the following properties:

  • e(P, Q) = finalExp(MillerLoop(P, Q))
  • e(P1, Q1) e(P2, Q2) = finalExp(MillerLoop(P1, Q1) MillerLoop(P2, Q2))

pairing

void mclBn_pairing(mclBnGT *z, const mclBnG1 *x, const mclBnG2 *y);

C++

void pairing(GT& z, const G1& x, const G2& y);

millerLoop

void mclBn_millerLoop(mclBnGT *z, const mclBnG1 *x, const mclBnG2 *y);

C++

void millerLoop(GT& z, const G1& x, const G2& y);

finalExp

void mclBn_finalExp(mclBnGT *y, const mclBnGT *x);

C++

void finalExp(GT& y, const GT& x);

Variants of MillerLoop

multi pairing

void mclBn_millerLoopVec(mclBnGT *z, const mclBnG1 *x, const mclBnG2 *y, mclSize n);

C++

void millerLoopVec(GT& z, const G1 *x, const G2 *y, size_t n);
  • This function is for multi-pairing
    • computes prod_{i=0}^{n-1} MillerLoop(x[i], y[i])
    • prod_{i=0}^{n-1} e(x[i], y[i]) = finalExp(prod_{i=0}^{n-1} MillerLoop(x[i], y[i]))
void mclBn_millerLoopVecMT(mclBnGT *z, const mclBnG1 *x, const mclBnG2 *y, mclSize n, mclSize cpuN);
  • multi-threaded version of mclBn_millerLoopVec with cpuN threads
  • enabled if the library is built with MCL_USE_OMP=1

pairing for a fixed point of G2

int mclBn_getUint64NumToPrecompute(void);
void mclBn_precomputeG2(uint64_t *Qbuf, const mclBnG2 *Q);
void mclBn_precomputedMillerLoop(mclBnGT *f, const mclBnG1 *P, const uint64_t *Qbuf);

These functions compute the same value as pairing(P, Q) as follows:

uint64_t *Qbuf = (uint64_t*)malloc(mclBn_getUint64NumToPrecompute() * sizeof(uint64_t));
mclBn_precomputeG2(Qbuf, Q); // precomputing of Q
mclBn_precomputedMillerLoop(f, P, Qbuf); // pairing of any P of G1 and the fixed Q
free(Qbuf);
void mclBn_precomputedMillerLoop2(
  mclBnGT *f,
  const mclBnG1 *P1, const uint64_t *Q1buf,
  const mclBnG1 *P2, const uint64_t *Q2buf
);
  • compute MillerLoop(P1, Q1buf) * MillerLoop(P2, Q2buf)
void mclBn_precomputedMillerLoop2mixed(
  mclBnGT *f,
  const mclBnG1 *P1, const mclBnG2 *Q1,
  const mclBnG1 *P2, const uint64_t *Q2buf
);
  • compute MillerLoop(P1, Q1) * MillerLoop(P2, Q2buf)

Check value

Check validity

int mclBnFr_isValid(const mclBnFr *x);
int mclBnFp_isValid(const mclBnFp *x);
int mclBnG1_isValid(const mclBnG1 *x);
int mclBnG2_isValid(const mclBnG2 *x);

C++

bool T::isValid() const;
  • return 1 if true else 0

Check the order of a point

int mclBnG1_isValidOrder(const mclBnG1 *x);
int mclBnG2_isValidOrder(const mclBnG2 *x);

C++

bool T::isValidOrder() const;
  • Check whether the order of x is valid or not
  • return 1 if true else 0
  • This function always performs the check regardless of the settings of mclBn_verifyOrderG1 and mclBn_verifyOrderG2.

Is equal / zero / one / isOdd

int mclBnFr_isEqual(const mclBnFr *x, const mclBnFr *y);
int mclBnFr_isZero(const mclBnFr *x);
int mclBnFr_isOne(const mclBnFr *x);
int mclBnFr_isOdd(const mclBnFr *x);

int mclBnFp_isEqual(const mclBnFp *x, const mclBnFp *y);
int mclBnFp_isZero(const mclBnFp *x);
int mclBnFp_isOne(const mclBnFp *x);
int mclBnFp_isOdd(const mclBnFp *x);

int mclBnFp2_isEqual(const mclBnFp2 *x, const mclBnFp2 *y);
int mclBnFp2_isZero(const mclBnFp2 *x);
int mclBnFp2_isOne(const mclBnFp2 *x);

int mclBnG1_isEqual(const mclBnG1 *x, const mclBnG1 *y);
int mclBnG1_isZero(const mclBnG1 *x);

int mclBnG2_isEqual(const mclBnG2 *x, const mclBnG2 *y);
int mclBnG2_isZero(const mclBnG2 *x);

int mclBnGT_isEqual(const mclBnGT *x, const mclBnGT *y);
int mclBnGT_isZero(const mclBnGT *x);
int mclBnGT_isOne(const mclBnGT *x);

C++

bool T::operator==(const T& rhs) const;
bool T::isZero() const;
bool T::isOne() const;
  • return 1 (true) if true else 0 (false)

isNegative

int mclBnFr_isNegative(const mclBnFr *x);
int mclBnFp_isNegative(const mclBnFp *x);

return 1 if x >= half where half = (r + 1) / 2 (resp. (p + 1) / 2).

Compare

int mclBnFr_cmp(const mclBnFr *x, const mclBnFr *y);
int mclBnFp_cmp(const mclBnFp *x, const mclBnFp *y);
  • compare x and y as unsigned integers
  • return -1 if x < y, 0 if x == y, 1 if x > y
  • NOTE: two Montgomery conversions may be required

Lagrange interpolation

int mclBn_FrLagrangeInterpolation(mclBnFr *out, const mclBnFr *xVec, const mclBnFr *yVec, mclSize k);
int mclBn_G1LagrangeInterpolation(mclBnG1 *out, const mclBnFr *xVec, const mclBnG1 *yVec, mclSize k);
int mclBn_G2LagrangeInterpolation(mclBnG2 *out, const mclBnFr *xVec, const mclBnG2 *yVec, mclSize k);
  • Lagrange interpolation
  • recover out = y(0) from {(xVec[i], yVec[i])} for {i=0..k-1}
  • return 0 if success else -1
    • requires that xVec[i] != 0 and xVec[i] != xVec[j] for i != j
int mclBn_FrEvaluatePolynomial(mclBnFr *out, const mclBnFr *cVec, mclSize cSize, const mclBnFr *x);
int mclBn_G1EvaluatePolynomial(mclBnG1 *out, const mclBnG1 *cVec, mclSize cSize, const mclBnFr *x);
int mclBn_G2EvaluatePolynomial(mclBnG2 *out, const mclBnG2 *cVec, mclSize cSize, const mclBnFr *x);
  • Evaluate polynomial
  • out = f(x) = c[0] + c[1] * x + ... + c[cSize - 1] * x^{cSize - 1}
  • return 0 if success else -1
    • requires cSize >= 1

FAQ

Why is the value set by Fp::setStr different?

The value set by Fp::setStr is masked and truncated if it is greater than p (resp. r). See Set string

What parameters of configuration are for Ethereum?

mcl supports various modes of the hash-to-curve function, serialize/deserialize, and getStr/setStr for historical reasons and backward compatibility.

If using BLS12-381 and Ethereum compatibility mode, set

// C++
Fp::setETHserialization(true);
Fr::setETHserialization(true);
bn::setMapToMode(MCL_MAP_TO_MODE_HASH_TO_CURVE);

or

// C
mclBn_setETHserialization(1);
mclBn_setMapToMode(MCL_MAP_TO_MODE_HASH_TO_CURVE);

and use

// C++
void Fp::setBigEndianMod(const uint8_t *x, size_t bufSize);
size_t T::serialize(void *buf, size_t maxBufSize) const
size_t T::deserialize(const void *buf, size_t bufSize);

or

// C
int mclBnFp_setBigEndianMod(mclBnFp *x, const void *buf, mclSize bufSize);
mclSize mclBnFp_serialize(void *buf, mclSize maxBufSize, const mclBnFp *x);
mclSize mclBnFp_deserialize(mclBnFp *x, const void *buf, mclSize bufSize);

Serialization of Fp/Fr

  • Fp
    • 48 bytes data in big-endian format
  • Fr
    • 32 bytes data in big-endian format
  • G1
    • zero : [0xc0 : (47 bytes zero)]
    • (x, y) : d = [48 bytes x], then d[0] |= 0x80, and d[0] |= 0x20 if y >= (p+1)/2.
  • G2
    • zero : [0xc0 : (95 bytes zero)]
    • (x, y) : d = [96 bytes x], then d[0] |= 0x80, and d[0] |= 0x20 if b >= (p+1)/2 where y = a + bi.

See Point Serialization Procedure for details.