UNPKG

@polygon-hermez/vm

Version:
207 lines (159 loc) 5.78 kB
import { padToEven, BN } from 'ethereumjs-util' import { VmError, ERROR } from '../../../exceptions' // base field modulus as described in the EIP const fieldModulus = new BN( '1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab', 16 ) // convert an input Buffer to a mcl G1 point // this does /NOT/ do any input checks. the input Buffer needs to be of length 128 // it does raise an error if the point is not on the curve. function BLS12_381_ToG1Point(input: Buffer, mcl: any): any { const p_x = input.slice(16, 64).toString('hex') const p_y = input.slice(80, 128).toString('hex') const ZeroString48Bytes = '0'.repeat(96) if (p_x == p_y && p_x == ZeroString48Bytes) { return new mcl.G1() } const Fp_X = new mcl.Fp() const Fp_Y = new mcl.Fp() const One = new mcl.Fp() Fp_X.setStr(p_x, 16) Fp_Y.setStr(p_y, 16) One.setStr('1', 16) const G1 = new mcl.G1() G1.setX(Fp_X) G1.setY(Fp_Y) G1.setZ(One) if (!G1.isValidOrder()) { throw new VmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) } // Check if these coordinates are actually on the curve. if (!G1.isValid()) { throw new VmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) } return G1 } // input: a mcl G1 point // output: a 128-byte Buffer function BLS12_381_FromG1Point(input: any): Buffer { // TODO: figure out if there is a better way to decode these values. const decodeStr = input.getStr(16) //return a string of pattern "1 <x_coord> <y_coord>" const decoded = decodeStr.match(/"?[0-9a-f]+"?/g) // match above pattern. if (decodeStr == '0') { return Buffer.alloc(128, 0) } // note: decoded[0] == 1 const xval = padToEven(decoded[1]) const yval = padToEven(decoded[2]) // convert to buffers. const xBuffer = Buffer.concat([Buffer.alloc(64 - xval.length / 2, 0), Buffer.from(xval, 'hex')]) const yBuffer = Buffer.concat([Buffer.alloc(64 - yval.length / 2, 0), Buffer.from(yval, 'hex')]) return Buffer.concat([xBuffer, yBuffer]) } // convert an input Buffer to a mcl G2 point // this does /NOT/ do any input checks. the input Buffer needs to be of length 256 function BLS12_381_ToG2Point(input: Buffer, mcl: any): any { const p_x_1 = input.slice(0, 64) const p_x_2 = input.slice(64, 128) const p_y_1 = input.slice(128, 192) const p_y_2 = input.slice(192, 256) const ZeroBytes64 = Buffer.alloc(64, 0) // check if we have to do with a zero point if ( p_x_1.equals(p_x_2) && p_x_1.equals(p_y_1) && p_x_1.equals(p_y_2) && p_x_1.equals(ZeroBytes64) ) { return new mcl.G2() } const Fp2X = BLS12_381_ToFp2Point(p_x_1, p_x_2, mcl) const Fp2Y = BLS12_381_ToFp2Point(p_y_1, p_y_2, mcl) const FpOne = new mcl.Fp() FpOne.setStr('1', 16) const FpZero = new mcl.Fp() FpZero.setStr('0', 16) const Fp2One = new mcl.Fp2() Fp2One.set_a(FpOne) Fp2One.set_b(FpZero) const mclPoint = new mcl.G2() mclPoint.setX(Fp2X) mclPoint.setY(Fp2Y) mclPoint.setZ(Fp2One) if (!mclPoint.isValidOrder()) { throw new VmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) } if (!mclPoint.isValid()) { throw new VmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE) } return mclPoint } // input: a mcl G2 point // output: a 256-byte Buffer function BLS12_381_FromG2Point(input: any): Buffer { // TODO: figure out if there is a better way to decode these values. const decodeStr = input.getStr(16) //return a string of pattern "1 <x_coord_1> <x_coord_2> <y_coord_1> <y_coord_2>" if (decodeStr == '0') { return Buffer.alloc(256, 0) } const decoded = decodeStr.match(/"?[0-9a-f]+"?/g) // match above pattern. // note: decoded[0] == 1 const x_1 = padToEven(decoded[1]) const x_2 = padToEven(decoded[2]) const y_1 = padToEven(decoded[3]) const y_2 = padToEven(decoded[4]) // convert to buffers. const xBuffer1 = Buffer.concat([Buffer.alloc(64 - x_1.length / 2, 0), Buffer.from(x_1, 'hex')]) const xBuffer2 = Buffer.concat([Buffer.alloc(64 - x_2.length / 2, 0), Buffer.from(x_2, 'hex')]) const yBuffer1 = Buffer.concat([Buffer.alloc(64 - y_1.length / 2, 0), Buffer.from(y_1, 'hex')]) const yBuffer2 = Buffer.concat([Buffer.alloc(64 - y_2.length / 2, 0), Buffer.from(y_2, 'hex')]) return Buffer.concat([xBuffer1, xBuffer2, yBuffer1, yBuffer2]) } // input: a 32-byte hex scalar Buffer // output: a mcl Fr point function BLS12_381_ToFrPoint(input: Buffer, mcl: any): any { const mclHex = mcl.fromHexStr(input.toString('hex')) const Fr = new mcl.Fr() Fr.setBigEndianMod(mclHex) return Fr } // input: a 64-byte buffer // output: a mcl Fp point function BLS12_381_ToFpPoint(fpCoordinate: Buffer, mcl: any): any { // check if point is in field if (new BN(fpCoordinate).gte(fieldModulus)) { throw new VmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) } const fp = new mcl.Fp() fp.setBigEndianMod(mcl.fromHexStr(fpCoordinate.toString('hex'))) return fp } // input: two 64-byte buffers // output: a mcl Fp2 point function BLS12_381_ToFp2Point(fpXCoordinate: Buffer, fpYCoordinate: Buffer, mcl: any): any { // check if the coordinates are in the field if (new BN(fpXCoordinate).gte(fieldModulus)) { throw new VmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) } if (new BN(fpYCoordinate).gte(fieldModulus)) { throw new VmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) } const fp_x = new mcl.Fp() const fp_y = new mcl.Fp() const fp2 = new mcl.Fp2() fp_x.setStr(fpXCoordinate.slice(16).toString('hex'), 16) fp_y.setStr(fpYCoordinate.slice(16).toString('hex'), 16) fp2.set_a(fp_x) fp2.set_b(fp_y) return fp2 } export { BLS12_381_ToG1Point, BLS12_381_FromG1Point, BLS12_381_ToG2Point, BLS12_381_FromG2Point, BLS12_381_ToFrPoint, BLS12_381_ToFpPoint, BLS12_381_ToFp2Point, }