From 0fd8140a6127e77c228bc1beb0b9374244478dcd Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 28 Oct 2024 21:12:30 -0500 Subject: [PATCH] Re-add kilic BLS12-381 library --- crypto/bls12381/arithmetic_decl.go | 84 + crypto/bls12381/arithmetic_fallback.go | 567 ++++++ crypto/bls12381/arithmetic_x86.s | 2150 +++++++++++++++++++++++ crypto/bls12381/arithmetic_x86_adx.go | 25 + crypto/bls12381/arithmetic_x86_noadx.go | 25 + crypto/bls12381/bls12_381.go | 230 +++ crypto/bls12381/bls12_381_test.go | 13 + crypto/bls12381/field_element.go | 340 ++++ crypto/bls12381/field_element_test.go | 250 +++ crypto/bls12381/fp.go | 167 ++ crypto/bls12381/fp12.go | 277 +++ crypto/bls12381/fp2.go | 252 +++ crypto/bls12381/fp6.go | 351 ++++ crypto/bls12381/fp_test.go | 1411 +++++++++++++++ crypto/bls12381/g1.go | 434 +++++ crypto/bls12381/g1_test.go | 284 +++ crypto/bls12381/g2.go | 455 +++++ crypto/bls12381/g2_test.go | 287 +++ crypto/bls12381/gt.go | 121 ++ crypto/bls12381/isogeny.go | 227 +++ crypto/bls12381/pairing.go | 282 +++ crypto/bls12381/pairing_test.go | 230 +++ crypto/bls12381/swu.go | 158 ++ crypto/bls12381/utils.go | 45 + 24 files changed, 8665 insertions(+) create mode 100644 crypto/bls12381/arithmetic_decl.go create mode 100644 crypto/bls12381/arithmetic_fallback.go create mode 100644 crypto/bls12381/arithmetic_x86.s create mode 100644 crypto/bls12381/arithmetic_x86_adx.go create mode 100644 crypto/bls12381/arithmetic_x86_noadx.go create mode 100644 crypto/bls12381/bls12_381.go create mode 100644 crypto/bls12381/bls12_381_test.go create mode 100644 crypto/bls12381/field_element.go create mode 100644 crypto/bls12381/field_element_test.go create mode 100644 crypto/bls12381/fp.go create mode 100644 crypto/bls12381/fp12.go create mode 100644 crypto/bls12381/fp2.go create mode 100644 crypto/bls12381/fp6.go create mode 100644 crypto/bls12381/fp_test.go create mode 100644 crypto/bls12381/g1.go create mode 100644 crypto/bls12381/g1_test.go create mode 100644 crypto/bls12381/g2.go create mode 100644 crypto/bls12381/g2_test.go create mode 100644 crypto/bls12381/gt.go create mode 100644 crypto/bls12381/isogeny.go create mode 100644 crypto/bls12381/pairing.go create mode 100644 crypto/bls12381/pairing_test.go create mode 100644 crypto/bls12381/swu.go create mode 100644 crypto/bls12381/utils.go diff --git a/crypto/bls12381/arithmetic_decl.go b/crypto/bls12381/arithmetic_decl.go new file mode 100644 index 0000000000..f6d232d658 --- /dev/null +++ b/crypto/bls12381/arithmetic_decl.go @@ -0,0 +1,84 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build (amd64 && blsasm) || (amd64 && blsadx) +// +build amd64,blsasm amd64,blsadx + +package bls12381 + +import ( + "golang.org/x/sys/cpu" +) + +func init() { + if !enableADX || !cpu.X86.HasADX || !cpu.X86.HasBMI2 { + mul = mulNoADX + } +} + +// Use ADX backend for default +var mul func(c, a, b *fe) = mulADX + +func square(c, a *fe) { + mul(c, a, a) +} + +func neg(c, a *fe) { + if a.isZero() { + c.set(a) + } else { + _neg(c, a) + } +} + +//go:noescape +func add(c, a, b *fe) + +//go:noescape +func addAssign(a, b *fe) + +//go:noescape +func ladd(c, a, b *fe) + +//go:noescape +func laddAssign(a, b *fe) + +//go:noescape +func double(c, a *fe) + +//go:noescape +func doubleAssign(a *fe) + +//go:noescape +func ldouble(c, a *fe) + +//go:noescape +func sub(c, a, b *fe) + +//go:noescape +func subAssign(a, b *fe) + +//go:noescape +func lsubAssign(a, b *fe) + +//go:noescape +func _neg(c, a *fe) + +//go:noescape +func mulNoADX(c, a, b *fe) + +//go:noescape +func mulADX(c, a, b *fe) diff --git a/crypto/bls12381/arithmetic_fallback.go b/crypto/bls12381/arithmetic_fallback.go new file mode 100644 index 0000000000..c09ae0d91c --- /dev/null +++ b/crypto/bls12381/arithmetic_fallback.go @@ -0,0 +1,567 @@ +// Native go field arithmetic code is generated with 'goff' +// https://github.com/ConsenSys/goff +// Many function signature of field operations are renamed. + +// Copyright 2020 ConsenSys AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// field modulus q = +// +// 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +// Code generated by goff DO NOT EDIT +// goff version: v0.1.0 - build: 790f1f56eac432441e043abff8819eacddd1d668 +// fe are assumed to be in Montgomery form in all methods + +// /!\ WARNING /!\ +// this code has not been audited and is provided as-is. In particular, +// there is no security guarantees such as constant time implementation +// or side-channel attack resistance +// /!\ WARNING /!\ + +// Package bls (generated by goff) contains field arithmetics operations + +//go:build !amd64 || (!blsasm && !blsadx) +// +build !amd64 !blsasm,!blsadx + +package bls12381 + +import ( + "math/bits" +) + +func add(z, x, y *fe) { + var carry uint64 + + z[0], carry = bits.Add64(x[0], y[0], 0) + z[1], carry = bits.Add64(x[1], y[1], carry) + z[2], carry = bits.Add64(x[2], y[2], carry) + z[3], carry = bits.Add64(x[3], y[3], carry) + z[4], carry = bits.Add64(x[4], y[4], carry) + z[5], _ = bits.Add64(x[5], y[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func addAssign(x, y *fe) { + var carry uint64 + + x[0], carry = bits.Add64(x[0], y[0], 0) + x[1], carry = bits.Add64(x[1], y[1], carry) + x[2], carry = bits.Add64(x[2], y[2], carry) + x[3], carry = bits.Add64(x[3], y[3], carry) + x[4], carry = bits.Add64(x[4], y[4], carry) + x[5], _ = bits.Add64(x[5], y[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(x[5] < 1873798617647539866 || (x[5] == 1873798617647539866 && (x[4] < 5412103778470702295 || (x[4] == 5412103778470702295 && (x[3] < 7239337960414712511 || (x[3] == 7239337960414712511 && (x[2] < 7435674573564081700 || (x[2] == 7435674573564081700 && (x[1] < 2210141511517208575 || (x[1] == 2210141511517208575 && (x[0] < 13402431016077863595))))))))))) { + var b uint64 + x[0], b = bits.Sub64(x[0], 13402431016077863595, 0) + x[1], b = bits.Sub64(x[1], 2210141511517208575, b) + x[2], b = bits.Sub64(x[2], 7435674573564081700, b) + x[3], b = bits.Sub64(x[3], 7239337960414712511, b) + x[4], b = bits.Sub64(x[4], 5412103778470702295, b) + x[5], _ = bits.Sub64(x[5], 1873798617647539866, b) + } +} + +func ladd(z, x, y *fe) { + var carry uint64 + z[0], carry = bits.Add64(x[0], y[0], 0) + z[1], carry = bits.Add64(x[1], y[1], carry) + z[2], carry = bits.Add64(x[2], y[2], carry) + z[3], carry = bits.Add64(x[3], y[3], carry) + z[4], carry = bits.Add64(x[4], y[4], carry) + z[5], _ = bits.Add64(x[5], y[5], carry) +} + +func laddAssign(x, y *fe) { + var carry uint64 + x[0], carry = bits.Add64(x[0], y[0], 0) + x[1], carry = bits.Add64(x[1], y[1], carry) + x[2], carry = bits.Add64(x[2], y[2], carry) + x[3], carry = bits.Add64(x[3], y[3], carry) + x[4], carry = bits.Add64(x[4], y[4], carry) + x[5], _ = bits.Add64(x[5], y[5], carry) +} + +func double(z, x *fe) { + var carry uint64 + + z[0], carry = bits.Add64(x[0], x[0], 0) + z[1], carry = bits.Add64(x[1], x[1], carry) + z[2], carry = bits.Add64(x[2], x[2], carry) + z[3], carry = bits.Add64(x[3], x[3], carry) + z[4], carry = bits.Add64(x[4], x[4], carry) + z[5], _ = bits.Add64(x[5], x[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func doubleAssign(z *fe) { + var carry uint64 + + z[0], carry = bits.Add64(z[0], z[0], 0) + z[1], carry = bits.Add64(z[1], z[1], carry) + z[2], carry = bits.Add64(z[2], z[2], carry) + z[3], carry = bits.Add64(z[3], z[3], carry) + z[4], carry = bits.Add64(z[4], z[4], carry) + z[5], _ = bits.Add64(z[5], z[5], carry) + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func ldouble(z, x *fe) { + var carry uint64 + + z[0], carry = bits.Add64(x[0], x[0], 0) + z[1], carry = bits.Add64(x[1], x[1], carry) + z[2], carry = bits.Add64(x[2], x[2], carry) + z[3], carry = bits.Add64(x[3], x[3], carry) + z[4], carry = bits.Add64(x[4], x[4], carry) + z[5], _ = bits.Add64(x[5], x[5], carry) +} + +func sub(z, x, y *fe) { + var b uint64 + z[0], b = bits.Sub64(x[0], y[0], 0) + z[1], b = bits.Sub64(x[1], y[1], b) + z[2], b = bits.Sub64(x[2], y[2], b) + z[3], b = bits.Sub64(x[3], y[3], b) + z[4], b = bits.Sub64(x[4], y[4], b) + z[5], b = bits.Sub64(x[5], y[5], b) + if b != 0 { + var c uint64 + z[0], c = bits.Add64(z[0], 13402431016077863595, 0) + z[1], c = bits.Add64(z[1], 2210141511517208575, c) + z[2], c = bits.Add64(z[2], 7435674573564081700, c) + z[3], c = bits.Add64(z[3], 7239337960414712511, c) + z[4], c = bits.Add64(z[4], 5412103778470702295, c) + z[5], _ = bits.Add64(z[5], 1873798617647539866, c) + } +} + +func subAssign(z, x *fe) { + var b uint64 + z[0], b = bits.Sub64(z[0], x[0], 0) + z[1], b = bits.Sub64(z[1], x[1], b) + z[2], b = bits.Sub64(z[2], x[2], b) + z[3], b = bits.Sub64(z[3], x[3], b) + z[4], b = bits.Sub64(z[4], x[4], b) + z[5], b = bits.Sub64(z[5], x[5], b) + if b != 0 { + var c uint64 + z[0], c = bits.Add64(z[0], 13402431016077863595, 0) + z[1], c = bits.Add64(z[1], 2210141511517208575, c) + z[2], c = bits.Add64(z[2], 7435674573564081700, c) + z[3], c = bits.Add64(z[3], 7239337960414712511, c) + z[4], c = bits.Add64(z[4], 5412103778470702295, c) + z[5], _ = bits.Add64(z[5], 1873798617647539866, c) + } +} + +func lsubAssign(z, x *fe) { + var b uint64 + z[0], b = bits.Sub64(z[0], x[0], 0) + z[1], b = bits.Sub64(z[1], x[1], b) + z[2], b = bits.Sub64(z[2], x[2], b) + z[3], b = bits.Sub64(z[3], x[3], b) + z[4], b = bits.Sub64(z[4], x[4], b) + z[5], _ = bits.Sub64(z[5], x[5], b) +} + +func neg(z *fe, x *fe) { + if x.isZero() { + z.zero() + return + } + var borrow uint64 + z[0], borrow = bits.Sub64(13402431016077863595, x[0], 0) + z[1], borrow = bits.Sub64(2210141511517208575, x[1], borrow) + z[2], borrow = bits.Sub64(7435674573564081700, x[2], borrow) + z[3], borrow = bits.Sub64(7239337960414712511, x[3], borrow) + z[4], borrow = bits.Sub64(5412103778470702295, x[4], borrow) + z[5], _ = bits.Sub64(1873798617647539866, x[5], borrow) +} + +func mul(z, x, y *fe) { + var t [6]uint64 + var c [3]uint64 + { + // round 0 + v := x[0] + c[1], c[0] = bits.Mul64(v, y[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd1(v, y[1], c[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd1(v, y[2], c[1]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd1(v, y[3], c[1]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd1(v, y[4], c[1]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd1(v, y[5], c[1]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 1 + v := x[1] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 2 + v := x[2] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 3 + v := x[3] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 4 + v := x[4] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], t[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], t[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], t[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], t[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + t[5], t[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + { + // round 5 + v := x[5] + c[1], c[0] = madd1(v, y[0], t[0]) + m := c[0] * 9940570264628428797 + c[2] = madd0(m, 13402431016077863595, c[0]) + c[1], c[0] = madd2(v, y[1], c[1], t[1]) + c[2], z[0] = madd2(m, 2210141511517208575, c[2], c[0]) + c[1], c[0] = madd2(v, y[2], c[1], t[2]) + c[2], z[1] = madd2(m, 7435674573564081700, c[2], c[0]) + c[1], c[0] = madd2(v, y[3], c[1], t[3]) + c[2], z[2] = madd2(m, 7239337960414712511, c[2], c[0]) + c[1], c[0] = madd2(v, y[4], c[1], t[4]) + c[2], z[3] = madd2(m, 5412103778470702295, c[2], c[0]) + c[1], c[0] = madd2(v, y[5], c[1], t[5]) + z[5], z[4] = madd3(m, 1873798617647539866, c[0], c[2], c[1]) + } + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +func square(z, x *fe) { + + var p [6]uint64 + + var u, v uint64 + { + // round 0 + u, p[0] = bits.Mul64(x[0], x[0]) + m := p[0] * 9940570264628428797 + C := madd0(m, 13402431016077863595, p[0]) + var t uint64 + t, u, v = madd1sb(x[0], x[1], u) + C, p[0] = madd2(m, 2210141511517208575, v, C) + t, u, v = madd1s(x[0], x[2], t, u) + C, p[1] = madd2(m, 7435674573564081700, v, C) + t, u, v = madd1s(x[0], x[3], t, u) + C, p[2] = madd2(m, 7239337960414712511, v, C) + t, u, v = madd1s(x[0], x[4], t, u) + C, p[3] = madd2(m, 5412103778470702295, v, C) + _, u, v = madd1s(x[0], x[5], t, u) + p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) + } + { + // round 1 + m := p[0] * 9940570264628428797 + C := madd0(m, 13402431016077863595, p[0]) + u, v = madd1(x[1], x[1], p[1]) + C, p[0] = madd2(m, 2210141511517208575, v, C) + var t uint64 + t, u, v = madd2sb(x[1], x[2], p[2], u) + C, p[1] = madd2(m, 7435674573564081700, v, C) + t, u, v = madd2s(x[1], x[3], p[3], t, u) + C, p[2] = madd2(m, 7239337960414712511, v, C) + t, u, v = madd2s(x[1], x[4], p[4], t, u) + C, p[3] = madd2(m, 5412103778470702295, v, C) + _, u, v = madd2s(x[1], x[5], p[5], t, u) + p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) + } + { + // round 2 + m := p[0] * 9940570264628428797 + C := madd0(m, 13402431016077863595, p[0]) + C, p[0] = madd2(m, 2210141511517208575, p[1], C) + u, v = madd1(x[2], x[2], p[2]) + C, p[1] = madd2(m, 7435674573564081700, v, C) + var t uint64 + t, u, v = madd2sb(x[2], x[3], p[3], u) + C, p[2] = madd2(m, 7239337960414712511, v, C) + t, u, v = madd2s(x[2], x[4], p[4], t, u) + C, p[3] = madd2(m, 5412103778470702295, v, C) + _, u, v = madd2s(x[2], x[5], p[5], t, u) + p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) + } + { + // round 3 + m := p[0] * 9940570264628428797 + C := madd0(m, 13402431016077863595, p[0]) + C, p[0] = madd2(m, 2210141511517208575, p[1], C) + C, p[1] = madd2(m, 7435674573564081700, p[2], C) + u, v = madd1(x[3], x[3], p[3]) + C, p[2] = madd2(m, 7239337960414712511, v, C) + var t uint64 + t, u, v = madd2sb(x[3], x[4], p[4], u) + C, p[3] = madd2(m, 5412103778470702295, v, C) + _, u, v = madd2s(x[3], x[5], p[5], t, u) + p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) + } + { + // round 4 + m := p[0] * 9940570264628428797 + C := madd0(m, 13402431016077863595, p[0]) + C, p[0] = madd2(m, 2210141511517208575, p[1], C) + C, p[1] = madd2(m, 7435674573564081700, p[2], C) + C, p[2] = madd2(m, 7239337960414712511, p[3], C) + u, v = madd1(x[4], x[4], p[4]) + C, p[3] = madd2(m, 5412103778470702295, v, C) + _, u, v = madd2sb(x[4], x[5], p[5], u) + p[5], p[4] = madd3(m, 1873798617647539866, v, C, u) + } + { + // round 5 + m := p[0] * 9940570264628428797 + C := madd0(m, 13402431016077863595, p[0]) + C, z[0] = madd2(m, 2210141511517208575, p[1], C) + C, z[1] = madd2(m, 7435674573564081700, p[2], C) + C, z[2] = madd2(m, 7239337960414712511, p[3], C) + C, z[3] = madd2(m, 5412103778470702295, p[4], C) + u, v = madd1(x[5], x[5], p[5]) + z[5], z[4] = madd3(m, 1873798617647539866, v, C, u) + } + + // if z > q --> z -= q + // note: this is NOT constant time + if !(z[5] < 1873798617647539866 || (z[5] == 1873798617647539866 && (z[4] < 5412103778470702295 || (z[4] == 5412103778470702295 && (z[3] < 7239337960414712511 || (z[3] == 7239337960414712511 && (z[2] < 7435674573564081700 || (z[2] == 7435674573564081700 && (z[1] < 2210141511517208575 || (z[1] == 2210141511517208575 && (z[0] < 13402431016077863595))))))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 13402431016077863595, 0) + z[1], b = bits.Sub64(z[1], 2210141511517208575, b) + z[2], b = bits.Sub64(z[2], 7435674573564081700, b) + z[3], b = bits.Sub64(z[3], 7239337960414712511, b) + z[4], b = bits.Sub64(z[4], 5412103778470702295, b) + z[5], _ = bits.Sub64(z[5], 1873798617647539866, b) + } +} + +// arith.go +// Copyright 2020 ConsenSys AG +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by goff DO NOT EDIT + +func madd(a, b, t, u, v uint64) (uint64, uint64, uint64) { + var carry uint64 + hi, lo := bits.Mul64(a, b) + v, carry = bits.Add64(lo, v, 0) + u, carry = bits.Add64(hi, u, carry) + t, _ = bits.Add64(t, 0, carry) + return t, u, v +} + +// madd0 hi = a*b + c (discards lo bits) +func madd0(a, b, c uint64) (hi uint64) { + var carry, lo uint64 + hi, lo = bits.Mul64(a, b) + _, carry = bits.Add64(lo, c, 0) + hi, _ = bits.Add64(hi, 0, carry) + return +} + +// madd1 hi, lo = a*b + c +func madd1(a, b, c uint64) (hi uint64, lo uint64) { + var carry uint64 + hi, lo = bits.Mul64(a, b) + lo, carry = bits.Add64(lo, c, 0) + hi, _ = bits.Add64(hi, 0, carry) + return +} + +// madd2 hi, lo = a*b + c + d +func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { + var carry uint64 + hi, lo = bits.Mul64(a, b) + c, carry = bits.Add64(c, d, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, c, 0) + hi, _ = bits.Add64(hi, 0, carry) + return +} + +// madd2s superhi, hi, lo = 2*a*b + c + d + e +func madd2s(a, b, c, d, e uint64) (superhi, hi, lo uint64) { + var carry, sum uint64 + + hi, lo = bits.Mul64(a, b) + lo, carry = bits.Add64(lo, lo, 0) + hi, superhi = bits.Add64(hi, hi, carry) + + sum, carry = bits.Add64(c, e, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, sum, 0) + hi, _ = bits.Add64(hi, 0, carry) + hi, _ = bits.Add64(hi, 0, d) + return +} + +func madd1s(a, b, d, e uint64) (superhi, hi, lo uint64) { + var carry uint64 + + hi, lo = bits.Mul64(a, b) + lo, carry = bits.Add64(lo, lo, 0) + hi, superhi = bits.Add64(hi, hi, carry) + lo, carry = bits.Add64(lo, e, 0) + hi, _ = bits.Add64(hi, 0, carry) + hi, _ = bits.Add64(hi, 0, d) + return +} + +func madd2sb(a, b, c, e uint64) (superhi, hi, lo uint64) { + var carry, sum uint64 + + hi, lo = bits.Mul64(a, b) + lo, carry = bits.Add64(lo, lo, 0) + hi, superhi = bits.Add64(hi, hi, carry) + + sum, carry = bits.Add64(c, e, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, sum, 0) + hi, _ = bits.Add64(hi, 0, carry) + return +} + +func madd1sb(a, b, e uint64) (superhi, hi, lo uint64) { + var carry uint64 + + hi, lo = bits.Mul64(a, b) + lo, carry = bits.Add64(lo, lo, 0) + hi, superhi = bits.Add64(hi, hi, carry) + lo, carry = bits.Add64(lo, e, 0) + hi, _ = bits.Add64(hi, 0, carry) + return +} + +func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { + var carry uint64 + hi, lo = bits.Mul64(a, b) + c, carry = bits.Add64(c, d, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, c, 0) + hi, _ = bits.Add64(hi, e, carry) + return +} diff --git a/crypto/bls12381/arithmetic_x86.s b/crypto/bls12381/arithmetic_x86.s new file mode 100644 index 0000000000..2cebbc46f7 --- /dev/null +++ b/crypto/bls12381/arithmetic_x86.s @@ -0,0 +1,2150 @@ +// +build amd64,blsasm amd64,blsadx + +#include "textflag.h" + +// addition w/ modular reduction +// a = (a + b) % p +TEXT ·addAssign(SB), NOSPLIT, $0-16 + // | + MOVQ a+0(FP), DI + MOVQ b+8(FP), SI + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + + // | + ADDQ (SI), R8 + ADCQ 8(SI), R9 + ADCQ 16(SI), R10 + ADCQ 24(SI), R11 + ADCQ 32(SI), R12 + ADCQ 40(SI), R13 + + // | + MOVQ R8, R14 + MOVQ R9, R15 + MOVQ R10, CX + MOVQ R11, DX + MOVQ R12, SI + MOVQ R13, BX + MOVQ $0xb9feffffffffaaab, AX + SUBQ AX, R14 + MOVQ $0x1eabfffeb153ffff, AX + SBBQ AX, R15 + MOVQ $0x6730d2a0f6b0f624, AX + SBBQ AX, CX + MOVQ $0x64774b84f38512bf, AX + SBBQ AX, DX + MOVQ $0x4b1ba7b6434bacd7, AX + SBBQ AX, SI + MOVQ $0x1a0111ea397fe69a, AX + SBBQ AX, BX + CMOVQCC R14, R8 + CMOVQCC R15, R9 + CMOVQCC CX, R10 + CMOVQCC DX, R11 + CMOVQCC SI, R12 + CMOVQCC BX, R13 + + // | + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET + +/* | end */ + + +// addition w/ modular reduction +// c = (a + b) % p +TEXT ·add(SB), NOSPLIT, $0-24 + // | + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + + // | + ADDQ (SI), R8 + ADCQ 8(SI), R9 + ADCQ 16(SI), R10 + ADCQ 24(SI), R11 + ADCQ 32(SI), R12 + ADCQ 40(SI), R13 + + // | + MOVQ R8, R14 + MOVQ R9, R15 + MOVQ R10, CX + MOVQ R11, DX + MOVQ R12, SI + MOVQ R13, BX + MOVQ $0xb9feffffffffaaab, DI + SUBQ DI, R14 + MOVQ $0x1eabfffeb153ffff, DI + SBBQ DI, R15 + MOVQ $0x6730d2a0f6b0f624, DI + SBBQ DI, CX + MOVQ $0x64774b84f38512bf, DI + SBBQ DI, DX + MOVQ $0x4b1ba7b6434bacd7, DI + SBBQ DI, SI + MOVQ $0x1a0111ea397fe69a, DI + SBBQ DI, BX + CMOVQCC R14, R8 + CMOVQCC R15, R9 + CMOVQCC CX, R10 + CMOVQCC DX, R11 + CMOVQCC SI, R12 + CMOVQCC BX, R13 + + // | + MOVQ c+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// addition w/o reduction check +// c = (a + b) +TEXT ·ladd(SB), NOSPLIT, $0-24 + // | + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + + // | + ADDQ (SI), R8 + ADCQ 8(SI), R9 + ADCQ 16(SI), R10 + ADCQ 24(SI), R11 + ADCQ 32(SI), R12 + ADCQ 40(SI), R13 + + // | + MOVQ c+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// addition w/o reduction check +// a = a + b +TEXT ·laddAssign(SB), NOSPLIT, $0-16 + // | + MOVQ a+0(FP), DI + MOVQ b+8(FP), SI + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + + // | + ADDQ (SI), R8 + ADCQ 8(SI), R9 + ADCQ 16(SI), R10 + ADCQ 24(SI), R11 + ADCQ 32(SI), R12 + ADCQ 40(SI), R13 + + // | + MOVQ a+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// subtraction w/ modular reduction +// c = (a - b) % p +TEXT ·sub(SB), NOSPLIT, $0-24 + // | + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + XORQ AX, AX + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + SUBQ (SI), R8 + SBBQ 8(SI), R9 + SBBQ 16(SI), R10 + SBBQ 24(SI), R11 + SBBQ 32(SI), R12 + SBBQ 40(SI), R13 + + // | + MOVQ $0xb9feffffffffaaab, R14 + MOVQ $0x1eabfffeb153ffff, R15 + MOVQ $0x6730d2a0f6b0f624, CX + MOVQ $0x64774b84f38512bf, DX + MOVQ $0x4b1ba7b6434bacd7, SI + MOVQ $0x1a0111ea397fe69a, BX + CMOVQCC AX, R14 + CMOVQCC AX, R15 + CMOVQCC AX, CX + CMOVQCC AX, DX + CMOVQCC AX, SI + CMOVQCC AX, BX + ADDQ R14, R8 + ADCQ R15, R9 + ADCQ CX, R10 + ADCQ DX, R11 + ADCQ SI, R12 + ADCQ BX, R13 + + // | + MOVQ c+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// subtraction w/ modular reduction +// a = (a - b) % p +TEXT ·subAssign(SB), NOSPLIT, $0-16 + // | + MOVQ a+0(FP), DI + MOVQ b+8(FP), SI + XORQ AX, AX + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + SUBQ (SI), R8 + SBBQ 8(SI), R9 + SBBQ 16(SI), R10 + SBBQ 24(SI), R11 + SBBQ 32(SI), R12 + SBBQ 40(SI), R13 + + // | + MOVQ $0xb9feffffffffaaab, R14 + MOVQ $0x1eabfffeb153ffff, R15 + MOVQ $0x6730d2a0f6b0f624, CX + MOVQ $0x64774b84f38512bf, DX + MOVQ $0x4b1ba7b6434bacd7, SI + MOVQ $0x1a0111ea397fe69a, BX + CMOVQCC AX, R14 + CMOVQCC AX, R15 + CMOVQCC AX, CX + CMOVQCC AX, DX + CMOVQCC AX, SI + CMOVQCC AX, BX + ADDQ R14, R8 + ADCQ R15, R9 + ADCQ CX, R10 + ADCQ DX, R11 + ADCQ SI, R12 + ADCQ BX, R13 + + // | + MOVQ a+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// subtraction w/o reduction check +// a = (a - b) +TEXT ·lsubAssign(SB), NOSPLIT, $0-16 + // | + MOVQ a+0(FP), DI + MOVQ b+8(FP), SI + + // | + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + SUBQ (SI), R8 + SBBQ 8(SI), R9 + SBBQ 16(SI), R10 + SBBQ 24(SI), R11 + SBBQ 32(SI), R12 + SBBQ 40(SI), R13 + + // | + MOVQ a+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + +// doubling w/ reduction +// c = (2 * a) % p +TEXT ·double(SB), NOSPLIT, $0-16 + // | + MOVQ a+8(FP), DI + + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + ADDQ R8, R8 + ADCQ R9, R9 + ADCQ R10, R10 + ADCQ R11, R11 + ADCQ R12, R12 + ADCQ R13, R13 + + // | + MOVQ R8, R14 + MOVQ R9, R15 + MOVQ R10, CX + MOVQ R11, DX + MOVQ R12, SI + MOVQ R13, BX + MOVQ $0xb9feffffffffaaab, DI + SUBQ DI, R14 + MOVQ $0x1eabfffeb153ffff, DI + SBBQ DI, R15 + MOVQ $0x6730d2a0f6b0f624, DI + SBBQ DI, CX + MOVQ $0x64774b84f38512bf, DI + SBBQ DI, DX + MOVQ $0x4b1ba7b6434bacd7, DI + SBBQ DI, SI + MOVQ $0x1a0111ea397fe69a, DI + SBBQ DI, BX + CMOVQCC R14, R8 + CMOVQCC R15, R9 + CMOVQCC CX, R10 + CMOVQCC DX, R11 + CMOVQCC SI, R12 + CMOVQCC BX, R13 + + // | + MOVQ c+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// doubling w/ reduction +// a = (2 * a) % p +TEXT ·doubleAssign(SB), NOSPLIT, $0-8 + // | + MOVQ a+0(FP), DI + + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + ADDQ R8, R8 + ADCQ R9, R9 + ADCQ R10, R10 + ADCQ R11, R11 + ADCQ R12, R12 + ADCQ R13, R13 + + // | + MOVQ R8, R14 + MOVQ R9, R15 + MOVQ R10, CX + MOVQ R11, DX + MOVQ R12, SI + MOVQ R13, BX + MOVQ $0xb9feffffffffaaab, AX + SUBQ AX, R14 + MOVQ $0x1eabfffeb153ffff, AX + SBBQ AX, R15 + MOVQ $0x6730d2a0f6b0f624, AX + SBBQ AX, CX + MOVQ $0x64774b84f38512bf, AX + SBBQ AX, DX + MOVQ $0x4b1ba7b6434bacd7, AX + SBBQ AX, SI + MOVQ $0x1a0111ea397fe69a, AX + SBBQ AX, BX + CMOVQCC R14, R8 + CMOVQCC R15, R9 + CMOVQCC CX, R10 + CMOVQCC DX, R11 + CMOVQCC SI, R12 + CMOVQCC BX, R13 + + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// doubling w/o reduction +// c = 2 * a +TEXT ·ldouble(SB), NOSPLIT, $0-16 + // | + MOVQ a+8(FP), DI + + MOVQ (DI), R8 + MOVQ 8(DI), R9 + MOVQ 16(DI), R10 + MOVQ 24(DI), R11 + MOVQ 32(DI), R12 + MOVQ 40(DI), R13 + + // | + ADDQ R8, R8 + ADCQ R9, R9 + ADCQ R10, R10 + ADCQ R11, R11 + ADCQ R12, R12 + ADCQ R13, R13 + + // | + MOVQ c+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + + RET +/* | end */ + + +TEXT ·_neg(SB), NOSPLIT, $0-16 + // | + MOVQ a+8(FP), DI + + // | + MOVQ $0xb9feffffffffaaab, R8 + MOVQ $0x1eabfffeb153ffff, R9 + MOVQ $0x6730d2a0f6b0f624, R10 + MOVQ $0x64774b84f38512bf, R11 + MOVQ $0x4b1ba7b6434bacd7, R12 + MOVQ $0x1a0111ea397fe69a, R13 + SUBQ (DI), R8 + SBBQ 8(DI), R9 + SBBQ 16(DI), R10 + SBBQ 24(DI), R11 + SBBQ 32(DI), R12 + SBBQ 40(DI), R13 + + // | + MOVQ c+0(FP), DI + MOVQ R8, (DI) + MOVQ R9, 8(DI) + MOVQ R10, 16(DI) + MOVQ R11, 24(DI) + MOVQ R12, 32(DI) + MOVQ R13, 40(DI) + RET +/* | end */ + + +// multiplication without using MULX/ADX +// c = a * b % p +TEXT ·mulNoADX(SB), NOSPLIT, $24-24 + // | + +/* inputs */ + + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + MOVQ $0x00, R9 + MOVQ $0x00, R10 + MOVQ $0x00, R11 + MOVQ $0x00, R12 + MOVQ $0x00, R13 + MOVQ $0x00, R14 + MOVQ $0x00, R15 + + // | + +/* i0 */ + + // | a0 @ CX + MOVQ (DI), CX + + // | a0 * b0 + MOVQ (SI), AX + MULQ CX + MOVQ AX, (SP) + MOVQ DX, R8 + + // | a0 * b1 + MOVQ 8(SI), AX + MULQ CX + ADDQ AX, R8 + ADCQ DX, R9 + + // | a0 * b2 + MOVQ 16(SI), AX + MULQ CX + ADDQ AX, R9 + ADCQ DX, R10 + + // | a0 * b3 + MOVQ 24(SI), AX + MULQ CX + ADDQ AX, R10 + ADCQ DX, R11 + + // | a0 * b4 + MOVQ 32(SI), AX + MULQ CX + ADDQ AX, R11 + ADCQ DX, R12 + + // | a0 * b5 + MOVQ 40(SI), AX + MULQ CX + ADDQ AX, R12 + ADCQ DX, R13 + + // | + +/* i1 */ + + // | a1 @ CX + MOVQ 8(DI), CX + MOVQ $0x00, BX + + // | a1 * b0 + MOVQ (SI), AX + MULQ CX + ADDQ AX, R8 + ADCQ DX, R9 + ADCQ $0x00, R10 + ADCQ $0x00, BX + MOVQ R8, 8(SP) + MOVQ $0x00, R8 + + // | a1 * b1 + MOVQ 8(SI), AX + MULQ CX + ADDQ AX, R9 + ADCQ DX, R10 + ADCQ BX, R11 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a1 * b2 + MOVQ 16(SI), AX + MULQ CX + ADDQ AX, R10 + ADCQ DX, R11 + ADCQ BX, R12 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a1 * b3 + MOVQ 24(SI), AX + MULQ CX + ADDQ AX, R11 + ADCQ DX, R12 + ADCQ BX, R13 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a1 * b4 + MOVQ 32(SI), AX + MULQ CX + ADDQ AX, R12 + ADCQ DX, R13 + ADCQ BX, R14 + + // | a1 * b5 + MOVQ 40(SI), AX + MULQ CX + ADDQ AX, R13 + ADCQ DX, R14 + + // | + +/* i2 */ + + // | a2 @ CX + MOVQ 16(DI), CX + MOVQ $0x00, BX + + // | a2 * b0 + MOVQ (SI), AX + MULQ CX + ADDQ AX, R9 + ADCQ DX, R10 + ADCQ $0x00, R11 + ADCQ $0x00, BX + MOVQ R9, 16(SP) + MOVQ $0x00, R9 + + // | a2 * b1 + MOVQ 8(SI), AX + MULQ CX + ADDQ AX, R10 + ADCQ DX, R11 + ADCQ BX, R12 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a2 * b2 + MOVQ 16(SI), AX + MULQ CX + ADDQ AX, R11 + ADCQ DX, R12 + ADCQ BX, R13 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a2 * b3 + MOVQ 24(SI), AX + MULQ CX + ADDQ AX, R12 + ADCQ DX, R13 + ADCQ BX, R14 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a2 * b4 + MOVQ 32(SI), AX + MULQ CX + ADDQ AX, R13 + ADCQ DX, R14 + ADCQ BX, R15 + + // | a2 * b5 + MOVQ 40(SI), AX + MULQ CX + ADDQ AX, R14 + ADCQ DX, R15 + + // | + +/* i3 */ + + // | a3 @ CX + MOVQ 24(DI), CX + MOVQ $0x00, BX + + // | a3 * b0 + MOVQ (SI), AX + MULQ CX + ADDQ AX, R10 + ADCQ DX, R11 + ADCQ $0x00, R12 + ADCQ $0x00, BX + + // | a3 * b1 + MOVQ 8(SI), AX + MULQ CX + ADDQ AX, R11 + ADCQ DX, R12 + ADCQ BX, R13 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a3 * b2 + MOVQ 16(SI), AX + MULQ CX + ADDQ AX, R12 + ADCQ DX, R13 + ADCQ BX, R14 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a3 * b3 + MOVQ 24(SI), AX + MULQ CX + ADDQ AX, R13 + ADCQ DX, R14 + ADCQ BX, R15 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a3 * b4 + MOVQ 32(SI), AX + MULQ CX + ADDQ AX, R14 + ADCQ DX, R15 + ADCQ BX, R8 + + // | a3 * b5 + MOVQ 40(SI), AX + MULQ CX + ADDQ AX, R15 + ADCQ DX, R8 + + // | + +/* i4 */ + + // | a4 @ CX + MOVQ 32(DI), CX + MOVQ $0x00, BX + + // | a4 * b0 + MOVQ (SI), AX + MULQ CX + ADDQ AX, R11 + ADCQ DX, R12 + ADCQ $0x00, R13 + ADCQ $0x00, BX + + // | a4 * b1 + MOVQ 8(SI), AX + MULQ CX + ADDQ AX, R12 + ADCQ DX, R13 + ADCQ BX, R14 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a4 * b2 + MOVQ 16(SI), AX + MULQ CX + ADDQ AX, R13 + ADCQ DX, R14 + ADCQ BX, R15 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a4 * b3 + MOVQ 24(SI), AX + MULQ CX + ADDQ AX, R14 + ADCQ DX, R15 + ADCQ BX, R8 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a4 * b4 + MOVQ 32(SI), AX + MULQ CX + ADDQ AX, R15 + ADCQ DX, R8 + ADCQ BX, R9 + + // | a4 * b5 + MOVQ 40(SI), AX + MULQ CX + ADDQ AX, R8 + ADCQ DX, R9 + + // | + +/* i5 */ + + // | a5 @ CX + MOVQ 40(DI), CX + MOVQ $0x00, BX + + // | a5 * b0 + MOVQ (SI), AX + MULQ CX + ADDQ AX, R12 + ADCQ DX, R13 + ADCQ $0x00, R14 + ADCQ $0x00, BX + + // | a5 * b1 + MOVQ 8(SI), AX + MULQ CX + ADDQ AX, R13 + ADCQ DX, R14 + ADCQ BX, R15 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a5 * b2 + MOVQ 16(SI), AX + MULQ CX + ADDQ AX, R14 + ADCQ DX, R15 + ADCQ BX, R8 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a5 * b3 + MOVQ 24(SI), AX + MULQ CX + ADDQ AX, R15 + ADCQ DX, R8 + ADCQ BX, R9 + MOVQ $0x00, BX + ADCQ $0x00, BX + + // | a5 * b4 + MOVQ 32(SI), AX + MULQ CX + ADDQ AX, R8 + ADCQ DX, R9 + ADCQ $0x00, BX + + // | a5 * b5 + MOVQ 40(SI), AX + MULQ CX + ADDQ AX, R9 + ADCQ DX, BX + + // | + +/* */ + + // | + // | W + // | 0 (SP) | 1 8(SP) | 2 16(SP) | 3 R10 | 4 R11 | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 R9 | 11 BX + + + MOVQ (SP), CX + MOVQ 8(SP), DI + MOVQ 16(SP), SI + MOVQ BX, (SP) + MOVQ R9, 8(SP) + + // | + +/* montgomery reduction */ + + // | + +/* i0 */ + + // | + // | W + // | 0 CX | 1 DI | 2 SI | 3 R10 | 4 R11 | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP) + + + // | | u0 = w0 * inp + MOVQ CX, AX + MULQ ·inp+0(SB) + MOVQ AX, R9 + MOVQ $0x00, BX + + // | + +/* */ + + // | j0 + + // | w0 @ CX + MOVQ ·modulus+0(SB), AX + MULQ R9 + ADDQ AX, CX + ADCQ DX, BX + + // | j1 + + // | w1 @ DI + MOVQ ·modulus+8(SB), AX + MULQ R9 + ADDQ AX, DI + ADCQ $0x00, DX + ADDQ BX, DI + MOVQ $0x00, BX + ADCQ DX, BX + + // | j2 + + // | w2 @ SI + MOVQ ·modulus+16(SB), AX + MULQ R9 + ADDQ AX, SI + ADCQ $0x00, DX + ADDQ BX, SI + MOVQ $0x00, BX + ADCQ DX, BX + + // | j3 + + // | w3 @ R10 + MOVQ ·modulus+24(SB), AX + MULQ R9 + ADDQ AX, R10 + ADCQ $0x00, DX + ADDQ BX, R10 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j4 + + // | w4 @ R11 + MOVQ ·modulus+32(SB), AX + MULQ R9 + ADDQ AX, R11 + ADCQ $0x00, DX + ADDQ BX, R11 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j5 + + // | w5 @ R12 + MOVQ ·modulus+40(SB), AX + MULQ R9 + ADDQ AX, R12 + ADCQ $0x00, DX + ADDQ BX, R12 + + // | w6 @ R13 + ADCQ DX, R13 + ADCQ $0x00, CX + + // | + +/* i1 */ + + // | + // | W + // | 0 - | 1 DI | 2 SI | 3 R10 | 4 R11 | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP) + + + // | | u1 = w1 * inp + MOVQ DI, AX + MULQ ·inp+0(SB) + MOVQ AX, R9 + MOVQ $0x00, BX + + // | + +/* */ + + // | j0 + + // | w1 @ DI + MOVQ ·modulus+0(SB), AX + MULQ R9 + ADDQ AX, DI + ADCQ DX, BX + + // | j1 + + // | w2 @ SI + MOVQ ·modulus+8(SB), AX + MULQ R9 + ADDQ AX, SI + ADCQ $0x00, DX + ADDQ BX, SI + MOVQ $0x00, BX + ADCQ DX, BX + + // | j2 + + // | w3 @ R10 + MOVQ ·modulus+16(SB), AX + MULQ R9 + ADDQ AX, R10 + ADCQ $0x00, DX + ADDQ BX, R10 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j3 + + // | w4 @ R11 + MOVQ ·modulus+24(SB), AX + MULQ R9 + ADDQ AX, R11 + ADCQ $0x00, DX + ADDQ BX, R11 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j4 + + // | w5 @ R12 + MOVQ ·modulus+32(SB), AX + MULQ R9 + ADDQ AX, R12 + ADCQ $0x00, DX + ADDQ BX, R12 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j5 + + // | w6 @ R13 + MOVQ ·modulus+40(SB), AX + MULQ R9 + ADDQ AX, R13 + ADCQ DX, CX + ADDQ BX, R13 + + // | w7 @ R14 + ADCQ CX, R14 + MOVQ $0x00, CX + ADCQ $0x00, CX + + // | + +/* i2 */ + + // | + // | W + // | 0 - | 1 - | 2 SI | 3 R10 | 4 R11 | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP) + + + // | | u2 = w2 * inp + MOVQ SI, AX + MULQ ·inp+0(SB) + MOVQ AX, R9 + MOVQ $0x00, BX + + // | + +/* */ + + // | j0 + + // | w2 @ SI + MOVQ ·modulus+0(SB), AX + MULQ R9 + ADDQ AX, SI + ADCQ DX, BX + + // | j1 + + // | w3 @ R10 + MOVQ ·modulus+8(SB), AX + MULQ R9 + ADDQ AX, R10 + ADCQ $0x00, DX + ADDQ BX, R10 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j2 + + // | w4 @ R11 + MOVQ ·modulus+16(SB), AX + MULQ R9 + ADDQ AX, R11 + ADCQ $0x00, DX + ADDQ BX, R11 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j3 + + // | w5 @ R12 + MOVQ ·modulus+24(SB), AX + MULQ R9 + ADDQ AX, R12 + ADCQ $0x00, DX + ADDQ BX, R12 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j4 + + // | w6 @ R13 + MOVQ ·modulus+32(SB), AX + MULQ R9 + ADDQ AX, R13 + ADCQ $0x00, DX + ADDQ BX, R13 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j5 + + // | w7 @ R14 + MOVQ ·modulus+40(SB), AX + MULQ R9 + ADDQ AX, R14 + ADCQ DX, CX + ADDQ BX, R14 + + // | w8 @ R15 + ADCQ CX, R15 + MOVQ $0x00, CX + ADCQ $0x00, CX + + // | + +/* i3 */ + + // | + // | W + // | 0 - | 1 - | 2 - | 3 R10 | 4 R11 | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP) + + + // | | u3 = w3 * inp + MOVQ R10, AX + MULQ ·inp+0(SB) + MOVQ AX, R9 + MOVQ $0x00, BX + + // | + +/* */ + + // | j0 + + // | w3 @ R10 + MOVQ ·modulus+0(SB), AX + MULQ R9 + ADDQ AX, R10 + ADCQ DX, BX + + // | j1 + + // | w4 @ R11 + MOVQ ·modulus+8(SB), AX + MULQ R9 + ADDQ AX, R11 + ADCQ $0x00, DX + ADDQ BX, R11 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j2 + + // | w5 @ R12 + MOVQ ·modulus+16(SB), AX + MULQ R9 + ADDQ AX, R12 + ADCQ $0x00, DX + ADDQ BX, R12 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j3 + + // | w6 @ R13 + MOVQ ·modulus+24(SB), AX + MULQ R9 + ADDQ AX, R13 + ADCQ $0x00, DX + ADDQ BX, R13 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j4 + + // | w7 @ R14 + MOVQ ·modulus+32(SB), AX + MULQ R9 + ADDQ AX, R14 + ADCQ $0x00, DX + ADDQ BX, R14 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j5 + + // | w8 @ R15 + MOVQ ·modulus+40(SB), AX + MULQ R9 + ADDQ AX, R15 + ADCQ DX, CX + ADDQ BX, R15 + + // | w9 @ R8 + ADCQ CX, R8 + MOVQ $0x00, CX + ADCQ $0x00, CX + + // | + +/* i4 */ + + // | + // | W + // | 0 - | 1 - | 2 - | 3 - | 4 R11 | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 8(SP) | 11 (SP) + + + // | | u4 = w4 * inp + MOVQ R11, AX + MULQ ·inp+0(SB) + MOVQ AX, R9 + MOVQ $0x00, BX + + // | + +/* */ + + // | j0 + + // | w4 @ R11 + MOVQ ·modulus+0(SB), AX + MULQ R9 + ADDQ AX, R11 + ADCQ DX, BX + + // | j1 + + // | w5 @ R12 + MOVQ ·modulus+8(SB), AX + MULQ R9 + ADDQ AX, R12 + ADCQ $0x00, DX + ADDQ BX, R12 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j2 + + // | w6 @ R13 + MOVQ ·modulus+16(SB), AX + MULQ R9 + ADDQ AX, R13 + ADCQ $0x00, DX + ADDQ BX, R13 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j3 + + // | w7 @ R14 + MOVQ ·modulus+24(SB), AX + MULQ R9 + ADDQ AX, R14 + ADCQ $0x00, DX + ADDQ BX, R14 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j4 + + // | w8 @ R15 + MOVQ ·modulus+32(SB), AX + MULQ R9 + ADDQ AX, R15 + ADCQ $0x00, DX + ADDQ BX, R15 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j5 + + // | w9 @ R8 + MOVQ ·modulus+40(SB), AX + MULQ R9 + ADDQ AX, R8 + ADCQ DX, CX + ADDQ BX, R8 + + // | move to idle register + MOVQ 8(SP), DI + + // | w10 @ DI + ADCQ CX, DI + MOVQ $0x00, CX + ADCQ $0x00, CX + + // | + +/* i5 */ + + // | + // | W + // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 R12 + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 DI | 11 (SP) + + + // | | u5 = w5 * inp + MOVQ R12, AX + MULQ ·inp+0(SB) + MOVQ AX, R9 + MOVQ $0x00, BX + + // | + +/* */ + + // | j0 + + // | w5 @ R12 + MOVQ ·modulus+0(SB), AX + MULQ R9 + ADDQ AX, R12 + ADCQ DX, BX + + // | j1 + + // | w6 @ R13 + MOVQ ·modulus+8(SB), AX + MULQ R9 + ADDQ AX, R13 + ADCQ $0x00, DX + ADDQ BX, R13 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j2 + + // | w7 @ R14 + MOVQ ·modulus+16(SB), AX + MULQ R9 + ADDQ AX, R14 + ADCQ $0x00, DX + ADDQ BX, R14 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j3 + + // | w8 @ R15 + MOVQ ·modulus+24(SB), AX + MULQ R9 + ADDQ AX, R15 + ADCQ $0x00, DX + ADDQ BX, R15 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j4 + + // | w9 @ R8 + MOVQ ·modulus+32(SB), AX + MULQ R9 + ADDQ AX, R8 + ADCQ $0x00, DX + ADDQ BX, R8 + MOVQ $0x00, BX + ADCQ DX, BX + + // | j5 + + // | w10 @ DI + MOVQ ·modulus+40(SB), AX + MULQ R9 + ADDQ AX, DI + ADCQ DX, CX + ADDQ BX, DI + + // | w11 @ CX + ADCQ (SP), CX + + // | + // | W montgomerry reduction ends + // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 - + // | 6 R13 | 7 R14 | 8 R15 | 9 R8 | 10 DI | 11 CX + + + // | + + +/* modular reduction */ + + MOVQ R13, R10 + SUBQ ·modulus+0(SB), R10 + MOVQ R14, R11 + SBBQ ·modulus+8(SB), R11 + MOVQ R15, R12 + SBBQ ·modulus+16(SB), R12 + MOVQ R8, AX + SBBQ ·modulus+24(SB), AX + MOVQ DI, BX + SBBQ ·modulus+32(SB), BX + MOVQ CX, R9 + SBBQ ·modulus+40(SB), R9 + // | + +/* out */ + + MOVQ c+0(FP), SI + CMOVQCC R10, R13 + MOVQ R13, (SI) + CMOVQCC R11, R14 + MOVQ R14, 8(SI) + CMOVQCC R12, R15 + MOVQ R15, 16(SI) + CMOVQCC AX, R8 + MOVQ R8, 24(SI) + CMOVQCC BX, DI + MOVQ DI, 32(SI) + CMOVQCC R9, CX + MOVQ CX, 40(SI) + RET + + // | + +/* end */ + + +// multiplication +// c = a * b % p +TEXT ·mulADX(SB), NOSPLIT, $16-24 + // | + +/* inputs */ + + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + XORQ AX, AX + + // | + +/* i0 */ + + // | a0 @ DX + MOVQ (DI), DX + + // | a0 * b0 + MULXQ (SI), AX, CX + MOVQ AX, (SP) + + // | a0 * b1 + MULXQ 8(SI), AX, R8 + ADCXQ AX, CX + + // | a0 * b2 + MULXQ 16(SI), AX, R9 + ADCXQ AX, R8 + + // | a0 * b3 + MULXQ 24(SI), AX, R10 + ADCXQ AX, R9 + + // | a0 * b4 + MULXQ 32(SI), AX, R11 + ADCXQ AX, R10 + + // | a0 * b5 + MULXQ 40(SI), AX, R12 + ADCXQ AX, R11 + ADCQ $0x00, R12 + + // | + +/* i1 */ + + // | a1 @ DX + MOVQ 8(DI), DX + XORQ R13, R13 + + // | a1 * b0 + MULXQ (SI), AX, BX + ADOXQ AX, CX + ADCXQ BX, R8 + MOVQ CX, 8(SP) + + // | a1 * b1 + MULXQ 8(SI), AX, BX + ADOXQ AX, R8 + ADCXQ BX, R9 + + // | a1 * b2 + MULXQ 16(SI), AX, BX + ADOXQ AX, R9 + ADCXQ BX, R10 + + // | a1 * b3 + MULXQ 24(SI), AX, BX + ADOXQ AX, R10 + ADCXQ BX, R11 + + // | a1 * b4 + MULXQ 32(SI), AX, BX + ADOXQ AX, R11 + ADCXQ BX, R12 + + // | a1 * b5 + MULXQ 40(SI), AX, BX + ADOXQ AX, R12 + ADOXQ R13, R13 + ADCXQ BX, R13 + + // | + +/* i2 */ + + // | a2 @ DX + MOVQ 16(DI), DX + XORQ R14, R14 + + // | a2 * b0 + MULXQ (SI), AX, BX + ADOXQ AX, R8 + ADCXQ BX, R9 + + // | a2 * b1 + MULXQ 8(SI), AX, BX + ADOXQ AX, R9 + ADCXQ BX, R10 + + // | a2 * b2 + MULXQ 16(SI), AX, BX + ADOXQ AX, R10 + ADCXQ BX, R11 + + // | a2 * b3 + MULXQ 24(SI), AX, BX + ADOXQ AX, R11 + ADCXQ BX, R12 + + // | a2 * b4 + MULXQ 32(SI), AX, BX + ADOXQ AX, R12 + ADCXQ BX, R13 + + // | a2 * b5 + MULXQ 40(SI), AX, BX + ADOXQ AX, R13 + ADOXQ R14, R14 + ADCXQ BX, R14 + + // | + +/* i3 */ + + // | a3 @ DX + MOVQ 24(DI), DX + XORQ R15, R15 + + // | a3 * b0 + MULXQ (SI), AX, BX + ADOXQ AX, R9 + ADCXQ BX, R10 + + // | a3 * b1 + MULXQ 8(SI), AX, BX + ADOXQ AX, R10 + ADCXQ BX, R11 + + // | a3 * b2 + MULXQ 16(SI), AX, BX + ADOXQ AX, R11 + ADCXQ BX, R12 + + // | a3 * b3 + MULXQ 24(SI), AX, BX + ADOXQ AX, R12 + ADCXQ BX, R13 + + // | a3 * b4 + MULXQ 32(SI), AX, BX + ADOXQ AX, R13 + ADCXQ BX, R14 + + // | a3 * b5 + MULXQ 40(SI), AX, BX + ADOXQ AX, R14 + ADOXQ R15, R15 + ADCXQ BX, R15 + + // | + +/* i4 */ + + // | a4 @ DX + MOVQ 32(DI), DX + XORQ CX, CX + + // | a4 * b0 + MULXQ (SI), AX, BX + ADOXQ AX, R10 + ADCXQ BX, R11 + + // | a4 * b1 + MULXQ 8(SI), AX, BX + ADOXQ AX, R11 + ADCXQ BX, R12 + + // | a4 * b2 + MULXQ 16(SI), AX, BX + ADOXQ AX, R12 + ADCXQ BX, R13 + + // | a4 * b3 + MULXQ 24(SI), AX, BX + ADOXQ AX, R13 + ADCXQ BX, R14 + + // | a4 * b4 + MULXQ 32(SI), AX, BX + ADOXQ AX, R14 + ADCXQ BX, R15 + + // | a4 * b5 + MULXQ 40(SI), AX, BX + ADOXQ AX, R15 + ADOXQ CX, CX + ADCXQ BX, CX + + // | + +/* i5 */ + + // | a5 @ DX + MOVQ 40(DI), DX + XORQ DI, DI + + // | a5 * b0 + MULXQ (SI), AX, BX + ADOXQ AX, R11 + ADCXQ BX, R12 + + // | a5 * b1 + MULXQ 8(SI), AX, BX + ADOXQ AX, R12 + ADCXQ BX, R13 + + // | a5 * b2 + MULXQ 16(SI), AX, BX + ADOXQ AX, R13 + ADCXQ BX, R14 + + // | a5 * b3 + MULXQ 24(SI), AX, BX + ADOXQ AX, R14 + ADCXQ BX, R15 + + // | a5 * b4 + MULXQ 32(SI), AX, BX + ADOXQ AX, R15 + ADCXQ BX, CX + + // | a5 * b5 + MULXQ 40(SI), AX, BX + ADOXQ AX, CX + ADOXQ BX, DI + ADCQ $0x00, DI + + // | + +/* */ + + // | + // | W + // | 0 (SP) | 1 8(SP) | 2 R8 | 3 R9 | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 DI + + + MOVQ (SP), BX + MOVQ 8(SP), SI + MOVQ DI, (SP) + + // | + // | W ready to mont + // | 0 BX | 1 SI | 2 R8 | 3 R9 | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | + +/* montgomery reduction */ + + // | clear flags + XORQ AX, AX + + // | + +/* i0 */ + + // | + // | W + // | 0 BX | 1 SI | 2 R8 | 3 R9 | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | | u0 = w0 * inp + MOVQ BX, DX + MULXQ ·inp+0(SB), DX, DI + + // | + +/* */ + + // | j0 + + // | w0 @ BX + MULXQ ·modulus+0(SB), AX, DI + ADOXQ AX, BX + ADCXQ DI, SI + + // | j1 + + // | w1 @ SI + MULXQ ·modulus+8(SB), AX, DI + ADOXQ AX, SI + ADCXQ DI, R8 + + // | j2 + + // | w2 @ R8 + MULXQ ·modulus+16(SB), AX, DI + ADOXQ AX, R8 + ADCXQ DI, R9 + + // | j3 + + // | w3 @ R9 + MULXQ ·modulus+24(SB), AX, DI + ADOXQ AX, R9 + ADCXQ DI, R10 + + // | j4 + + // | w4 @ R10 + MULXQ ·modulus+32(SB), AX, DI + ADOXQ AX, R10 + ADCXQ DI, R11 + + // | j5 + + // | w5 @ R11 + MULXQ ·modulus+40(SB), AX, DI + ADOXQ AX, R11 + ADCXQ DI, R12 + ADOXQ BX, R12 + ADCXQ BX, BX + MOVQ $0x00, AX + ADOXQ AX, BX + + // | clear flags + XORQ AX, AX + + // | + +/* i1 */ + + // | + // | W + // | 0 - | 1 SI | 2 R8 | 3 R9 | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | | u1 = w1 * inp + MOVQ SI, DX + MULXQ ·inp+0(SB), DX, DI + + // | + +/* */ + + // | j0 + + // | w1 @ SI + MULXQ ·modulus+0(SB), AX, DI + ADOXQ AX, SI + ADCXQ DI, R8 + + // | j1 + + // | w2 @ R8 + MULXQ ·modulus+8(SB), AX, DI + ADOXQ AX, R8 + ADCXQ DI, R9 + + // | j2 + + // | w3 @ R9 + MULXQ ·modulus+16(SB), AX, DI + ADOXQ AX, R9 + ADCXQ DI, R10 + + // | j3 + + // | w4 @ R10 + MULXQ ·modulus+24(SB), AX, DI + ADOXQ AX, R10 + ADCXQ DI, R11 + + // | j4 + + // | w5 @ R11 + MULXQ ·modulus+32(SB), AX, DI + ADOXQ AX, R11 + ADCXQ DI, R12 + + // | j5 + + // | w6 @ R12 + MULXQ ·modulus+40(SB), AX, DI + ADOXQ AX, R12 + ADCXQ DI, R13 + ADOXQ BX, R13 + ADCXQ SI, SI + MOVQ $0x00, AX + ADOXQ AX, SI + + // | clear flags + XORQ AX, AX + + // | + +/* i2 */ + + // | + // | W + // | 0 - | 1 - | 2 R8 | 3 R9 | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | | u2 = w2 * inp + MOVQ R8, DX + MULXQ ·inp+0(SB), DX, DI + + // | + +/* */ + + // | j0 + + // | w2 @ R8 + MULXQ ·modulus+0(SB), AX, DI + ADOXQ AX, R8 + ADCXQ DI, R9 + + // | j1 + + // | w3 @ R9 + MULXQ ·modulus+8(SB), AX, DI + ADOXQ AX, R9 + ADCXQ DI, R10 + + // | j2 + + // | w4 @ R10 + MULXQ ·modulus+16(SB), AX, DI + ADOXQ AX, R10 + ADCXQ DI, R11 + + // | j3 + + // | w5 @ R11 + MULXQ ·modulus+24(SB), AX, DI + ADOXQ AX, R11 + ADCXQ DI, R12 + + // | j4 + + // | w6 @ R12 + MULXQ ·modulus+32(SB), AX, DI + ADOXQ AX, R12 + ADCXQ DI, R13 + + // | j5 + + // | w7 @ R13 + MULXQ ·modulus+40(SB), AX, DI + ADOXQ AX, R13 + ADCXQ DI, R14 + ADOXQ SI, R14 + ADCXQ R8, R8 + MOVQ $0x00, AX + ADOXQ AX, R8 + + // | clear flags + XORQ AX, AX + + // | + +/* i3 */ + + // | + // | W + // | 0 - | 1 - | 2 - | 3 R9 | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | | u3 = w3 * inp + MOVQ R9, DX + MULXQ ·inp+0(SB), DX, DI + + // | + +/* */ + + // | j0 + + // | w3 @ R9 + MULXQ ·modulus+0(SB), AX, DI + ADOXQ AX, R9 + ADCXQ DI, R10 + + // | j1 + + // | w4 @ R10 + MULXQ ·modulus+8(SB), AX, DI + ADOXQ AX, R10 + ADCXQ DI, R11 + + // | j2 + + // | w5 @ R11 + MULXQ ·modulus+16(SB), AX, DI + ADOXQ AX, R11 + ADCXQ DI, R12 + + // | j3 + + // | w6 @ R12 + MULXQ ·modulus+24(SB), AX, DI + ADOXQ AX, R12 + ADCXQ DI, R13 + + // | j4 + + // | w7 @ R13 + MULXQ ·modulus+32(SB), AX, DI + ADOXQ AX, R13 + ADCXQ DI, R14 + + // | j5 + + // | w8 @ R14 + MULXQ ·modulus+40(SB), AX, DI + ADOXQ AX, R14 + ADCXQ DI, R15 + ADOXQ R8, R15 + ADCXQ R9, R9 + MOVQ $0x00, AX + ADOXQ AX, R9 + + // | clear flags + XORQ AX, AX + + // | + +/* i4 */ + + // | + // | W + // | 0 - | 1 - | 2 - | 3 - | 4 R10 | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | | u4 = w4 * inp + MOVQ R10, DX + MULXQ ·inp+0(SB), DX, DI + + // | + +/* */ + + // | j0 + + // | w4 @ R10 + MULXQ ·modulus+0(SB), AX, DI + ADOXQ AX, R10 + ADCXQ DI, R11 + + // | j1 + + // | w5 @ R11 + MULXQ ·modulus+8(SB), AX, DI + ADOXQ AX, R11 + ADCXQ DI, R12 + + // | j2 + + // | w6 @ R12 + MULXQ ·modulus+16(SB), AX, DI + ADOXQ AX, R12 + ADCXQ DI, R13 + + // | j3 + + // | w7 @ R13 + MULXQ ·modulus+24(SB), AX, DI + ADOXQ AX, R13 + ADCXQ DI, R14 + + // | j4 + + // | w8 @ R14 + MULXQ ·modulus+32(SB), AX, DI + ADOXQ AX, R14 + ADCXQ DI, R15 + + // | j5 + + // | w9 @ R15 + MULXQ ·modulus+40(SB), AX, DI + ADOXQ AX, R15 + ADCXQ DI, CX + ADOXQ R9, CX + ADCXQ R10, R10 + MOVQ $0x00, AX + ADOXQ AX, R10 + + // | clear flags + XORQ AX, AX + + // | + +/* i5 */ + + // | + // | W + // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 R11 + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 (SP) + + + // | | u5 = w5 * inp + MOVQ R11, DX + MULXQ ·inp+0(SB), DX, DI + + // | + +/* */ + + // | j0 + + // | w5 @ R11 + MULXQ ·modulus+0(SB), AX, DI + ADOXQ AX, R11 + ADCXQ DI, R12 + + // | j1 + + // | w6 @ R12 + MULXQ ·modulus+8(SB), AX, DI + ADOXQ AX, R12 + ADCXQ DI, R13 + + // | j2 + + // | w7 @ R13 + MULXQ ·modulus+16(SB), AX, DI + ADOXQ AX, R13 + ADCXQ DI, R14 + + // | j3 + + // | w8 @ R14 + MULXQ ·modulus+24(SB), AX, DI + ADOXQ AX, R14 + ADCXQ DI, R15 + + // | j4 + + // | w9 @ R15 + MULXQ ·modulus+32(SB), AX, DI + ADOXQ AX, R15 + ADCXQ DI, CX + + // | j5 + + // | w10 @ CX + MULXQ ·modulus+40(SB), AX, DI + ADOXQ AX, CX + + // | w11 @ (SP) + // | move to an idle register + MOVQ (SP), BX + ADCXQ DI, BX + ADOXQ R10, BX + + // | + // | W montgomery reduction ends + // | 0 - | 1 - | 2 - | 3 - | 4 - | 5 - + // | 6 R12 | 7 R13 | 8 R14 | 9 R15 | 10 CX | 11 BX + + + // | + +/* modular reduction */ + + MOVQ R12, AX + SUBQ ·modulus+0(SB), AX + MOVQ R13, DI + SBBQ ·modulus+8(SB), DI + MOVQ R14, SI + SBBQ ·modulus+16(SB), SI + MOVQ R15, R8 + SBBQ ·modulus+24(SB), R8 + MOVQ CX, R9 + SBBQ ·modulus+32(SB), R9 + MOVQ BX, R10 + SBBQ ·modulus+40(SB), R10 + + // | + +/* out */ + + MOVQ c+0(FP), R11 + CMOVQCC AX, R12 + MOVQ R12, (R11) + CMOVQCC DI, R13 + MOVQ R13, 8(R11) + CMOVQCC SI, R14 + MOVQ R14, 16(R11) + CMOVQCC R8, R15 + MOVQ R15, 24(R11) + CMOVQCC R9, CX + MOVQ CX, 32(R11) + CMOVQCC R10, BX + MOVQ BX, 40(R11) + RET + + // | + +/* end */ diff --git a/crypto/bls12381/arithmetic_x86_adx.go b/crypto/bls12381/arithmetic_x86_adx.go new file mode 100644 index 0000000000..a40c7384eb --- /dev/null +++ b/crypto/bls12381/arithmetic_x86_adx.go @@ -0,0 +1,25 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build amd64 && blsadx +// +build amd64,blsadx + +package bls12381 + +// enableADX is true if the ADX/BMI2 instruction set was requested for the BLS +// implementation. The system may still fall back to plain ASM if the necessary +// instructions are unavailable on the CPU. +const enableADX = true diff --git a/crypto/bls12381/arithmetic_x86_noadx.go b/crypto/bls12381/arithmetic_x86_noadx.go new file mode 100644 index 0000000000..679b30ec8c --- /dev/null +++ b/crypto/bls12381/arithmetic_x86_noadx.go @@ -0,0 +1,25 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build amd64 && blsasm +// +build amd64,blsasm + +package bls12381 + +// enableADX is true if the ADX/BMI2 instruction set was requested for the BLS +// implementation. The system may still fall back to plain ASM if the necessary +// instructions are unavailable on the CPU. +const enableADX = false diff --git a/crypto/bls12381/bls12_381.go b/crypto/bls12381/bls12_381.go new file mode 100644 index 0000000000..1c1c97765f --- /dev/null +++ b/crypto/bls12381/bls12_381.go @@ -0,0 +1,230 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +/* + Field Constants +*/ + +// Base field modulus +// p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab + +// Size of six words +// r = 2 ^ 384 + +// modulus = p +var modulus = fe{0xb9feffffffffaaab, 0x1eabfffeb153ffff, 0x6730d2a0f6b0f624, 0x64774b84f38512bf, 0x4b1ba7b6434bacd7, 0x1a0111ea397fe69a} + +var ( + // -p^(-1) mod 2^64 + inp uint64 = 0x89f3fffcfffcfffd + // This value is used in assembly code + _ = inp +) + +// r mod p +var r1 = &fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493} + +// r^2 mod p +var r2 = &fe{ + 0xf4df1f341c341746, 0x0a76e6a609d104f1, 0x8de5476c4c95b6d5, 0x67eb88a9939d83c0, 0x9a793e85b519952d, 0x11988fe592cae3aa, +} + +// -1 + 0 * u +var negativeOne2 = &fe2{ + fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, +} + +// 2 ^ (-1) +var twoInv = &fe{0x1804000000015554, 0x855000053ab00001, 0x633cb57c253c276f, 0x6e22d1ec31ebb502, 0xd3916126f2d14ca2, 0x17fbb8571a006596} + +// (p - 3) / 4 +var pMinus3Over4 = bigFromHex("0x680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaaa") + +// (p + 1) / 4 +var pPlus1Over4 = bigFromHex("0x680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaab") + +// (p - 1) / 2 +var pMinus1Over2 = bigFromHex("0xd0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555") + +// -1 +var nonResidue1 = &fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206} + +// (1 + 1 * u) +var nonResidue2 = &fe2{ + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, +} + +/* + Curve Constants +*/ + +// b coefficient for G1 +var b = &fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e} + +// b coefficient for G2 +var b2 = &fe2{ + fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e}, + fe{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x09d645513d83de7e}, +} + +// Curve order +var q = bigFromHex("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001") + +// Efficient cofactor of G1 +var cofactorEFFG1 = bigFromHex("0xd201000000010001") + +// Efficient cofactor of G2 +var cofactorEFFG2 = bigFromHex("0x0bc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551") + +var g1One = PointG1{ + fe{0x5cb38790fd530c16, 0x7817fc679976fff5, 0x154f95c7143ba1c1, 0xf0ae6acdf3d0e747, 0xedce6ecc21dbf440, 0x120177419e0bfb75}, + fe{0xbaac93d50ce72271, 0x8c22631a7918fd8e, 0xdd595f13570725ce, 0x51ac582950405194, 0x0e1c8c3fad0059c0, 0x0bbc3efc5008a26a}, + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, +} + +var g2One = PointG2{ + fe2{ + fe{0xf5f28fa202940a10, 0xb3f5fb2687b4961a, 0xa1a893b53e2ae580, 0x9894999d1a3caee9, 0x6f67b7631863366b, 0x058191924350bcd7}, + fe{0xa5a9c0759e23f606, 0xaaa0c59dbccd60c3, 0x3bb17e18e2867806, 0x1b1ab6cc8541b367, 0xc2b6ed0ef2158547, 0x11922a097360edf3}, + }, + fe2{ + fe{0x4c730af860494c4a, 0x597cfa1f5e369c5a, 0xe7e6856caa0a635a, 0xbbefb5e96e0d495f, 0x07d3a975f0ef25a2, 0x083fd8e7e80dae5}, + fe{0xadc0fc92df64b05d, 0x18aa270a2b1461dc, 0x86adac6a3be4eba0, 0x79495c4ec93da33a, 0xe7175850a43ccaed, 0xb2bc2a163de1bf2}, + }, + fe2{ + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, +} + +/* + Frobenious Coeffs +*/ + +var frobeniusCoeffs61 = [6]fe2{ + { + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, + }, + { + fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + }, + { + fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, + }, +} + +var frobeniusCoeffs62 = [6]fe2{ + { + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0x0ec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x0110f184e51c5f59}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, +} + +var frobeniusCoeffs12 = [12]fe2{ + { + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x07089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x08f2220fb0fb66eb}, + fe{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf}, + }, + { + fe{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0x0ec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x0110f184e51c5f59}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8}, + fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, + }, + { + fe{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x051ba4ab241b6160}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x0095ba654ed2226b, 0x02e370eccc86f7dd}, + fe{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd}, + }, + { + fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf}, + fe{0x07089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x08f2220fb0fb66eb}, + }, + { + fe{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2}, + fe{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8}, + }, + { + fe{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}, + fe{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + }, + { + fe{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd}, + fe{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x0095ba654ed2226b, 0x02e370eccc86f7dd}, + }, +} + +/* + x +*/ + +var x = bigFromHex("0xd201000000010000") diff --git a/crypto/bls12381/bls12_381_test.go b/crypto/bls12381/bls12_381_test.go new file mode 100644 index 0000000000..6bf5834105 --- /dev/null +++ b/crypto/bls12381/bls12_381_test.go @@ -0,0 +1,13 @@ +package bls12381 + +import ( + "crypto/rand" + "math/big" +) + +var fuz = 10 + +func randScalar(max *big.Int) *big.Int { + a, _ := rand.Int(rand.Reader, max) + return a +} diff --git a/crypto/bls12381/field_element.go b/crypto/bls12381/field_element.go new file mode 100644 index 0000000000..9fdddc6184 --- /dev/null +++ b/crypto/bls12381/field_element.go @@ -0,0 +1,340 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "math/big" +) + +// fe is base field element representation +type fe [6]uint64 + +// fe2 is element representation of 'fp2' which is quadratic extension of base field 'fp' +// Representation follows c[0] + c[1] * u encoding order. +type fe2 [2]fe + +// fe6 is element representation of 'fp6' field which is cubic extension of 'fp2' +// Representation follows c[0] + c[1] * v + c[2] * v^2 encoding order. +type fe6 [3]fe2 + +// fe12 is element representation of 'fp12' field which is quadratic extension of 'fp6' +// Representation follows c[0] + c[1] * w encoding order. +type fe12 [2]fe6 + +func (fe *fe) setBytes(in []byte) *fe { + size := 48 + l := len(in) + if l >= size { + l = size + } + padded := make([]byte, size) + copy(padded[size-l:], in[:]) + var a int + for i := 0; i < 6; i++ { + a = size - i*8 + fe[i] = uint64(padded[a-1]) | uint64(padded[a-2])<<8 | + uint64(padded[a-3])<<16 | uint64(padded[a-4])<<24 | + uint64(padded[a-5])<<32 | uint64(padded[a-6])<<40 | + uint64(padded[a-7])<<48 | uint64(padded[a-8])<<56 + } + return fe +} + +func (fe *fe) setBig(a *big.Int) *fe { + return fe.setBytes(a.Bytes()) +} + +func (fe *fe) setString(s string) (*fe, error) { + if s[:2] == "0x" { + s = s[2:] + } + bytes, err := hex.DecodeString(s) + if err != nil { + return nil, err + } + return fe.setBytes(bytes), nil +} + +func (fe *fe) set(fe2 *fe) *fe { + fe[0] = fe2[0] + fe[1] = fe2[1] + fe[2] = fe2[2] + fe[3] = fe2[3] + fe[4] = fe2[4] + fe[5] = fe2[5] + return fe +} + +func (fe *fe) bytes() []byte { + out := make([]byte, 48) + var a int + for i := 0; i < 6; i++ { + a = 48 - i*8 + out[a-1] = byte(fe[i]) + out[a-2] = byte(fe[i] >> 8) + out[a-3] = byte(fe[i] >> 16) + out[a-4] = byte(fe[i] >> 24) + out[a-5] = byte(fe[i] >> 32) + out[a-6] = byte(fe[i] >> 40) + out[a-7] = byte(fe[i] >> 48) + out[a-8] = byte(fe[i] >> 56) + } + return out +} + +func (fe *fe) big() *big.Int { + return new(big.Int).SetBytes(fe.bytes()) +} + +func (fe *fe) string() (s string) { + for i := 5; i >= 0; i-- { + s = fmt.Sprintf("%s%16.16x", s, fe[i]) + } + return "0x" + s +} + +func (fe *fe) zero() *fe { + fe[0] = 0 + fe[1] = 0 + fe[2] = 0 + fe[3] = 0 + fe[4] = 0 + fe[5] = 0 + return fe +} + +func (fe *fe) one() *fe { + return fe.set(r1) +} + +func (fe *fe) rand(r io.Reader) (*fe, error) { + bi, err := rand.Int(r, modulus.big()) + if err != nil { + return nil, err + } + return fe.setBig(bi), nil +} + +func (fe *fe) isValid() bool { + return fe.cmp(&modulus) < 0 +} + +func (fe *fe) isOdd() bool { + var mask uint64 = 1 + return fe[0]&mask != 0 +} + +func (fe *fe) isEven() bool { + var mask uint64 = 1 + return fe[0]&mask == 0 +} + +func (fe *fe) isZero() bool { + return (fe[5] | fe[4] | fe[3] | fe[2] | fe[1] | fe[0]) == 0 +} + +func (fe *fe) isOne() bool { + return fe.equal(r1) +} + +func (fe *fe) cmp(fe2 *fe) int { + for i := 5; i >= 0; i-- { + if fe[i] > fe2[i] { + return 1 + } else if fe[i] < fe2[i] { + return -1 + } + } + return 0 +} + +func (fe *fe) equal(fe2 *fe) bool { + return fe2[0] == fe[0] && fe2[1] == fe[1] && fe2[2] == fe[2] && fe2[3] == fe[3] && fe2[4] == fe[4] && fe2[5] == fe[5] +} + +func (e *fe) sign() bool { + r := new(fe) + fromMont(r, e) + return r[0]&1 == 0 +} + +func (fe *fe) div2(e uint64) { + fe[0] = fe[0]>>1 | fe[1]<<63 + fe[1] = fe[1]>>1 | fe[2]<<63 + fe[2] = fe[2]>>1 | fe[3]<<63 + fe[3] = fe[3]>>1 | fe[4]<<63 + fe[4] = fe[4]>>1 | fe[5]<<63 + fe[5] = fe[5]>>1 | e<<63 +} + +func (fe *fe) mul2() uint64 { + e := fe[5] >> 63 + fe[5] = fe[5]<<1 | fe[4]>>63 + fe[4] = fe[4]<<1 | fe[3]>>63 + fe[3] = fe[3]<<1 | fe[2]>>63 + fe[2] = fe[2]<<1 | fe[1]>>63 + fe[1] = fe[1]<<1 | fe[0]>>63 + fe[0] = fe[0] << 1 + return e +} + +func (e *fe2) zero() *fe2 { + e[0].zero() + e[1].zero() + return e +} + +func (e *fe2) one() *fe2 { + e[0].one() + e[1].zero() + return e +} + +func (e *fe2) set(e2 *fe2) *fe2 { + e[0].set(&e2[0]) + e[1].set(&e2[1]) + return e +} + +func (e *fe2) rand(r io.Reader) (*fe2, error) { + a0, err := new(fe).rand(r) + if err != nil { + return nil, err + } + a1, err := new(fe).rand(r) + if err != nil { + return nil, err + } + return &fe2{*a0, *a1}, nil +} + +func (e *fe2) isOne() bool { + return e[0].isOne() && e[1].isZero() +} + +func (e *fe2) isZero() bool { + return e[0].isZero() && e[1].isZero() +} + +func (e *fe2) equal(e2 *fe2) bool { + return e[0].equal(&e2[0]) && e[1].equal(&e2[1]) +} + +func (e *fe2) sign() bool { + r := new(fe) + if !e[0].isZero() { + fromMont(r, &e[0]) + return r[0]&1 == 0 + } + fromMont(r, &e[1]) + return r[0]&1 == 0 +} + +func (e *fe6) zero() *fe6 { + e[0].zero() + e[1].zero() + e[2].zero() + return e +} + +func (e *fe6) one() *fe6 { + e[0].one() + e[1].zero() + e[2].zero() + return e +} + +func (e *fe6) set(e2 *fe6) *fe6 { + e[0].set(&e2[0]) + e[1].set(&e2[1]) + e[2].set(&e2[2]) + return e +} + +func (e *fe6) rand(r io.Reader) (*fe6, error) { + a0, err := new(fe2).rand(r) + if err != nil { + return nil, err + } + a1, err := new(fe2).rand(r) + if err != nil { + return nil, err + } + a2, err := new(fe2).rand(r) + if err != nil { + return nil, err + } + return &fe6{*a0, *a1, *a2}, nil +} + +func (e *fe6) isOne() bool { + return e[0].isOne() && e[1].isZero() && e[2].isZero() +} + +func (e *fe6) isZero() bool { + return e[0].isZero() && e[1].isZero() && e[2].isZero() +} + +func (e *fe6) equal(e2 *fe6) bool { + return e[0].equal(&e2[0]) && e[1].equal(&e2[1]) && e[2].equal(&e2[2]) +} + +func (e *fe12) zero() *fe12 { + e[0].zero() + e[1].zero() + return e +} + +func (e *fe12) one() *fe12 { + e[0].one() + e[1].zero() + return e +} + +func (e *fe12) set(e2 *fe12) *fe12 { + e[0].set(&e2[0]) + e[1].set(&e2[1]) + return e +} + +func (e *fe12) rand(r io.Reader) (*fe12, error) { + a0, err := new(fe6).rand(r) + if err != nil { + return nil, err + } + a1, err := new(fe6).rand(r) + if err != nil { + return nil, err + } + return &fe12{*a0, *a1}, nil +} + +func (e *fe12) isOne() bool { + return e[0].isOne() && e[1].isZero() +} + +func (e *fe12) isZero() bool { + return e[0].isZero() && e[1].isZero() +} + +func (e *fe12) equal(e2 *fe12) bool { + return e[0].equal(&e2[0]) && e[1].equal(&e2[1]) +} diff --git a/crypto/bls12381/field_element_test.go b/crypto/bls12381/field_element_test.go new file mode 100644 index 0000000000..70bbe5cfe5 --- /dev/null +++ b/crypto/bls12381/field_element_test.go @@ -0,0 +1,250 @@ +package bls12381 + +import ( + "bytes" + "crypto/rand" + "math/big" + "testing" +) + +func TestFieldElementValidation(t *testing.T) { + zero := new(fe).zero() + if !zero.isValid() { + t.Fatal("zero must be valid") + } + one := new(fe).one() + if !one.isValid() { + t.Fatal("one must be valid") + } + if modulus.isValid() { + t.Fatal("modulus must be invalid") + } + n := modulus.big() + n.Add(n, big.NewInt(1)) + if new(fe).setBig(n).isValid() { + t.Fatal("number greater than modulus must be invalid") + } +} + +func TestFieldElementEquality(t *testing.T) { + // fe + zero := new(fe).zero() + if !zero.equal(zero) { + t.Fatal("0 == 0") + } + one := new(fe).one() + if !one.equal(one) { + t.Fatal("1 == 1") + } + a, _ := new(fe).rand(rand.Reader) + if !a.equal(a) { + t.Fatal("a == a") + } + b := new(fe) + add(b, a, one) + if a.equal(b) { + t.Fatal("a != a + 1") + } + // fe2 + zero2 := new(fe2).zero() + if !zero2.equal(zero2) { + t.Fatal("0 == 0") + } + one2 := new(fe2).one() + if !one2.equal(one2) { + t.Fatal("1 == 1") + } + a2, _ := new(fe2).rand(rand.Reader) + if !a2.equal(a2) { + t.Fatal("a == a") + } + b2 := new(fe2) + fp2 := newFp2() + fp2.add(b2, a2, one2) + if a2.equal(b2) { + t.Fatal("a != a + 1") + } + // fe6 + zero6 := new(fe6).zero() + if !zero6.equal(zero6) { + t.Fatal("0 == 0") + } + one6 := new(fe6).one() + if !one6.equal(one6) { + t.Fatal("1 == 1") + } + a6, _ := new(fe6).rand(rand.Reader) + if !a6.equal(a6) { + t.Fatal("a == a") + } + b6 := new(fe6) + fp6 := newFp6(fp2) + fp6.add(b6, a6, one6) + if a6.equal(b6) { + t.Fatal("a != a + 1") + } + // fe12 + zero12 := new(fe12).zero() + if !zero12.equal(zero12) { + t.Fatal("0 == 0") + } + one12 := new(fe12).one() + if !one12.equal(one12) { + t.Fatal("1 == 1") + } + a12, _ := new(fe12).rand(rand.Reader) + if !a12.equal(a12) { + t.Fatal("a == a") + } + b12 := new(fe12) + fp12 := newFp12(fp6) + fp12.add(b12, a12, one12) + if a12.equal(b12) { + t.Fatal("a != a + 1") + } +} + +func TestFieldElementHelpers(t *testing.T) { + // fe + zero := new(fe).zero() + if !zero.isZero() { + t.Fatal("'zero' is not zero") + } + one := new(fe).one() + if !one.isOne() { + t.Fatal("'one' is not one") + } + odd := new(fe).setBig(big.NewInt(1)) + if !odd.isOdd() { + t.Fatal("1 must be odd") + } + if odd.isEven() { + t.Fatal("1 must not be even") + } + even := new(fe).setBig(big.NewInt(2)) + if !even.isEven() { + t.Fatal("2 must be even") + } + if even.isOdd() { + t.Fatal("2 must not be odd") + } + // fe2 + zero2 := new(fe2).zero() + if !zero2.isZero() { + t.Fatal("'zero' is not zero, 2") + } + one2 := new(fe2).one() + if !one2.isOne() { + t.Fatal("'one' is not one, 2") + } + // fe6 + zero6 := new(fe6).zero() + if !zero6.isZero() { + t.Fatal("'zero' is not zero, 6") + } + one6 := new(fe6).one() + if !one6.isOne() { + t.Fatal("'one' is not one, 6") + } + // fe12 + zero12 := new(fe12).zero() + if !zero12.isZero() { + t.Fatal("'zero' is not zero, 12") + } + one12 := new(fe12).one() + if !one12.isOne() { + t.Fatal("'one' is not one, 12") + } +} + +func TestFieldElementSerialization(t *testing.T) { + t.Run("zero", func(t *testing.T) { + in := make([]byte, 48) + fe := new(fe).setBytes(in) + if !fe.isZero() { + t.Fatal("bad serialization") + } + if !bytes.Equal(in, fe.bytes()) { + t.Fatal("bad serialization") + } + }) + t.Run("bytes", func(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b := new(fe).setBytes(a.bytes()) + if !a.equal(b) { + t.Fatal("bad serialization") + } + } + }) + t.Run("big", func(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b := new(fe).setBig(a.big()) + if !a.equal(b) { + t.Fatal("bad encoding or decoding") + } + } + }) + t.Run("string", func(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, err := new(fe).setString(a.string()) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad encoding or decoding") + } + } + }) +} + +func TestFieldElementByteInputs(t *testing.T) { + zero := new(fe).zero() + in := make([]byte, 0) + a := new(fe).setBytes(in) + if !a.equal(zero) { + t.Fatal("bad serialization") + } + in = make([]byte, 48) + a = new(fe).setBytes(in) + if !a.equal(zero) { + t.Fatal("bad serialization") + } + in = make([]byte, 64) + a = new(fe).setBytes(in) + if !a.equal(zero) { + t.Fatal("bad serialization") + } + in = make([]byte, 49) + in[47] = 1 + normalOne := &fe{1, 0, 0, 0, 0, 0} + a = new(fe).setBytes(in) + if !a.equal(normalOne) { + t.Fatal("bad serialization") + } +} + +func TestFieldElementCopy(t *testing.T) { + a, _ := new(fe).rand(rand.Reader) + b := new(fe).set(a) + if !a.equal(b) { + t.Fatal("bad copy, 1") + } + a2, _ := new(fe2).rand(rand.Reader) + b2 := new(fe2).set(a2) + if !a2.equal(b2) { + t.Fatal("bad copy, 2") + } + a6, _ := new(fe6).rand(rand.Reader) + b6 := new(fe6).set(a6) + if !a6.equal(b6) { + t.Fatal("bad copy, 6") + } + a12, _ := new(fe12).rand(rand.Reader) + b12 := new(fe12).set(a12) + if !a12.equal(b12) { + t.Fatal("bad copy, 12") + } +} diff --git a/crypto/bls12381/fp.go b/crypto/bls12381/fp.go new file mode 100644 index 0000000000..09f6f49bc0 --- /dev/null +++ b/crypto/bls12381/fp.go @@ -0,0 +1,167 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math/big" +) + +func fromBytes(in []byte) (*fe, error) { + fe := &fe{} + if len(in) != 48 { + return nil, errors.New("input string should be equal 48 bytes") + } + fe.setBytes(in) + if !fe.isValid() { + return nil, errors.New("must be less than modulus") + } + toMont(fe, fe) + return fe, nil +} + +func fromBig(in *big.Int) (*fe, error) { + fe := new(fe).setBig(in) + if !fe.isValid() { + return nil, errors.New("invalid input string") + } + toMont(fe, fe) + return fe, nil +} + +func fromString(in string) (*fe, error) { + fe, err := new(fe).setString(in) + if err != nil { + return nil, err + } + if !fe.isValid() { + return nil, errors.New("invalid input string") + } + toMont(fe, fe) + return fe, nil +} + +func toBytes(e *fe) []byte { + e2 := new(fe) + fromMont(e2, e) + return e2.bytes() +} + +func toBig(e *fe) *big.Int { + e2 := new(fe) + fromMont(e2, e) + return e2.big() +} + +func toString(e *fe) (s string) { + e2 := new(fe) + fromMont(e2, e) + return e2.string() +} + +func toMont(c, a *fe) { + mul(c, a, r2) +} + +func fromMont(c, a *fe) { + mul(c, a, &fe{1}) +} + +func exp(c, a *fe, e *big.Int) { + z := new(fe).set(r1) + for i := e.BitLen(); i >= 0; i-- { + mul(z, z, z) + if e.Bit(i) == 1 { + mul(z, z, a) + } + } + c.set(z) +} + +func inverse(inv, e *fe) { + if e.isZero() { + inv.zero() + return + } + u := new(fe).set(&modulus) + v := new(fe).set(e) + s := &fe{1} + r := &fe{0} + var k int + var z uint64 + var found = false + // Phase 1 + for i := 0; i < 768; i++ { + if v.isZero() { + found = true + break + } + if u.isEven() { + u.div2(0) + s.mul2() + } else if v.isEven() { + v.div2(0) + z += r.mul2() + } else if u.cmp(v) == 1 { + lsubAssign(u, v) + u.div2(0) + laddAssign(r, s) + s.mul2() + } else { + lsubAssign(v, u) + v.div2(0) + laddAssign(s, r) + z += r.mul2() + } + k += 1 + } + + if !found { + inv.zero() + return + } + + if k < 381 || k > 381+384 { + inv.zero() + return + } + + if r.cmp(&modulus) != -1 || z > 0 { + lsubAssign(r, &modulus) + } + u.set(&modulus) + lsubAssign(u, r) + + // Phase 2 + for i := k; i < 384*2; i++ { + double(u, u) + } + inv.set(u) +} + +func sqrt(c, a *fe) bool { + u, v := new(fe).set(a), new(fe) + exp(c, a, pPlus1Over4) + square(v, c) + return u.equal(v) +} + +func isQuadraticNonResidue(elem *fe) bool { + result := new(fe) + exp(result, elem, pMinus1Over2) + return !result.isOne() +} diff --git a/crypto/bls12381/fp12.go b/crypto/bls12381/fp12.go new file mode 100644 index 0000000000..51e949fe5f --- /dev/null +++ b/crypto/bls12381/fp12.go @@ -0,0 +1,277 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math/big" +) + +type fp12 struct { + fp12temp + fp6 *fp6 +} + +type fp12temp struct { + t2 [9]*fe2 + t6 [5]*fe6 + t12 *fe12 +} + +func newFp12Temp() fp12temp { + t2 := [9]*fe2{} + t6 := [5]*fe6{} + for i := 0; i < len(t2); i++ { + t2[i] = &fe2{} + } + for i := 0; i < len(t6); i++ { + t6[i] = &fe6{} + } + return fp12temp{t2, t6, &fe12{}} +} + +func newFp12(fp6 *fp6) *fp12 { + t := newFp12Temp() + if fp6 == nil { + return &fp12{t, newFp6(nil)} + } + return &fp12{t, fp6} +} + +func (e *fp12) fp2() *fp2 { + return e.fp6.fp2 +} + +func (e *fp12) fromBytes(in []byte) (*fe12, error) { + if len(in) != 576 { + return nil, errors.New("input string should be larger than 96 bytes") + } + fp6 := e.fp6 + c1, err := fp6.fromBytes(in[:288]) + if err != nil { + return nil, err + } + c0, err := fp6.fromBytes(in[288:]) + if err != nil { + return nil, err + } + return &fe12{*c0, *c1}, nil +} + +func (e *fp12) toBytes(a *fe12) []byte { + fp6 := e.fp6 + out := make([]byte, 576) + copy(out[:288], fp6.toBytes(&a[1])) + copy(out[288:], fp6.toBytes(&a[0])) + return out +} + +func (e *fp12) new() *fe12 { + return new(fe12) +} + +func (e *fp12) zero() *fe12 { + return new(fe12) +} + +func (e *fp12) one() *fe12 { + return new(fe12).one() +} + +func (e *fp12) add(c, a, b *fe12) { + fp6 := e.fp6 + fp6.add(&c[0], &a[0], &b[0]) + fp6.add(&c[1], &a[1], &b[1]) +} + +func (e *fp12) double(c, a *fe12) { + fp6 := e.fp6 + fp6.double(&c[0], &a[0]) + fp6.double(&c[1], &a[1]) +} + +func (e *fp12) sub(c, a, b *fe12) { + fp6 := e.fp6 + fp6.sub(&c[0], &a[0], &b[0]) + fp6.sub(&c[1], &a[1], &b[1]) +} + +func (e *fp12) neg(c, a *fe12) { + fp6 := e.fp6 + fp6.neg(&c[0], &a[0]) + fp6.neg(&c[1], &a[1]) +} + +func (e *fp12) conjugate(c, a *fe12) { + fp6 := e.fp6 + c[0].set(&a[0]) + fp6.neg(&c[1], &a[1]) +} + +func (e *fp12) square(c, a *fe12) { + fp6, t := e.fp6, e.t6 + fp6.add(t[0], &a[0], &a[1]) + fp6.mul(t[2], &a[0], &a[1]) + fp6.mulByNonResidue(t[1], &a[1]) + fp6.addAssign(t[1], &a[0]) + fp6.mulByNonResidue(t[3], t[2]) + fp6.mulAssign(t[0], t[1]) + fp6.subAssign(t[0], t[2]) + fp6.sub(&c[0], t[0], t[3]) + fp6.double(&c[1], t[2]) +} + +func (e *fp12) cyclotomicSquare(c, a *fe12) { + t, fp2 := e.t2, e.fp2() + e.fp4Square(t[3], t[4], &a[0][0], &a[1][1]) + fp2.sub(t[2], t[3], &a[0][0]) + fp2.doubleAssign(t[2]) + fp2.add(&c[0][0], t[2], t[3]) + fp2.add(t[2], t[4], &a[1][1]) + fp2.doubleAssign(t[2]) + fp2.add(&c[1][1], t[2], t[4]) + e.fp4Square(t[3], t[4], &a[1][0], &a[0][2]) + e.fp4Square(t[5], t[6], &a[0][1], &a[1][2]) + fp2.sub(t[2], t[3], &a[0][1]) + fp2.doubleAssign(t[2]) + fp2.add(&c[0][1], t[2], t[3]) + fp2.add(t[2], t[4], &a[1][2]) + fp2.doubleAssign(t[2]) + fp2.add(&c[1][2], t[2], t[4]) + fp2.mulByNonResidue(t[3], t[6]) + fp2.add(t[2], t[3], &a[1][0]) + fp2.doubleAssign(t[2]) + fp2.add(&c[1][0], t[2], t[3]) + fp2.sub(t[2], t[5], &a[0][2]) + fp2.doubleAssign(t[2]) + fp2.add(&c[0][2], t[2], t[5]) +} + +func (e *fp12) mul(c, a, b *fe12) { + t, fp6 := e.t6, e.fp6 + fp6.mul(t[1], &a[0], &b[0]) + fp6.mul(t[2], &a[1], &b[1]) + fp6.add(t[0], t[1], t[2]) + fp6.mulByNonResidue(t[2], t[2]) + fp6.add(t[3], t[1], t[2]) + fp6.add(t[1], &a[0], &a[1]) + fp6.add(t[2], &b[0], &b[1]) + fp6.mulAssign(t[1], t[2]) + c[0].set(t[3]) + fp6.sub(&c[1], t[1], t[0]) +} + +func (e *fp12) mulAssign(a, b *fe12) { + t, fp6 := e.t6, e.fp6 + fp6.mul(t[1], &a[0], &b[0]) + fp6.mul(t[2], &a[1], &b[1]) + fp6.add(t[0], t[1], t[2]) + fp6.mulByNonResidue(t[2], t[2]) + fp6.add(t[3], t[1], t[2]) + fp6.add(t[1], &a[0], &a[1]) + fp6.add(t[2], &b[0], &b[1]) + fp6.mulAssign(t[1], t[2]) + a[0].set(t[3]) + fp6.sub(&a[1], t[1], t[0]) +} + +func (e *fp12) fp4Square(c0, c1, a0, a1 *fe2) { + t, fp2 := e.t2, e.fp2() + fp2.square(t[0], a0) + fp2.square(t[1], a1) + fp2.mulByNonResidue(t[2], t[1]) + fp2.add(c0, t[2], t[0]) + fp2.add(t[2], a0, a1) + fp2.squareAssign(t[2]) + fp2.subAssign(t[2], t[0]) + fp2.sub(c1, t[2], t[1]) +} + +func (e *fp12) inverse(c, a *fe12) { + fp6, t := e.fp6, e.t6 + fp6.square(t[0], &a[0]) + fp6.square(t[1], &a[1]) + fp6.mulByNonResidue(t[1], t[1]) + fp6.sub(t[1], t[0], t[1]) + fp6.inverse(t[0], t[1]) + fp6.mul(&c[0], &a[0], t[0]) + fp6.mulAssign(t[0], &a[1]) + fp6.neg(&c[1], t[0]) +} + +func (e *fp12) mulBy014Assign(a *fe12, c0, c1, c4 *fe2) { + fp2, fp6, t, t2 := e.fp2(), e.fp6, e.t6, e.t2[0] + fp6.mulBy01(t[0], &a[0], c0, c1) + fp6.mulBy1(t[1], &a[1], c4) + fp2.add(t2, c1, c4) + fp6.add(t[2], &a[1], &a[0]) + fp6.mulBy01Assign(t[2], c0, t2) + fp6.subAssign(t[2], t[0]) + fp6.sub(&a[1], t[2], t[1]) + fp6.mulByNonResidue(t[1], t[1]) + fp6.add(&a[0], t[1], t[0]) +} + +func (e *fp12) exp(c, a *fe12, s *big.Int) { + z := e.one() + for i := s.BitLen() - 1; i >= 0; i-- { + e.square(z, z) + if s.Bit(i) == 1 { + e.mul(z, z, a) + } + } + c.set(z) +} + +func (e *fp12) cyclotomicExp(c, a *fe12, s *big.Int) { + z := e.one() + for i := s.BitLen() - 1; i >= 0; i-- { + e.cyclotomicSquare(z, z) + if s.Bit(i) == 1 { + e.mul(z, z, a) + } + } + c.set(z) +} + +func (e *fp12) frobeniusMap(c, a *fe12, power uint) { + fp6 := e.fp6 + fp6.frobeniusMap(&c[0], &a[0], power) + fp6.frobeniusMap(&c[1], &a[1], power) + switch power { + case 0: + return + case 6: + fp6.neg(&c[1], &c[1]) + default: + fp6.mulByBaseField(&c[1], &c[1], &frobeniusCoeffs12[power]) + } +} + +func (e *fp12) frobeniusMapAssign(a *fe12, power uint) { + fp6 := e.fp6 + fp6.frobeniusMapAssign(&a[0], power) + fp6.frobeniusMapAssign(&a[1], power) + switch power { + case 0: + return + case 6: + fp6.neg(&a[1], &a[1]) + default: + fp6.mulByBaseField(&a[1], &a[1], &frobeniusCoeffs12[power]) + } +} diff --git a/crypto/bls12381/fp2.go b/crypto/bls12381/fp2.go new file mode 100644 index 0000000000..0f1c5a23ac --- /dev/null +++ b/crypto/bls12381/fp2.go @@ -0,0 +1,252 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math/big" +) + +type fp2Temp struct { + t [4]*fe +} + +type fp2 struct { + fp2Temp +} + +func newFp2Temp() fp2Temp { + t := [4]*fe{} + for i := 0; i < len(t); i++ { + t[i] = &fe{} + } + return fp2Temp{t} +} + +func newFp2() *fp2 { + t := newFp2Temp() + return &fp2{t} +} + +func (e *fp2) fromBytes(in []byte) (*fe2, error) { + if len(in) != 96 { + return nil, errors.New("length of input string should be 96 bytes") + } + c1, err := fromBytes(in[:48]) + if err != nil { + return nil, err + } + c0, err := fromBytes(in[48:]) + if err != nil { + return nil, err + } + return &fe2{*c0, *c1}, nil +} + +func (e *fp2) toBytes(a *fe2) []byte { + out := make([]byte, 96) + copy(out[:48], toBytes(&a[1])) + copy(out[48:], toBytes(&a[0])) + return out +} + +func (e *fp2) new() *fe2 { + return new(fe2).zero() +} + +func (e *fp2) zero() *fe2 { + return new(fe2).zero() +} + +func (e *fp2) one() *fe2 { + return new(fe2).one() +} + +func (e *fp2) add(c, a, b *fe2) { + add(&c[0], &a[0], &b[0]) + add(&c[1], &a[1], &b[1]) +} + +func (e *fp2) addAssign(a, b *fe2) { + addAssign(&a[0], &b[0]) + addAssign(&a[1], &b[1]) +} + +func (e *fp2) ladd(c, a, b *fe2) { + ladd(&c[0], &a[0], &b[0]) + ladd(&c[1], &a[1], &b[1]) +} + +func (e *fp2) double(c, a *fe2) { + double(&c[0], &a[0]) + double(&c[1], &a[1]) +} + +func (e *fp2) doubleAssign(a *fe2) { + doubleAssign(&a[0]) + doubleAssign(&a[1]) +} + +func (e *fp2) ldouble(c, a *fe2) { + ldouble(&c[0], &a[0]) + ldouble(&c[1], &a[1]) +} + +func (e *fp2) sub(c, a, b *fe2) { + sub(&c[0], &a[0], &b[0]) + sub(&c[1], &a[1], &b[1]) +} + +func (e *fp2) subAssign(c, a *fe2) { + subAssign(&c[0], &a[0]) + subAssign(&c[1], &a[1]) +} + +func (e *fp2) neg(c, a *fe2) { + neg(&c[0], &a[0]) + neg(&c[1], &a[1]) +} + +func (e *fp2) mul(c, a, b *fe2) { + t := e.t + mul(t[1], &a[0], &b[0]) + mul(t[2], &a[1], &b[1]) + add(t[0], &a[0], &a[1]) + add(t[3], &b[0], &b[1]) + sub(&c[0], t[1], t[2]) + addAssign(t[1], t[2]) + mul(t[0], t[0], t[3]) + sub(&c[1], t[0], t[1]) +} + +func (e *fp2) mulAssign(a, b *fe2) { + t := e.t + mul(t[1], &a[0], &b[0]) + mul(t[2], &a[1], &b[1]) + add(t[0], &a[0], &a[1]) + add(t[3], &b[0], &b[1]) + sub(&a[0], t[1], t[2]) + addAssign(t[1], t[2]) + mul(t[0], t[0], t[3]) + sub(&a[1], t[0], t[1]) +} + +func (e *fp2) square(c, a *fe2) { + t := e.t + ladd(t[0], &a[0], &a[1]) + sub(t[1], &a[0], &a[1]) + ldouble(t[2], &a[0]) + mul(&c[0], t[0], t[1]) + mul(&c[1], t[2], &a[1]) +} + +func (e *fp2) squareAssign(a *fe2) { + t := e.t + ladd(t[0], &a[0], &a[1]) + sub(t[1], &a[0], &a[1]) + ldouble(t[2], &a[0]) + mul(&a[0], t[0], t[1]) + mul(&a[1], t[2], &a[1]) +} + +func (e *fp2) mulByNonResidue(c, a *fe2) { + t := e.t + sub(t[0], &a[0], &a[1]) + add(&c[1], &a[0], &a[1]) + c[0].set(t[0]) +} + +func (e *fp2) mulByB(c, a *fe2) { + t := e.t + double(t[0], &a[0]) + double(t[1], &a[1]) + doubleAssign(t[0]) + doubleAssign(t[1]) + sub(&c[0], t[0], t[1]) + add(&c[1], t[0], t[1]) +} + +func (e *fp2) inverse(c, a *fe2) { + t := e.t + square(t[0], &a[0]) + square(t[1], &a[1]) + addAssign(t[0], t[1]) + inverse(t[0], t[0]) + mul(&c[0], &a[0], t[0]) + mul(t[0], t[0], &a[1]) + neg(&c[1], t[0]) +} + +func (e *fp2) mulByFq(c, a *fe2, b *fe) { + mul(&c[0], &a[0], b) + mul(&c[1], &a[1], b) +} + +func (e *fp2) exp(c, a *fe2, s *big.Int) { + z := e.one() + for i := s.BitLen() - 1; i >= 0; i-- { + e.square(z, z) + if s.Bit(i) == 1 { + e.mul(z, z, a) + } + } + c.set(z) +} + +func (e *fp2) frobeniusMap(c, a *fe2, power uint) { + c[0].set(&a[0]) + if power%2 == 1 { + neg(&c[1], &a[1]) + return + } + c[1].set(&a[1]) +} + +func (e *fp2) frobeniusMapAssign(a *fe2, power uint) { + if power%2 == 1 { + neg(&a[1], &a[1]) + return + } +} + +func (e *fp2) sqrt(c, a *fe2) bool { + u, x0, a1, alpha := &fe2{}, &fe2{}, &fe2{}, &fe2{} + u.set(a) + e.exp(a1, a, pMinus3Over4) + e.square(alpha, a1) + e.mul(alpha, alpha, a) + e.mul(x0, a1, a) + if alpha.equal(negativeOne2) { + neg(&c[0], &x0[1]) + c[1].set(&x0[0]) + return true + } + e.add(alpha, alpha, e.one()) + e.exp(alpha, alpha, pMinus1Over2) + e.mul(c, alpha, x0) + e.square(alpha, c) + return alpha.equal(u) +} + +func (e *fp2) isQuadraticNonResidue(a *fe2) bool { + // https://github.com/leovt/constructible/wiki/Taking-Square-Roots-in-quadratic-extension-Fields + c0, c1 := new(fe), new(fe) + square(c0, &a[0]) + square(c1, &a[1]) + add(c1, c1, c0) + return isQuadraticNonResidue(c1) +} diff --git a/crypto/bls12381/fp6.go b/crypto/bls12381/fp6.go new file mode 100644 index 0000000000..304173baa3 --- /dev/null +++ b/crypto/bls12381/fp6.go @@ -0,0 +1,351 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math/big" +) + +type fp6Temp struct { + t [6]*fe2 +} + +type fp6 struct { + fp2 *fp2 + fp6Temp +} + +func newFp6Temp() fp6Temp { + t := [6]*fe2{} + for i := 0; i < len(t); i++ { + t[i] = &fe2{} + } + return fp6Temp{t} +} + +func newFp6(f *fp2) *fp6 { + t := newFp6Temp() + if f == nil { + return &fp6{newFp2(), t} + } + return &fp6{f, t} +} + +func (e *fp6) fromBytes(b []byte) (*fe6, error) { + if len(b) < 288 { + return nil, errors.New("input string should be larger than 288 bytes") + } + fp2 := e.fp2 + u2, err := fp2.fromBytes(b[:96]) + if err != nil { + return nil, err + } + u1, err := fp2.fromBytes(b[96:192]) + if err != nil { + return nil, err + } + u0, err := fp2.fromBytes(b[192:]) + if err != nil { + return nil, err + } + return &fe6{*u0, *u1, *u2}, nil +} + +func (e *fp6) toBytes(a *fe6) []byte { + fp2 := e.fp2 + out := make([]byte, 288) + copy(out[:96], fp2.toBytes(&a[2])) + copy(out[96:192], fp2.toBytes(&a[1])) + copy(out[192:], fp2.toBytes(&a[0])) + return out +} + +func (e *fp6) new() *fe6 { + return new(fe6) +} + +func (e *fp6) zero() *fe6 { + return new(fe6) +} + +func (e *fp6) one() *fe6 { + return new(fe6).one() +} + +func (e *fp6) add(c, a, b *fe6) { + fp2 := e.fp2 + fp2.add(&c[0], &a[0], &b[0]) + fp2.add(&c[1], &a[1], &b[1]) + fp2.add(&c[2], &a[2], &b[2]) +} + +func (e *fp6) addAssign(a, b *fe6) { + fp2 := e.fp2 + fp2.addAssign(&a[0], &b[0]) + fp2.addAssign(&a[1], &b[1]) + fp2.addAssign(&a[2], &b[2]) +} + +func (e *fp6) double(c, a *fe6) { + fp2 := e.fp2 + fp2.double(&c[0], &a[0]) + fp2.double(&c[1], &a[1]) + fp2.double(&c[2], &a[2]) +} + +func (e *fp6) doubleAssign(a *fe6) { + fp2 := e.fp2 + fp2.doubleAssign(&a[0]) + fp2.doubleAssign(&a[1]) + fp2.doubleAssign(&a[2]) +} + +func (e *fp6) sub(c, a, b *fe6) { + fp2 := e.fp2 + fp2.sub(&c[0], &a[0], &b[0]) + fp2.sub(&c[1], &a[1], &b[1]) + fp2.sub(&c[2], &a[2], &b[2]) +} + +func (e *fp6) subAssign(a, b *fe6) { + fp2 := e.fp2 + fp2.subAssign(&a[0], &b[0]) + fp2.subAssign(&a[1], &b[1]) + fp2.subAssign(&a[2], &b[2]) +} + +func (e *fp6) neg(c, a *fe6) { + fp2 := e.fp2 + fp2.neg(&c[0], &a[0]) + fp2.neg(&c[1], &a[1]) + fp2.neg(&c[2], &a[2]) +} + +func (e *fp6) mul(c, a, b *fe6) { + fp2, t := e.fp2, e.t + fp2.mul(t[0], &a[0], &b[0]) + fp2.mul(t[1], &a[1], &b[1]) + fp2.mul(t[2], &a[2], &b[2]) + fp2.add(t[3], &a[1], &a[2]) + fp2.add(t[4], &b[1], &b[2]) + fp2.mulAssign(t[3], t[4]) + fp2.add(t[4], t[1], t[2]) + fp2.subAssign(t[3], t[4]) + fp2.mulByNonResidue(t[3], t[3]) + fp2.add(t[5], t[0], t[3]) + fp2.add(t[3], &a[0], &a[1]) + fp2.add(t[4], &b[0], &b[1]) + fp2.mulAssign(t[3], t[4]) + fp2.add(t[4], t[0], t[1]) + fp2.subAssign(t[3], t[4]) + fp2.mulByNonResidue(t[4], t[2]) + fp2.add(&c[1], t[3], t[4]) + fp2.add(t[3], &a[0], &a[2]) + fp2.add(t[4], &b[0], &b[2]) + fp2.mulAssign(t[3], t[4]) + fp2.add(t[4], t[0], t[2]) + fp2.subAssign(t[3], t[4]) + fp2.add(&c[2], t[1], t[3]) + c[0].set(t[5]) +} + +func (e *fp6) mulAssign(a, b *fe6) { + fp2, t := e.fp2, e.t + fp2.mul(t[0], &a[0], &b[0]) + fp2.mul(t[1], &a[1], &b[1]) + fp2.mul(t[2], &a[2], &b[2]) + fp2.add(t[3], &a[1], &a[2]) + fp2.add(t[4], &b[1], &b[2]) + fp2.mulAssign(t[3], t[4]) + fp2.add(t[4], t[1], t[2]) + fp2.subAssign(t[3], t[4]) + fp2.mulByNonResidue(t[3], t[3]) + fp2.add(t[5], t[0], t[3]) + fp2.add(t[3], &a[0], &a[1]) + fp2.add(t[4], &b[0], &b[1]) + fp2.mulAssign(t[3], t[4]) + fp2.add(t[4], t[0], t[1]) + fp2.subAssign(t[3], t[4]) + fp2.mulByNonResidue(t[4], t[2]) + fp2.add(&a[1], t[3], t[4]) + fp2.add(t[3], &a[0], &a[2]) + fp2.add(t[4], &b[0], &b[2]) + fp2.mulAssign(t[3], t[4]) + fp2.add(t[4], t[0], t[2]) + fp2.subAssign(t[3], t[4]) + fp2.add(&a[2], t[1], t[3]) + a[0].set(t[5]) +} + +func (e *fp6) square(c, a *fe6) { + fp2, t := e.fp2, e.t + fp2.square(t[0], &a[0]) + fp2.mul(t[1], &a[0], &a[1]) + fp2.doubleAssign(t[1]) + fp2.sub(t[2], &a[0], &a[1]) + fp2.addAssign(t[2], &a[2]) + fp2.squareAssign(t[2]) + fp2.mul(t[3], &a[1], &a[2]) + fp2.doubleAssign(t[3]) + fp2.square(t[4], &a[2]) + fp2.mulByNonResidue(t[5], t[3]) + fp2.add(&c[0], t[0], t[5]) + fp2.mulByNonResidue(t[5], t[4]) + fp2.add(&c[1], t[1], t[5]) + fp2.addAssign(t[1], t[2]) + fp2.addAssign(t[1], t[3]) + fp2.addAssign(t[0], t[4]) + fp2.sub(&c[2], t[1], t[0]) +} + +func (e *fp6) mulBy01Assign(a *fe6, b0, b1 *fe2) { + fp2, t := e.fp2, e.t + fp2.mul(t[0], &a[0], b0) + fp2.mul(t[1], &a[1], b1) + fp2.add(t[5], &a[1], &a[2]) + fp2.mul(t[2], b1, t[5]) + fp2.subAssign(t[2], t[1]) + fp2.mulByNonResidue(t[2], t[2]) + fp2.add(t[5], &a[0], &a[2]) + fp2.mul(t[3], b0, t[5]) + fp2.subAssign(t[3], t[0]) + fp2.add(&a[2], t[3], t[1]) + fp2.add(t[4], b0, b1) + fp2.add(t[5], &a[0], &a[1]) + fp2.mulAssign(t[4], t[5]) + fp2.subAssign(t[4], t[0]) + fp2.sub(&a[1], t[4], t[1]) + fp2.add(&a[0], t[2], t[0]) +} + +func (e *fp6) mulBy01(c, a *fe6, b0, b1 *fe2) { + fp2, t := e.fp2, e.t + fp2.mul(t[0], &a[0], b0) + fp2.mul(t[1], &a[1], b1) + fp2.add(t[2], &a[1], &a[2]) + fp2.mulAssign(t[2], b1) + fp2.subAssign(t[2], t[1]) + fp2.mulByNonResidue(t[2], t[2]) + fp2.add(t[3], &a[0], &a[2]) + fp2.mulAssign(t[3], b0) + fp2.subAssign(t[3], t[0]) + fp2.add(&c[2], t[3], t[1]) + fp2.add(t[4], b0, b1) + fp2.add(t[3], &a[0], &a[1]) + fp2.mulAssign(t[4], t[3]) + fp2.subAssign(t[4], t[0]) + fp2.sub(&c[1], t[4], t[1]) + fp2.add(&c[0], t[2], t[0]) +} + +func (e *fp6) mulBy1(c, a *fe6, b1 *fe2) { + fp2, t := e.fp2, e.t + fp2.mul(t[0], &a[2], b1) + fp2.mul(&c[2], &a[1], b1) + fp2.mul(&c[1], &a[0], b1) + fp2.mulByNonResidue(&c[0], t[0]) +} + +func (e *fp6) mulByNonResidue(c, a *fe6) { + fp2, t := e.fp2, e.t + t[0].set(&a[0]) + fp2.mulByNonResidue(&c[0], &a[2]) + c[2].set(&a[1]) + c[1].set(t[0]) +} + +func (e *fp6) mulByBaseField(c, a *fe6, b *fe2) { + fp2 := e.fp2 + fp2.mul(&c[0], &a[0], b) + fp2.mul(&c[1], &a[1], b) + fp2.mul(&c[2], &a[2], b) +} + +func (e *fp6) exp(c, a *fe6, s *big.Int) { + z := e.one() + for i := s.BitLen() - 1; i >= 0; i-- { + e.square(z, z) + if s.Bit(i) == 1 { + e.mul(z, z, a) + } + } + c.set(z) +} + +func (e *fp6) inverse(c, a *fe6) { + fp2, t := e.fp2, e.t + fp2.square(t[0], &a[0]) + fp2.mul(t[1], &a[1], &a[2]) + fp2.mulByNonResidue(t[1], t[1]) + fp2.subAssign(t[0], t[1]) + fp2.square(t[1], &a[1]) + fp2.mul(t[2], &a[0], &a[2]) + fp2.subAssign(t[1], t[2]) + fp2.square(t[2], &a[2]) + fp2.mulByNonResidue(t[2], t[2]) + fp2.mul(t[3], &a[0], &a[1]) + fp2.subAssign(t[2], t[3]) + fp2.mul(t[3], &a[2], t[2]) + fp2.mul(t[4], &a[1], t[1]) + fp2.addAssign(t[3], t[4]) + fp2.mulByNonResidue(t[3], t[3]) + fp2.mul(t[4], &a[0], t[0]) + fp2.addAssign(t[3], t[4]) + fp2.inverse(t[3], t[3]) + fp2.mul(&c[0], t[0], t[3]) + fp2.mul(&c[1], t[2], t[3]) + fp2.mul(&c[2], t[1], t[3]) +} + +func (e *fp6) frobeniusMap(c, a *fe6, power uint) { + fp2 := e.fp2 + fp2.frobeniusMap(&c[0], &a[0], power) + fp2.frobeniusMap(&c[1], &a[1], power) + fp2.frobeniusMap(&c[2], &a[2], power) + switch power % 6 { + case 0: + return + case 3: + neg(&c[0][0], &a[1][1]) + c[1][1].set(&a[1][0]) + fp2.neg(&a[2], &a[2]) + default: + fp2.mul(&c[1], &c[1], &frobeniusCoeffs61[power%6]) + fp2.mul(&c[2], &c[2], &frobeniusCoeffs62[power%6]) + } +} + +func (e *fp6) frobeniusMapAssign(a *fe6, power uint) { + fp2 := e.fp2 + fp2.frobeniusMapAssign(&a[0], power) + fp2.frobeniusMapAssign(&a[1], power) + fp2.frobeniusMapAssign(&a[2], power) + t := e.t + switch power % 6 { + case 0: + return + case 3: + neg(&t[0][0], &a[1][1]) + a[1][1].set(&a[1][0]) + a[1][0].set(&t[0][0]) + fp2.neg(&a[2], &a[2]) + default: + fp2.mulAssign(&a[1], &frobeniusCoeffs61[power%6]) + fp2.mulAssign(&a[2], &frobeniusCoeffs62[power%6]) + } +} diff --git a/crypto/bls12381/fp_test.go b/crypto/bls12381/fp_test.go new file mode 100644 index 0000000000..0bad35de16 --- /dev/null +++ b/crypto/bls12381/fp_test.go @@ -0,0 +1,1411 @@ +package bls12381 + +import ( + "bytes" + "crypto/rand" + "math/big" + "testing" +) + +func TestFpSerialization(t *testing.T) { + t.Run("zero", func(t *testing.T) { + in := make([]byte, 48) + fe, err := fromBytes(in) + if err != nil { + t.Fatal(err) + } + if !fe.isZero() { + t.Fatal("bad serialization") + } + if !bytes.Equal(in, toBytes(fe)) { + t.Fatal("bad serialization") + } + }) + t.Run("bytes", func(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, err := fromBytes(toBytes(a)) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad serialization") + } + } + }) + t.Run("string", func(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, err := fromString(toString(a)) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad encoding or decoding") + } + } + }) + t.Run("big", func(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, err := fromBig(toBig(a)) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad encoding or decoding") + } + } + }) +} + +func TestFpAdditionCrossAgainstBigInt(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + c := new(fe) + big_a := toBig(a) + big_b := toBig(b) + big_c := new(big.Int) + add(c, a, b) + out_1 := toBytes(c) + out_2 := padBytes(big_c.Add(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied A") + } + double(c, a) + out_1 = toBytes(c) + out_2 = padBytes(big_c.Add(big_a, big_a).Mod(big_c, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied B") + } + sub(c, a, b) + out_1 = toBytes(c) + out_2 = padBytes(big_c.Sub(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied C") + } + neg(c, a) + out_1 = toBytes(c) + out_2 = padBytes(big_c.Neg(big_a).Mod(big_c, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied D") + } + } +} + +func TestFpAdditionCrossAgainstBigIntAssigned(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + big_a, big_b := toBig(a), toBig(b) + addAssign(a, b) + out_1 := toBytes(a) + out_2 := padBytes(big_a.Add(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied A") + } + a, _ = new(fe).rand(rand.Reader) + big_a = toBig(a) + doubleAssign(a) + out_1 = toBytes(a) + out_2 = padBytes(big_a.Add(big_a, big_a).Mod(big_a, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied B") + } + a, _ = new(fe).rand(rand.Reader) + b, _ = new(fe).rand(rand.Reader) + big_a, big_b = toBig(a), toBig(b) + subAssign(a, b) + out_1 = toBytes(a) + out_2 = padBytes(big_a.Sub(big_a, big_b).Mod(big_a, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied A") + } + } +} + +func TestFpAdditionProperties(t *testing.T) { + for i := 0; i < fuz; i++ { + zero := new(fe).zero() + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + c_1, c_2 := new(fe), new(fe) + add(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a + 0 == a") + } + sub(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a - 0 == a") + } + double(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("2 * 0 == 0") + } + neg(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("-0 == 0") + } + sub(c_1, zero, a) + neg(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("0-a == -a") + } + double(c_1, a) + add(c_2, a, a) + if !c_1.equal(c_2) { + t.Fatal("2 * a == a + a") + } + add(c_1, a, b) + add(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + sub(c_1, a, b) + sub(c_2, b, a) + neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + c_x, _ := new(fe).rand(rand.Reader) + add(c_1, a, b) + add(c_1, c_1, c_x) + add(c_2, a, c_x) + add(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a + b) + c == (a + c ) + b") + } + sub(c_1, a, b) + sub(c_1, c_1, c_x) + sub(c_2, a, c_x) + sub(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a - b) - c == (a - c ) -b") + } + } +} + +func TestFpAdditionPropertiesAssigned(t *testing.T) { + for i := 0; i < fuz; i++ { + zero := new(fe).zero() + a, b := new(fe), new(fe) + _, _ = a.rand(rand.Reader) + b.set(a) + addAssign(a, zero) + if !a.equal(b) { + t.Fatal("a + 0 == a") + } + subAssign(a, zero) + if !a.equal(b) { + t.Fatal("a - 0 == a") + } + a.set(zero) + doubleAssign(a) + if !a.equal(zero) { + t.Fatal("2 * 0 == 0") + } + a.set(zero) + subAssign(a, b) + neg(b, b) + if !a.equal(b) { + t.Fatal("0-a == -a") + } + _, _ = a.rand(rand.Reader) + b.set(a) + doubleAssign(a) + addAssign(b, b) + if !a.equal(b) { + t.Fatal("2 * a == a + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c_1, c_2 := new(fe).set(a), new(fe).set(b) + addAssign(c_1, b) + addAssign(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c_1.set(a) + c_2.set(b) + subAssign(c_1, b) + subAssign(c_2, a) + neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c, _ := new(fe).rand(rand.Reader) + a0 := new(fe).set(a) + addAssign(a, b) + addAssign(a, c) + addAssign(b, c) + addAssign(b, a0) + if !a.equal(b) { + t.Fatal("(a + b) + c == (b + c) + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + _, _ = c.rand(rand.Reader) + a0.set(a) + subAssign(a, b) + subAssign(a, c) + subAssign(a0, c) + subAssign(a0, b) + if !a.equal(a0) { + t.Fatal("(a - b) - c == (a - c) -b") + } + } +} + +func TestFpLazyOperations(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + c, _ := new(fe).rand(rand.Reader) + c0 := new(fe) + c1 := new(fe) + ladd(c0, a, b) + add(c1, a, b) + mul(c0, c0, c) + mul(c1, c1, c) + if !c0.equal(c1) { + // l+ operator stands for lazy addition + t.Fatal("(a + b) * c == (a l+ b) * c") + } + _, _ = a.rand(rand.Reader) + b.set(a) + ldouble(a, a) + ladd(b, b, b) + if !a.equal(b) { + t.Fatal("2 l* a = a l+ a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + _, _ = c.rand(rand.Reader) + a0 := new(fe).set(a) + lsubAssign(a, b) + laddAssign(a, &modulus) + mul(a, a, c) + subAssign(a0, b) + mul(a0, a0, c) + if !a.equal(a0) { + t.Fatal("((a l- b) + p) * c = (a-b) * c") + } + } +} + +func TestFpMultiplicationCrossAgainstBigInt(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + c := new(fe) + big_a := toBig(a) + big_b := toBig(b) + big_c := new(big.Int) + mul(c, a, b) + out_1 := toBytes(c) + out_2 := padBytes(big_c.Mul(big_a, big_b).Mod(big_c, modulus.big()).Bytes(), 48) + if !bytes.Equal(out_1, out_2) { + t.Fatal("cross test against big.Int is not satisfied") + } + } +} + +func TestFpMultiplicationProperties(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + zero, one := new(fe).zero(), new(fe).one() + c_1, c_2 := new(fe), new(fe) + mul(c_1, a, zero) + if !c_1.equal(zero) { + t.Fatal("a * 0 == 0") + } + mul(c_1, a, one) + if !c_1.equal(a) { + t.Fatal("a * 1 == a") + } + mul(c_1, a, b) + mul(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a * b == b * a") + } + c_x, _ := new(fe).rand(rand.Reader) + mul(c_1, a, b) + mul(c_1, c_1, c_x) + mul(c_2, c_x, b) + mul(c_2, c_2, a) + if !c_1.equal(c_2) { + t.Fatal("(a * b) * c == (a * c) * b") + } + square(a, zero) + if !a.equal(zero) { + t.Fatal("0^2 == 0") + } + square(a, one) + if !a.equal(one) { + t.Fatal("1^2 == 1") + } + _, _ = a.rand(rand.Reader) + square(c_1, a) + mul(c_2, a, a) + if !c_1.equal(c_1) { + t.Fatal("a^2 == a*a") + } + } +} + +func TestFpExponentiation(t *testing.T) { + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + u := new(fe) + exp(u, a, big.NewInt(0)) + if !u.isOne() { + t.Fatal("a^0 == 1") + } + exp(u, a, big.NewInt(1)) + if !u.equal(a) { + t.Fatal("a^1 == a") + } + v := new(fe) + mul(u, a, a) + mul(u, u, u) + mul(u, u, u) + exp(v, a, big.NewInt(8)) + if !u.equal(v) { + t.Fatal("((a^2)^2)^2 == a^8") + } + p := modulus.big() + exp(u, a, p) + if !u.equal(a) { + t.Fatal("a^p == a") + } + exp(u, a, p.Sub(p, big.NewInt(1))) + if !u.isOne() { + t.Fatal("a^(p-1) == 1") + } + } +} + +func TestFpInversion(t *testing.T) { + for i := 0; i < fuz; i++ { + u := new(fe) + zero, one := new(fe).zero(), new(fe).one() + inverse(u, zero) + if !u.equal(zero) { + t.Fatal("(0^-1) == 0)") + } + inverse(u, one) + if !u.equal(one) { + t.Fatal("(1^-1) == 1)") + } + a, _ := new(fe).rand(rand.Reader) + inverse(u, a) + mul(u, u, a) + if !u.equal(one) { + t.Fatal("(r*a) * r*(a^-1) == r)") + } + v := new(fe) + p := modulus.big() + exp(u, a, p.Sub(p, big.NewInt(2))) + inverse(v, a) + if !v.equal(u) { + t.Fatal("a^(p-2) == a^-1") + } + } +} + +func TestFpSquareRoot(t *testing.T) { + r := new(fe) + if sqrt(r, nonResidue1) { + t.Fatal("non residue cannot have a sqrt") + } + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + aa, rr, r := &fe{}, &fe{}, &fe{} + square(aa, a) + if !sqrt(r, aa) { + t.Fatal("bad sqrt 1") + } + square(rr, r) + if !rr.equal(aa) { + t.Fatal("bad sqrt 2") + } + } +} + +func TestFpNonResidue(t *testing.T) { + if !isQuadraticNonResidue(nonResidue1) { + t.Fatal("element is quadratic non residue, 1") + } + if isQuadraticNonResidue(new(fe).one()) { + t.Fatal("one is not quadratic non residue") + } + if !isQuadraticNonResidue(new(fe).zero()) { + t.Fatal("should accept zero as quadratic non residue") + } + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + square(a, a) + if isQuadraticNonResidue(new(fe).one()) { + t.Fatal("element is not quadratic non residue") + } + } + for i := 0; i < fuz; i++ { + a, _ := new(fe).rand(rand.Reader) + if !sqrt(new(fe), a) { + if !isQuadraticNonResidue(a) { + t.Fatal("element is quadratic non residue, 2", i) + } + } else { + i -= 1 + } + } +} + +func TestFp2Serialization(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + b, err := field.fromBytes(field.toBytes(a)) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad serialization") + } + } +} + +func TestFp2AdditionProperties(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + zero := field.zero() + a, _ := new(fe2).rand(rand.Reader) + b, _ := new(fe2).rand(rand.Reader) + c_1 := field.new() + c_2 := field.new() + field.add(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a + 0 == a") + } + field.sub(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a - 0 == a") + } + field.double(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("2 * 0 == 0") + } + field.neg(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("-0 == 0") + } + field.sub(c_1, zero, a) + field.neg(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("0-a == -a") + } + field.double(c_1, a) + field.add(c_2, a, a) + if !c_1.equal(c_2) { + t.Fatal("2 * a == a + a") + } + field.add(c_1, a, b) + field.add(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + field.sub(c_1, a, b) + field.sub(c_2, b, a) + field.neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + c_x, _ := new(fe2).rand(rand.Reader) + field.add(c_1, a, b) + field.add(c_1, c_1, c_x) + field.add(c_2, a, c_x) + field.add(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a + b) + c == (a + c ) + b") + } + field.sub(c_1, a, b) + field.sub(c_1, c_1, c_x) + field.sub(c_2, a, c_x) + field.sub(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a - b) - c == (a - c ) -b") + } + } +} + +func TestFp2AdditionPropertiesAssigned(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + zero := new(fe2).zero() + a, b := new(fe2), new(fe2) + _, _ = a.rand(rand.Reader) + b.set(a) + field.addAssign(a, zero) + if !a.equal(b) { + t.Fatal("a + 0 == a") + } + field.subAssign(a, zero) + if !a.equal(b) { + t.Fatal("a - 0 == a") + } + a.set(zero) + field.doubleAssign(a) + if !a.equal(zero) { + t.Fatal("2 * 0 == 0") + } + a.set(zero) + field.subAssign(a, b) + field.neg(b, b) + if !a.equal(b) { + t.Fatal("0-a == -a") + } + _, _ = a.rand(rand.Reader) + b.set(a) + field.doubleAssign(a) + field.addAssign(b, b) + if !a.equal(b) { + t.Fatal("2 * a == a + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c_1, c_2 := new(fe2).set(a), new(fe2).set(b) + field.addAssign(c_1, b) + field.addAssign(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c_1.set(a) + c_2.set(b) + field.subAssign(c_1, b) + field.subAssign(c_2, a) + field.neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c, _ := new(fe2).rand(rand.Reader) + a0 := new(fe2).set(a) + field.addAssign(a, b) + field.addAssign(a, c) + field.addAssign(b, c) + field.addAssign(b, a0) + if !a.equal(b) { + t.Fatal("(a + b) + c == (b + c) + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + _, _ = c.rand(rand.Reader) + a0.set(a) + field.subAssign(a, b) + field.subAssign(a, c) + field.subAssign(a0, c) + field.subAssign(a0, b) + if !a.equal(a0) { + t.Fatal("(a - b) - c == (a - c) -b") + } + } +} + +func TestFp2LazyOperations(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + b, _ := new(fe2).rand(rand.Reader) + c, _ := new(fe2).rand(rand.Reader) + c0 := new(fe2) + c1 := new(fe2) + field.ladd(c0, a, b) + field.add(c1, a, b) + field.mulAssign(c0, c) + field.mulAssign(c1, c) + if !c0.equal(c1) { + // l+ operator stands for lazy addition + t.Fatal("(a + b) * c == (a l+ b) * c") + } + _, _ = a.rand(rand.Reader) + b.set(a) + field.ldouble(a, a) + field.ladd(b, b, b) + if !a.equal(b) { + t.Fatal("2 l* a = a l+ a") + } + } +} + +func TestFp2MultiplicationProperties(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + b, _ := new(fe2).rand(rand.Reader) + zero := field.zero() + one := field.one() + c_1, c_2 := field.new(), field.new() + field.mul(c_1, a, zero) + if !c_1.equal(zero) { + t.Fatal("a * 0 == 0") + } + field.mul(c_1, a, one) + if !c_1.equal(a) { + t.Fatal("a * 1 == a") + } + field.mul(c_1, a, b) + field.mul(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a * b == b * a") + } + c_x, _ := new(fe2).rand(rand.Reader) + field.mul(c_1, a, b) + field.mul(c_1, c_1, c_x) + field.mul(c_2, c_x, b) + field.mul(c_2, c_2, a) + if !c_1.equal(c_2) { + t.Fatal("(a * b) * c == (a * c) * b") + } + field.square(a, zero) + if !a.equal(zero) { + t.Fatal("0^2 == 0") + } + field.square(a, one) + if !a.equal(one) { + t.Fatal("1^2 == 1") + } + _, _ = a.rand(rand.Reader) + field.square(c_1, a) + field.mul(c_2, a, a) + if !c_2.equal(c_1) { + t.Fatal("a^2 == a*a") + } + } +} + +func TestFp2MultiplicationPropertiesAssigned(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + zero, one := new(fe2).zero(), new(fe2).one() + field.mulAssign(a, zero) + if !a.equal(zero) { + t.Fatal("a * 0 == 0") + } + _, _ = a.rand(rand.Reader) + a0 := new(fe2).set(a) + field.mulAssign(a, one) + if !a.equal(a0) { + t.Fatal("a * 1 == a") + } + _, _ = a.rand(rand.Reader) + b, _ := new(fe2).rand(rand.Reader) + a0.set(a) + field.mulAssign(a, b) + field.mulAssign(b, a0) + if !a.equal(b) { + t.Fatal("a * b == b * a") + } + c, _ := new(fe2).rand(rand.Reader) + a0.set(a) + field.mulAssign(a, b) + field.mulAssign(a, c) + field.mulAssign(a0, c) + field.mulAssign(a0, b) + if !a.equal(a0) { + t.Fatal("(a * b) * c == (a * c) * b") + } + a0.set(a) + field.squareAssign(a) + field.mulAssign(a0, a0) + if !a.equal(a0) { + t.Fatal("a^2 == a*a") + } + } +} + +func TestFp2Exponentiation(t *testing.T) { + field := newFp2() + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + u := field.new() + field.exp(u, a, big.NewInt(0)) + if !u.equal(field.one()) { + t.Fatal("a^0 == 1") + } + field.exp(u, a, big.NewInt(1)) + if !u.equal(a) { + t.Fatal("a^1 == a") + } + v := field.new() + field.mul(u, a, a) + field.mul(u, u, u) + field.mul(u, u, u) + field.exp(v, a, big.NewInt(8)) + if !u.equal(v) { + t.Fatal("((a^2)^2)^2 == a^8") + } + } +} + +func TestFp2Inversion(t *testing.T) { + field := newFp2() + u := field.new() + zero := field.zero() + one := field.one() + field.inverse(u, zero) + if !u.equal(zero) { + t.Fatal("(0 ^ -1) == 0)") + } + field.inverse(u, one) + if !u.equal(one) { + t.Fatal("(1 ^ -1) == 1)") + } + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + field.inverse(u, a) + field.mul(u, u, a) + if !u.equal(one) { + t.Fatal("(r * a) * r * (a ^ -1) == r)") + } + } +} + +func TestFp2SquareRoot(t *testing.T) { + field := newFp2() + for z := 0; z < 1000; z++ { + zi := new(fe) + sub(zi, &modulus, &fe{uint64(z * z)}) + // r = (-z*z, 0) + r := &fe2{*zi, fe{0}} + toMont(&r[0], &r[0]) + toMont(&r[1], &r[1]) + c := field.new() + // sqrt((-z*z, 0)) = (0, z) + if !field.sqrt(c, r) { + t.Fatal("z*z does have a square root") + } + e := &fe2{fe{uint64(0)}, fe{uint64(z)}} + toMont(&e[0], &e[0]) + toMont(&e[1], &e[1]) + field.square(e, e) + field.square(c, c) + if !e.equal(c) { + t.Fatal("square root failed") + } + } + if field.sqrt(field.new(), nonResidue2) { + t.Fatal("non residue cannot have a sqrt") + } + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + aa, rr, r := field.new(), field.new(), field.new() + field.square(aa, a) + if !field.sqrt(r, aa) { + t.Fatal("bad sqrt 1") + } + field.square(rr, r) + if !rr.equal(aa) { + t.Fatal("bad sqrt 2") + } + } +} + +func TestFp2NonResidue(t *testing.T) { + field := newFp2() + if !field.isQuadraticNonResidue(nonResidue2) { + t.Fatal("element is quadratic non residue, 1") + } + if field.isQuadraticNonResidue(new(fe2).one()) { + t.Fatal("one is not quadratic non residue") + } + if !field.isQuadraticNonResidue(new(fe2).zero()) { + t.Fatal("should accept zero as quadratic non residue") + } + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + field.squareAssign(a) + if field.isQuadraticNonResidue(new(fe2).one()) { + t.Fatal("element is not quadratic non residue") + } + } + for i := 0; i < fuz; i++ { + a, _ := new(fe2).rand(rand.Reader) + if !field.sqrt(new(fe2), a) { + if !field.isQuadraticNonResidue(a) { + t.Fatal("element is quadratic non residue, 2", i) + } + } else { + i -= 1 + } + } +} + +func TestFp6Serialization(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe6).rand(rand.Reader) + b, err := field.fromBytes(field.toBytes(a)) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad serialization") + } + } +} + +func TestFp6AdditionProperties(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + zero := field.zero() + a, _ := new(fe6).rand(rand.Reader) + b, _ := new(fe6).rand(rand.Reader) + c_1 := field.new() + c_2 := field.new() + field.add(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a + 0 == a") + } + field.sub(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a - 0 == a") + } + field.double(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("2 * 0 == 0") + } + field.neg(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("-0 == 0") + } + field.sub(c_1, zero, a) + field.neg(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("0-a == -a") + } + field.double(c_1, a) + field.add(c_2, a, a) + if !c_1.equal(c_2) { + t.Fatal("2 * a == a + a") + } + field.add(c_1, a, b) + field.add(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + field.sub(c_1, a, b) + field.sub(c_2, b, a) + field.neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + c_x, _ := new(fe6).rand(rand.Reader) + field.add(c_1, a, b) + field.add(c_1, c_1, c_x) + field.add(c_2, a, c_x) + field.add(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a + b) + c == (a + c ) + b") + } + field.sub(c_1, a, b) + field.sub(c_1, c_1, c_x) + field.sub(c_2, a, c_x) + field.sub(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a - b) - c == (a - c ) -b") + } + } +} + +func TestFp6AdditionPropertiesAssigned(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + zero := new(fe6).zero() + a, b := new(fe6), new(fe6) + _, _ = a.rand(rand.Reader) + b.set(a) + field.addAssign(a, zero) + if !a.equal(b) { + t.Fatal("a + 0 == a") + } + field.subAssign(a, zero) + if !a.equal(b) { + t.Fatal("a - 0 == a") + } + a.set(zero) + field.doubleAssign(a) + if !a.equal(zero) { + t.Fatal("2 * 0 == 0") + } + a.set(zero) + field.subAssign(a, b) + field.neg(b, b) + if !a.equal(b) { + t.Fatal("0-a == -a") + } + _, _ = a.rand(rand.Reader) + b.set(a) + field.doubleAssign(a) + field.addAssign(b, b) + if !a.equal(b) { + t.Fatal("2 * a == a + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c_1, c_2 := new(fe6).set(a), new(fe6).set(b) + field.addAssign(c_1, b) + field.addAssign(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c_1.set(a) + c_2.set(b) + field.subAssign(c_1, b) + field.subAssign(c_2, a) + field.neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + c, _ := new(fe6).rand(rand.Reader) + a0 := new(fe6).set(a) + field.addAssign(a, b) + field.addAssign(a, c) + field.addAssign(b, c) + field.addAssign(b, a0) + if !a.equal(b) { + t.Fatal("(a + b) + c == (b + c) + a") + } + _, _ = a.rand(rand.Reader) + _, _ = b.rand(rand.Reader) + _, _ = c.rand(rand.Reader) + a0.set(a) + field.subAssign(a, b) + field.subAssign(a, c) + field.subAssign(a0, c) + field.subAssign(a0, b) + if !a.equal(a0) { + t.Fatal("(a - b) - c == (a - c) -b") + } + } +} + +func TestFp6SparseMultiplication(t *testing.T) { + fp6 := newFp6(nil) + var a, b, u *fe6 + for j := 0; j < fuz; j++ { + a, _ = new(fe6).rand(rand.Reader) + b, _ = new(fe6).rand(rand.Reader) + u, _ = new(fe6).rand(rand.Reader) + b[2].zero() + fp6.mul(u, a, b) + fp6.mulBy01(a, a, &b[0], &b[1]) + if !a.equal(u) { + t.Fatal("bad mul by 01") + } + } + for j := 0; j < fuz; j++ { + a, _ = new(fe6).rand(rand.Reader) + b, _ = new(fe6).rand(rand.Reader) + u, _ = new(fe6).rand(rand.Reader) + b[2].zero() + b[0].zero() + fp6.mul(u, a, b) + fp6.mulBy1(a, a, &b[1]) + if !a.equal(u) { + t.Fatal("bad mul by 1") + } + } +} + +func TestFp6MultiplicationProperties(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe6).rand(rand.Reader) + b, _ := new(fe6).rand(rand.Reader) + zero := field.zero() + one := field.one() + c_1, c_2 := field.new(), field.new() + field.mul(c_1, a, zero) + if !c_1.equal(zero) { + t.Fatal("a * 0 == 0") + } + field.mul(c_1, a, one) + if !c_1.equal(a) { + t.Fatal("a * 1 == a") + } + field.mul(c_1, a, b) + field.mul(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a * b == b * a") + } + c_x, _ := new(fe6).rand(rand.Reader) + field.mul(c_1, a, b) + field.mul(c_1, c_1, c_x) + field.mul(c_2, c_x, b) + field.mul(c_2, c_2, a) + if !c_1.equal(c_2) { + t.Fatal("(a * b) * c == (a * c) * b") + } + field.square(a, zero) + if !a.equal(zero) { + t.Fatal("0^2 == 0") + } + field.square(a, one) + if !a.equal(one) { + t.Fatal("1^2 == 1") + } + _, _ = a.rand(rand.Reader) + field.square(c_1, a) + field.mul(c_2, a, a) + if !c_2.equal(c_1) { + t.Fatal("a^2 == a*a") + } + } +} + +func TestFp6MultiplicationPropertiesAssigned(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe6).rand(rand.Reader) + zero, one := new(fe6).zero(), new(fe6).one() + field.mulAssign(a, zero) + if !a.equal(zero) { + t.Fatal("a * 0 == 0") + } + _, _ = a.rand(rand.Reader) + a0 := new(fe6).set(a) + field.mulAssign(a, one) + if !a.equal(a0) { + t.Fatal("a * 1 == a") + } + _, _ = a.rand(rand.Reader) + b, _ := new(fe6).rand(rand.Reader) + a0.set(a) + field.mulAssign(a, b) + field.mulAssign(b, a0) + if !a.equal(b) { + t.Fatal("a * b == b * a") + } + c, _ := new(fe6).rand(rand.Reader) + a0.set(a) + field.mulAssign(a, b) + field.mulAssign(a, c) + field.mulAssign(a0, c) + field.mulAssign(a0, b) + if !a.equal(a0) { + t.Fatal("(a * b) * c == (a * c) * b") + } + } +} + +func TestFp6Exponentiation(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe6).rand(rand.Reader) + u := field.new() + field.exp(u, a, big.NewInt(0)) + if !u.equal(field.one()) { + t.Fatal("a^0 == 1") + } + field.exp(u, a, big.NewInt(1)) + if !u.equal(a) { + t.Fatal("a^1 == a") + } + v := field.new() + field.mul(u, a, a) + field.mul(u, u, u) + field.mul(u, u, u) + field.exp(v, a, big.NewInt(8)) + if !u.equal(v) { + t.Fatal("((a^2)^2)^2 == a^8") + } + } +} + +func TestFp6Inversion(t *testing.T) { + field := newFp6(nil) + for i := 0; i < fuz; i++ { + u := field.new() + zero := field.zero() + one := field.one() + field.inverse(u, zero) + if !u.equal(zero) { + t.Fatal("(0^-1) == 0)") + } + field.inverse(u, one) + if !u.equal(one) { + t.Fatal("(1^-1) == 1)") + } + a, _ := new(fe6).rand(rand.Reader) + field.inverse(u, a) + field.mul(u, u, a) + if !u.equal(one) { + t.Fatal("(r*a) * r*(a^-1) == r)") + } + } +} + +func TestFp12Serialization(t *testing.T) { + field := newFp12(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe12).rand(rand.Reader) + b, err := field.fromBytes(field.toBytes(a)) + if err != nil { + t.Fatal(err) + } + if !a.equal(b) { + t.Fatal("bad serialization") + } + } +} + +func TestFp12AdditionProperties(t *testing.T) { + field := newFp12(nil) + for i := 0; i < fuz; i++ { + zero := field.zero() + a, _ := new(fe12).rand(rand.Reader) + b, _ := new(fe12).rand(rand.Reader) + c_1 := field.new() + c_2 := field.new() + field.add(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a + 0 == a") + } + field.sub(c_1, a, zero) + if !c_1.equal(a) { + t.Fatal("a - 0 == a") + } + field.double(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("2 * 0 == 0") + } + field.neg(c_1, zero) + if !c_1.equal(zero) { + t.Fatal("-0 == 0") + } + field.sub(c_1, zero, a) + field.neg(c_2, a) + if !c_1.equal(c_2) { + t.Fatal("0-a == -a") + } + field.double(c_1, a) + field.add(c_2, a, a) + if !c_1.equal(c_2) { + t.Fatal("2 * a == a + a") + } + field.add(c_1, a, b) + field.add(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a + b = b + a") + } + field.sub(c_1, a, b) + field.sub(c_2, b, a) + field.neg(c_2, c_2) + if !c_1.equal(c_2) { + t.Fatal("a - b = - ( b - a )") + } + c_x, _ := new(fe12).rand(rand.Reader) + field.add(c_1, a, b) + field.add(c_1, c_1, c_x) + field.add(c_2, a, c_x) + field.add(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a + b) + c == (a + c ) + b") + } + field.sub(c_1, a, b) + field.sub(c_1, c_1, c_x) + field.sub(c_2, a, c_x) + field.sub(c_2, c_2, b) + if !c_1.equal(c_2) { + t.Fatal("(a - b) - c == (a - c ) -b") + } + } +} + +func TestFp12MultiplicationProperties(t *testing.T) { + field := newFp12(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe12).rand(rand.Reader) + b, _ := new(fe12).rand(rand.Reader) + zero := field.zero() + one := field.one() + c_1, c_2 := field.new(), field.new() + field.mul(c_1, a, zero) + if !c_1.equal(zero) { + t.Fatal("a * 0 == 0") + } + field.mul(c_1, a, one) + if !c_1.equal(a) { + t.Fatal("a * 1 == a") + } + field.mul(c_1, a, b) + field.mul(c_2, b, a) + if !c_1.equal(c_2) { + t.Fatal("a * b == b * a") + } + c_x, _ := new(fe12).rand(rand.Reader) + field.mul(c_1, a, b) + field.mul(c_1, c_1, c_x) + field.mul(c_2, c_x, b) + field.mul(c_2, c_2, a) + if !c_1.equal(c_2) { + t.Fatal("(a * b) * c == (a * c) * b") + } + field.square(a, zero) + if !a.equal(zero) { + t.Fatal("0^2 == 0") + } + field.square(a, one) + if !a.equal(one) { + t.Fatal("1^2 == 1") + } + _, _ = a.rand(rand.Reader) + field.square(c_1, a) + field.mul(c_2, a, a) + if !c_2.equal(c_1) { + t.Fatal("a^2 == a*a") + } + } +} + +func TestFp12MultiplicationPropertiesAssigned(t *testing.T) { + field := newFp12(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe12).rand(rand.Reader) + zero, one := new(fe12).zero(), new(fe12).one() + field.mulAssign(a, zero) + if !a.equal(zero) { + t.Fatal("a * 0 == 0") + } + _, _ = a.rand(rand.Reader) + a0 := new(fe12).set(a) + field.mulAssign(a, one) + if !a.equal(a0) { + t.Fatal("a * 1 == a") + } + _, _ = a.rand(rand.Reader) + b, _ := new(fe12).rand(rand.Reader) + a0.set(a) + field.mulAssign(a, b) + field.mulAssign(b, a0) + if !a.equal(b) { + t.Fatal("a * b == b * a") + } + c, _ := new(fe12).rand(rand.Reader) + a0.set(a) + field.mulAssign(a, b) + field.mulAssign(a, c) + field.mulAssign(a0, c) + field.mulAssign(a0, b) + if !a.equal(a0) { + t.Fatal("(a * b) * c == (a * c) * b") + } + } +} + +func TestFp12SparseMultiplication(t *testing.T) { + fp12 := newFp12(nil) + var a, b, u *fe12 + for j := 0; j < fuz; j++ { + a, _ = new(fe12).rand(rand.Reader) + b, _ = new(fe12).rand(rand.Reader) + u, _ = new(fe12).rand(rand.Reader) + b[0][2].zero() + b[1][0].zero() + b[1][2].zero() + fp12.mul(u, a, b) + fp12.mulBy014Assign(a, &b[0][0], &b[0][1], &b[1][1]) + if !a.equal(u) { + t.Fatal("bad mul by 01") + } + } +} + +func TestFp12Exponentiation(t *testing.T) { + field := newFp12(nil) + for i := 0; i < fuz; i++ { + a, _ := new(fe12).rand(rand.Reader) + u := field.new() + field.exp(u, a, big.NewInt(0)) + if !u.equal(field.one()) { + t.Fatal("a^0 == 1") + } + field.exp(u, a, big.NewInt(1)) + if !u.equal(a) { + t.Fatal("a^1 == a") + } + v := field.new() + field.mul(u, a, a) + field.mul(u, u, u) + field.mul(u, u, u) + field.exp(v, a, big.NewInt(8)) + if !u.equal(v) { + t.Fatal("((a^2)^2)^2 == a^8") + } + } +} + +func TestFp12Inversion(t *testing.T) { + field := newFp12(nil) + for i := 0; i < fuz; i++ { + u := field.new() + zero := field.zero() + one := field.one() + field.inverse(u, zero) + if !u.equal(zero) { + t.Fatal("(0^-1) == 0)") + } + field.inverse(u, one) + if !u.equal(one) { + t.Fatal("(1^-1) == 1)") + } + a, _ := new(fe12).rand(rand.Reader) + field.inverse(u, a) + field.mul(u, u, a) + if !u.equal(one) { + t.Fatal("(r*a) * r*(a^-1) == r)") + } + } +} + +func BenchmarkMultiplication(t *testing.B) { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + c, _ := new(fe).rand(rand.Reader) + t.ResetTimer() + for i := 0; i < t.N; i++ { + mul(c, a, b) + } +} + +func BenchmarkInverse(t *testing.B) { + a, _ := new(fe).rand(rand.Reader) + b, _ := new(fe).rand(rand.Reader) + t.ResetTimer() + for i := 0; i < t.N; i++ { + inverse(a, b) + } +} + +func padBytes(in []byte, size int) []byte { + out := make([]byte, size) + if len(in) > size { + panic("bad input for padding") + } + copy(out[size-len(in):], in) + return out +} diff --git a/crypto/bls12381/g1.go b/crypto/bls12381/g1.go new file mode 100644 index 0000000000..bcb898027a --- /dev/null +++ b/crypto/bls12381/g1.go @@ -0,0 +1,434 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math" + "math/big" +) + +// PointG1 is type for point in G1. +// PointG1 is both used for Affine and Jacobian point representation. +// If z is equal to one the point is considered as in affine form. +type PointG1 [3]fe + +func (p *PointG1) Set(p2 *PointG1) *PointG1 { + p[0].set(&p2[0]) + p[1].set(&p2[1]) + p[2].set(&p2[2]) + return p +} + +// Zero returns G1 point in point at infinity representation +func (p *PointG1) Zero() *PointG1 { + p[0].zero() + p[1].one() + p[2].zero() + return p +} + +type tempG1 struct { + t [9]*fe +} + +// G1 is struct for G1 group. +type G1 struct { + tempG1 +} + +// NewG1 constructs a new G1 instance. +func NewG1() *G1 { + t := newTempG1() + return &G1{t} +} + +func newTempG1() tempG1 { + t := [9]*fe{} + for i := 0; i < 9; i++ { + t[i] = &fe{} + } + return tempG1{t} +} + +// Q returns group order in big.Int. +func (g *G1) Q() *big.Int { + return new(big.Int).Set(q) +} + +func (g *G1) fromBytesUnchecked(in []byte) (*PointG1, error) { + p0, err := fromBytes(in[:48]) + if err != nil { + return nil, err + } + p1, err := fromBytes(in[48:]) + if err != nil { + return nil, err + } + p2 := new(fe).one() + return &PointG1{*p0, *p1, *p2}, nil +} + +// FromBytes constructs a new point given uncompressed byte input. +// FromBytes does not take zcash flags into account. +// Byte input expected to be larger than 96 bytes. +// First 96 bytes should be concatenation of x and y values. +// Point (0, 0) is considered as infinity. +func (g *G1) FromBytes(in []byte) (*PointG1, error) { + if len(in) != 96 { + return nil, errors.New("input string should be equal or larger than 96") + } + p0, err := fromBytes(in[:48]) + if err != nil { + return nil, err + } + p1, err := fromBytes(in[48:]) + if err != nil { + return nil, err + } + // check if given input points to infinity + if p0.isZero() && p1.isZero() { + return g.Zero(), nil + } + p2 := new(fe).one() + p := &PointG1{*p0, *p1, *p2} + if !g.IsOnCurve(p) { + return nil, errors.New("point is not on curve") + } + return p, nil +} + +// DecodePoint given encoded (x, y) coordinates in 128 bytes returns a valid G1 Point. +func (g *G1) DecodePoint(in []byte) (*PointG1, error) { + if len(in) != 128 { + return nil, errors.New("invalid g1 point length") + } + pointBytes := make([]byte, 96) + // decode x + xBytes, err := decodeFieldElement(in[:64]) + if err != nil { + return nil, err + } + // decode y + yBytes, err := decodeFieldElement(in[64:]) + if err != nil { + return nil, err + } + copy(pointBytes[:48], xBytes) + copy(pointBytes[48:], yBytes) + return g.FromBytes(pointBytes) +} + +// ToBytes serializes a point into bytes in uncompressed form. +// ToBytes does not take zcash flags into account. +// ToBytes returns (0, 0) if point is infinity. +func (g *G1) ToBytes(p *PointG1) []byte { + out := make([]byte, 96) + if g.IsZero(p) { + return out + } + g.Affine(p) + copy(out[:48], toBytes(&p[0])) + copy(out[48:], toBytes(&p[1])) + return out +} + +// EncodePoint encodes a point into 128 bytes. +func (g *G1) EncodePoint(p *PointG1) []byte { + outRaw := g.ToBytes(p) + out := make([]byte, 128) + // encode x + copy(out[16:], outRaw[:48]) + // encode y + copy(out[64+16:], outRaw[48:]) + return out +} + +// New creates a new G1 Point which is equal to zero in other words point at infinity. +func (g *G1) New() *PointG1 { + return g.Zero() +} + +// Zero returns a new G1 Point which is equal to point at infinity. +func (g *G1) Zero() *PointG1 { + return new(PointG1).Zero() +} + +// One returns a new G1 Point which is equal to generator point. +func (g *G1) One() *PointG1 { + p := &PointG1{} + return p.Set(&g1One) +} + +// IsZero returns true if given point is equal to zero. +func (g *G1) IsZero(p *PointG1) bool { + return p[2].isZero() +} + +// Equal checks if given two G1 point is equal in their affine form. +func (g *G1) Equal(p1, p2 *PointG1) bool { + if g.IsZero(p1) { + return g.IsZero(p2) + } + if g.IsZero(p2) { + return g.IsZero(p1) + } + t := g.t + square(t[0], &p1[2]) + square(t[1], &p2[2]) + mul(t[2], t[0], &p2[0]) + mul(t[3], t[1], &p1[0]) + mul(t[0], t[0], &p1[2]) + mul(t[1], t[1], &p2[2]) + mul(t[1], t[1], &p1[1]) + mul(t[0], t[0], &p2[1]) + return t[0].equal(t[1]) && t[2].equal(t[3]) +} + +// InCorrectSubgroup checks whether given point is in correct subgroup. +func (g *G1) InCorrectSubgroup(p *PointG1) bool { + tmp := &PointG1{} + g.MulScalar(tmp, p, q) + return g.IsZero(tmp) +} + +// IsOnCurve checks a G1 point is on curve. +func (g *G1) IsOnCurve(p *PointG1) bool { + if g.IsZero(p) { + return true + } + t := g.t + square(t[0], &p[1]) + square(t[1], &p[0]) + mul(t[1], t[1], &p[0]) + square(t[2], &p[2]) + square(t[3], t[2]) + mul(t[2], t[2], t[3]) + mul(t[2], b, t[2]) + add(t[1], t[1], t[2]) + return t[0].equal(t[1]) +} + +// IsAffine checks a G1 point whether it is in affine form. +func (g *G1) IsAffine(p *PointG1) bool { + return p[2].isOne() +} + +// Affine calculates affine form of given G1 point. +func (g *G1) Affine(p *PointG1) *PointG1 { + if g.IsZero(p) { + return p + } + if !g.IsAffine(p) { + t := g.t + inverse(t[0], &p[2]) + square(t[1], t[0]) + mul(&p[0], &p[0], t[1]) + mul(t[0], t[0], t[1]) + mul(&p[1], &p[1], t[0]) + p[2].one() + } + return p +} + +// Add adds two G1 points p1, p2 and assigns the result to point at first argument. +func (g *G1) Add(r, p1, p2 *PointG1) *PointG1 { + // www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + if g.IsZero(p1) { + return r.Set(p2) + } + if g.IsZero(p2) { + return r.Set(p1) + } + t := g.t + square(t[7], &p1[2]) + mul(t[1], &p2[0], t[7]) + mul(t[2], &p1[2], t[7]) + mul(t[0], &p2[1], t[2]) + square(t[8], &p2[2]) + mul(t[3], &p1[0], t[8]) + mul(t[4], &p2[2], t[8]) + mul(t[2], &p1[1], t[4]) + if t[1].equal(t[3]) { + if t[0].equal(t[2]) { + return g.Double(r, p1) + } + return r.Zero() + } + sub(t[1], t[1], t[3]) + double(t[4], t[1]) + square(t[4], t[4]) + mul(t[5], t[1], t[4]) + sub(t[0], t[0], t[2]) + double(t[0], t[0]) + square(t[6], t[0]) + sub(t[6], t[6], t[5]) + mul(t[3], t[3], t[4]) + double(t[4], t[3]) + sub(&r[0], t[6], t[4]) + sub(t[4], t[3], &r[0]) + mul(t[6], t[2], t[5]) + double(t[6], t[6]) + mul(t[0], t[0], t[4]) + sub(&r[1], t[0], t[6]) + add(t[0], &p1[2], &p2[2]) + square(t[0], t[0]) + sub(t[0], t[0], t[7]) + sub(t[0], t[0], t[8]) + mul(&r[2], t[0], t[1]) + return r +} + +// Double doubles a G1 point p and assigns the result to the point at first argument. +func (g *G1) Double(r, p *PointG1) *PointG1 { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + if g.IsZero(p) { + return r.Set(p) + } + t := g.t + square(t[0], &p[0]) + square(t[1], &p[1]) + square(t[2], t[1]) + add(t[1], &p[0], t[1]) + square(t[1], t[1]) + sub(t[1], t[1], t[0]) + sub(t[1], t[1], t[2]) + double(t[1], t[1]) + double(t[3], t[0]) + add(t[0], t[3], t[0]) + square(t[4], t[0]) + double(t[3], t[1]) + sub(&r[0], t[4], t[3]) + sub(t[1], t[1], &r[0]) + double(t[2], t[2]) + double(t[2], t[2]) + double(t[2], t[2]) + mul(t[0], t[0], t[1]) + sub(t[1], t[0], t[2]) + mul(t[0], &p[1], &p[2]) + r[1].set(t[1]) + double(&r[2], t[0]) + return r +} + +// Neg negates a G1 point p and assigns the result to the point at first argument. +func (g *G1) Neg(r, p *PointG1) *PointG1 { + r[0].set(&p[0]) + r[2].set(&p[2]) + neg(&r[1], &p[1]) + return r +} + +// Sub subtracts two G1 points p1, p2 and assigns the result to point at first argument. +func (g *G1) Sub(c, a, b *PointG1) *PointG1 { + d := &PointG1{} + g.Neg(d, b) + g.Add(c, a, d) + return c +} + +// MulScalar multiplies a point by given scalar value in big.Int and assigns the result to point at first argument. +func (g *G1) MulScalar(c, p *PointG1, e *big.Int) *PointG1 { + q, n := &PointG1{}, &PointG1{} + n.Set(p) + l := e.BitLen() + for i := 0; i < l; i++ { + if e.Bit(i) == 1 { + g.Add(q, q, n) + } + g.Double(n, n) + } + return c.Set(q) +} + +// ClearCofactor maps given a G1 point to correct subgroup +func (g *G1) ClearCofactor(p *PointG1) { + g.MulScalar(p, p, cofactorEFFG1) +} + +// MultiExp calculates multi exponentiation. Given pairs of G1 point and scalar values +// (P_0, e_0), (P_1, e_1), ... (P_n, e_n) calculates r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n +// Length of points and scalars are expected to be equal, otherwise an error is returned. +// Result is assigned to point at first argument. +func (g *G1) MultiExp(r *PointG1, points []*PointG1, powers []*big.Int) (*PointG1, error) { + if len(points) != len(powers) { + return nil, errors.New("point and scalar vectors should be in same length") + } + var c uint32 = 3 + if len(powers) >= 32 { + c = uint32(math.Ceil(math.Log10(float64(len(powers))))) + } + bucketSize, numBits := (1<= 0; i-- { + g.Add(sum, sum, bucket[i]) + g.Add(acc, acc, sum) + } + windows[j] = g.New() + windows[j].Set(acc) + j++ + cur += c + } + acc.Zero() + for i := len(windows) - 1; i >= 0; i-- { + for j := uint32(0); j < c; j++ { + g.Double(acc, acc) + } + g.Add(acc, acc, windows[i]) + } + return r.Set(acc), nil +} + +// MapToCurve given a byte slice returns a valid G1 point. +// This mapping function implements the Simplified Shallue-van de Woestijne-Ulas method. +// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06 +// Input byte slice should be a valid field element, otherwise an error is returned. +func (g *G1) MapToCurve(in []byte) (*PointG1, error) { + u, err := fromBytes(in) + if err != nil { + return nil, err + } + x, y := swuMapG1(u) + isogenyMapG1(x, y) + one := new(fe).one() + p := &PointG1{*x, *y, *one} + g.ClearCofactor(p) + return g.Affine(p), nil +} diff --git a/crypto/bls12381/g1_test.go b/crypto/bls12381/g1_test.go new file mode 100644 index 0000000000..87140459fb --- /dev/null +++ b/crypto/bls12381/g1_test.go @@ -0,0 +1,284 @@ +package bls12381 + +import ( + "bytes" + "crypto/rand" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func (g *G1) one() *PointG1 { + one, _ := g.fromBytesUnchecked( + common.FromHex("" + + "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + + "08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1", + ), + ) + return one +} + +func (g *G1) rand() *PointG1 { + k, err := rand.Int(rand.Reader, q) + if err != nil { + panic(err) + } + return g.MulScalar(&PointG1{}, g.one(), k) +} + +func TestG1Serialization(t *testing.T) { + g1 := NewG1() + for i := 0; i < fuz; i++ { + a := g1.rand() + buf := g1.ToBytes(a) + b, err := g1.FromBytes(buf) + if err != nil { + t.Fatal(err) + } + if !g1.Equal(a, b) { + t.Fatal("bad serialization from/to") + } + } + for i := 0; i < fuz; i++ { + a := g1.rand() + encoded := g1.EncodePoint(a) + b, err := g1.DecodePoint(encoded) + if err != nil { + t.Fatal(err) + } + if !g1.Equal(a, b) { + t.Fatal("bad serialization encode/decode") + } + } +} + +func TestG1IsOnCurve(t *testing.T) { + g := NewG1() + zero := g.Zero() + if !g.IsOnCurve(zero) { + t.Fatal("zero must be on curve") + } + one := new(fe).one() + p := &PointG1{*one, *one, *one} + if g.IsOnCurve(p) { + t.Fatal("(1, 1) is not on curve") + } +} + +func TestG1AdditiveProperties(t *testing.T) { + g := NewG1() + t0, t1 := g.New(), g.New() + zero := g.Zero() + for i := 0; i < fuz; i++ { + a, b := g.rand(), g.rand() + g.Add(t0, a, zero) + if !g.Equal(t0, a) { + t.Fatal("a + 0 == a") + } + g.Add(t0, zero, zero) + if !g.Equal(t0, zero) { + t.Fatal("0 + 0 == 0") + } + g.Sub(t0, a, zero) + if !g.Equal(t0, a) { + t.Fatal("a - 0 == a") + } + g.Sub(t0, zero, zero) + if !g.Equal(t0, zero) { + t.Fatal("0 - 0 == 0") + } + g.Neg(t0, zero) + if !g.Equal(t0, zero) { + t.Fatal("- 0 == 0") + } + g.Sub(t0, zero, a) + g.Neg(t0, t0) + if !g.Equal(t0, a) { + t.Fatal(" - (0 - a) == a") + } + g.Double(t0, zero) + if !g.Equal(t0, zero) { + t.Fatal("2 * 0 == 0") + } + g.Double(t0, a) + g.Sub(t0, t0, a) + if !g.Equal(t0, a) || !g.IsOnCurve(t0) { + t.Fatal(" (2 * a) - a == a") + } + g.Add(t0, a, b) + g.Add(t1, b, a) + if !g.Equal(t0, t1) { + t.Fatal("a + b == b + a") + } + g.Sub(t0, a, b) + g.Sub(t1, b, a) + g.Neg(t1, t1) + if !g.Equal(t0, t1) { + t.Fatal("a - b == - ( b - a )") + } + c := g.rand() + g.Add(t0, a, b) + g.Add(t0, t0, c) + g.Add(t1, a, c) + g.Add(t1, t1, b) + if !g.Equal(t0, t1) { + t.Fatal("(a + b) + c == (a + c ) + b") + } + g.Sub(t0, a, b) + g.Sub(t0, t0, c) + g.Sub(t1, a, c) + g.Sub(t1, t1, b) + if !g.Equal(t0, t1) { + t.Fatal("(a - b) - c == (a - c) -b") + } + } +} + +func TestG1MultiplicativeProperties(t *testing.T) { + g := NewG1() + t0, t1 := g.New(), g.New() + zero := g.Zero() + for i := 0; i < fuz; i++ { + a := g.rand() + s1, s2, s3 := randScalar(q), randScalar(q), randScalar(q) + sone := big.NewInt(1) + g.MulScalar(t0, zero, s1) + if !g.Equal(t0, zero) { + t.Fatal(" 0 ^ s == 0") + } + g.MulScalar(t0, a, sone) + if !g.Equal(t0, a) { + t.Fatal(" a ^ 1 == a") + } + g.MulScalar(t0, zero, s1) + if !g.Equal(t0, zero) { + t.Fatal(" 0 ^ s == a") + } + g.MulScalar(t0, a, s1) + g.MulScalar(t0, t0, s2) + s3.Mul(s1, s2) + g.MulScalar(t1, a, s3) + if !g.Equal(t0, t1) { + t.Errorf(" (a ^ s1) ^ s2 == a ^ (s1 * s2)") + } + g.MulScalar(t0, a, s1) + g.MulScalar(t1, a, s2) + g.Add(t0, t0, t1) + s3.Add(s1, s2) + g.MulScalar(t1, a, s3) + if !g.Equal(t0, t1) { + t.Errorf(" (a ^ s1) + (a ^ s2) == a ^ (s1 + s2)") + } + } +} + +func TestG1MultiExpExpected(t *testing.T) { + g := NewG1() + one := g.one() + var scalars [2]*big.Int + var bases [2]*PointG1 + scalars[0] = big.NewInt(2) + scalars[1] = big.NewInt(3) + bases[0], bases[1] = new(PointG1).Set(one), new(PointG1).Set(one) + expected, result := g.New(), g.New() + g.MulScalar(expected, one, big.NewInt(5)) + _, _ = g.MultiExp(result, bases[:], scalars[:]) + if !g.Equal(expected, result) { + t.Fatal("bad multi-exponentiation") + } +} + +func TestG1MultiExpBatch(t *testing.T) { + g := NewG1() + one := g.one() + n := 1000 + bases := make([]*PointG1, n) + scalars := make([]*big.Int, n) + // scalars: [s0,s1 ... s(n-1)] + // bases: [P0,P1,..P(n-1)] = [s(n-1)*G, s(n-2)*G ... s0*G] + for i, j := 0, n-1; i < n; i, j = i+1, j-1 { + scalars[j], _ = rand.Int(rand.Reader, big.NewInt(100000)) + bases[i] = g.New() + g.MulScalar(bases[i], one, scalars[j]) + } + // expected: s(n-1)*P0 + s(n-2)*P1 + s0*P(n-1) + expected, tmp := g.New(), g.New() + for i := 0; i < n; i++ { + g.MulScalar(tmp, bases[i], scalars[i]) + g.Add(expected, expected, tmp) + } + result := g.New() + _, _ = g.MultiExp(result, bases, scalars) + if !g.Equal(expected, result) { + t.Fatal("bad multi-exponentiation") + } +} + +func TestG1MapToCurve(t *testing.T) { + for i, v := range []struct { + u []byte + expected []byte + }{ + { + u: make([]byte, 48), + expected: common.FromHex("11a9a0372b8f332d5c30de9ad14e50372a73fa4c45d5f2fa5097f2d6fb93bcac592f2e1711ac43db0519870c7d0ea415" + "092c0f994164a0719f51c24ba3788de240ff926b55f58c445116e8bc6a47cd63392fd4e8e22bdf9feaa96ee773222133"), + }, + { + u: common.FromHex("07fdf49ea58e96015d61f6b5c9d1c8f277146a533ae7fbca2a8ef4c41055cd961fbc6e26979b5554e4b4f22330c0e16d"), + expected: common.FromHex("1223effdbb2d38152495a864d78eee14cb0992d89a241707abb03819a91a6d2fd65854ab9a69e9aacb0cbebfd490732c" + "0f925d61e0b235ecd945cbf0309291878df0d06e5d80d6b84aa4ff3e00633b26f9a7cb3523ef737d90e6d71e8b98b2d5"), + }, + { + u: common.FromHex("1275ab3adbf824a169ed4b1fd669b49cf406d822f7fe90d6b2f8c601b5348436f89761bb1ad89a6fb1137cd91810e5d2"), + expected: common.FromHex("179d3fd0b4fb1da43aad06cea1fb3f828806ddb1b1fa9424b1e3944dfdbab6e763c42636404017da03099af0dcca0fd6" + "0d037cb1c6d495c0f5f22b061d23f1be3d7fe64d3c6820cfcd99b6b36fa69f7b4c1f4addba2ae7aa46fb25901ab483e4"), + }, + { + u: common.FromHex("0e93d11d30de6d84b8578827856f5c05feef36083eef0b7b263e35ecb9b56e86299614a042e57d467fa20948e8564909"), + expected: common.FromHex("15aa66c77eded1209db694e8b1ba49daf8b686733afaa7b68c683d0b01788dfb0617a2e2d04c0856db4981921d3004af" + "0952bb2f61739dd1d201dd0a79d74cda3285403d47655ee886afe860593a8a4e51c5b77a22d2133e3a4280eaaaa8b788"), + }, + { + u: common.FromHex("015a41481155d17074d20be6d8ec4d46632a51521cd9c916e265bd9b47343b3689979b50708c8546cbc2916b86cb1a3a"), + expected: common.FromHex("06328ce5106e837935e8da84bd9af473422e62492930aa5f460369baad9545defa468d9399854c23a75495d2a80487ee" + "094bfdfe3e552447433b5a00967498a3f1314b86ce7a7164c8a8f4131f99333b30a574607e301d5f774172c627fd0bca"), + }, + } { + g := NewG1() + p0, err := g.MapToCurve(v.u) + if err != nil { + t.Fatal("map to curve fails", i, err) + } + if !bytes.Equal(g.ToBytes(p0), v.expected) { + t.Fatal("map to curve fails", i) + } + } +} + +func BenchmarkG1Add(t *testing.B) { + g1 := NewG1() + a, b, c := g1.rand(), g1.rand(), PointG1{} + t.ResetTimer() + for i := 0; i < t.N; i++ { + g1.Add(&c, a, b) + } +} + +func BenchmarkG1Mul(t *testing.B) { + worstCaseScalar, _ := new(big.Int).SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) + g1 := NewG1() + a, e, c := g1.rand(), worstCaseScalar, PointG1{} + t.ResetTimer() + for i := 0; i < t.N; i++ { + g1.MulScalar(&c, a, e) + } +} + +func BenchmarkG1MapToCurve(t *testing.B) { + a := make([]byte, 48) + g1 := NewG1() + t.ResetTimer() + for i := 0; i < t.N; i++ { + _, err := g1.MapToCurve(a) + if err != nil { + t.Fatal(err) + } + } +} diff --git a/crypto/bls12381/g2.go b/crypto/bls12381/g2.go new file mode 100644 index 0000000000..b942bf94fd --- /dev/null +++ b/crypto/bls12381/g2.go @@ -0,0 +1,455 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math" + "math/big" +) + +// PointG2 is type for point in G2. +// PointG2 is both used for Affine and Jacobian point representation. +// If z is equal to one the point is considered as in affine form. +type PointG2 [3]fe2 + +// Set copies values of one point to another. +func (p *PointG2) Set(p2 *PointG2) *PointG2 { + p[0].set(&p2[0]) + p[1].set(&p2[1]) + p[2].set(&p2[2]) + return p +} + +// Zero returns G2 point in point at infinity representation +func (p *PointG2) Zero() *PointG2 { + p[0].zero() + p[1].one() + p[2].zero() + return p +} + +type tempG2 struct { + t [9]*fe2 +} + +// G2 is struct for G2 group. +type G2 struct { + f *fp2 + tempG2 +} + +// NewG2 constructs a new G2 instance. +func NewG2() *G2 { + return newG2(nil) +} + +func newG2(f *fp2) *G2 { + if f == nil { + f = newFp2() + } + t := newTempG2() + return &G2{f, t} +} + +func newTempG2() tempG2 { + t := [9]*fe2{} + for i := 0; i < 9; i++ { + t[i] = &fe2{} + } + return tempG2{t} +} + +// Q returns group order in big.Int. +func (g *G2) Q() *big.Int { + return new(big.Int).Set(q) +} + +func (g *G2) fromBytesUnchecked(in []byte) (*PointG2, error) { + p0, err := g.f.fromBytes(in[:96]) + if err != nil { + return nil, err + } + p1, err := g.f.fromBytes(in[96:]) + if err != nil { + return nil, err + } + p2 := new(fe2).one() + return &PointG2{*p0, *p1, *p2}, nil +} + +// FromBytes constructs a new point given uncompressed byte input. +// FromBytes does not take zcash flags into account. +// Byte input expected to be larger than 96 bytes. +// First 192 bytes should be concatenation of x and y values +// Point (0, 0) is considered as infinity. +func (g *G2) FromBytes(in []byte) (*PointG2, error) { + if len(in) != 192 { + return nil, errors.New("input string should be equal or larger than 192") + } + p0, err := g.f.fromBytes(in[:96]) + if err != nil { + return nil, err + } + p1, err := g.f.fromBytes(in[96:]) + if err != nil { + return nil, err + } + // check if given input points to infinity + if p0.isZero() && p1.isZero() { + return g.Zero(), nil + } + p2 := new(fe2).one() + p := &PointG2{*p0, *p1, *p2} + if !g.IsOnCurve(p) { + return nil, errors.New("point is not on curve") + } + return p, nil +} + +// DecodePoint given encoded (x, y) coordinates in 256 bytes returns a valid G2 Point. +func (g *G2) DecodePoint(in []byte) (*PointG2, error) { + if len(in) != 256 { + return nil, errors.New("invalid g2 point length") + } + pointBytes := make([]byte, 192) + x0Bytes, err := decodeFieldElement(in[:64]) + if err != nil { + return nil, err + } + x1Bytes, err := decodeFieldElement(in[64:128]) + if err != nil { + return nil, err + } + y0Bytes, err := decodeFieldElement(in[128:192]) + if err != nil { + return nil, err + } + y1Bytes, err := decodeFieldElement(in[192:]) + if err != nil { + return nil, err + } + copy(pointBytes[:48], x1Bytes) + copy(pointBytes[48:96], x0Bytes) + copy(pointBytes[96:144], y1Bytes) + copy(pointBytes[144:192], y0Bytes) + return g.FromBytes(pointBytes) +} + +// ToBytes serializes a point into bytes in uncompressed form, +// does not take zcash flags into account, +// returns (0, 0) if point is infinity. +func (g *G2) ToBytes(p *PointG2) []byte { + out := make([]byte, 192) + if g.IsZero(p) { + return out + } + g.Affine(p) + copy(out[:96], g.f.toBytes(&p[0])) + copy(out[96:], g.f.toBytes(&p[1])) + return out +} + +// EncodePoint encodes a point into 256 bytes. +func (g *G2) EncodePoint(p *PointG2) []byte { + // outRaw is 96 bytes + outRaw := g.ToBytes(p) + out := make([]byte, 256) + // encode x + copy(out[16:16+48], outRaw[48:96]) + copy(out[80:80+48], outRaw[:48]) + // encode y + copy(out[144:144+48], outRaw[144:]) + copy(out[208:208+48], outRaw[96:144]) + return out +} + +// New creates a new G2 Point which is equal to zero in other words point at infinity. +func (g *G2) New() *PointG2 { + return new(PointG2).Zero() +} + +// Zero returns a new G2 Point which is equal to point at infinity. +func (g *G2) Zero() *PointG2 { + return new(PointG2).Zero() +} + +// One returns a new G2 Point which is equal to generator point. +func (g *G2) One() *PointG2 { + p := &PointG2{} + return p.Set(&g2One) +} + +// IsZero returns true if given point is equal to zero. +func (g *G2) IsZero(p *PointG2) bool { + return p[2].isZero() +} + +// Equal checks if given two G2 point is equal in their affine form. +func (g *G2) Equal(p1, p2 *PointG2) bool { + if g.IsZero(p1) { + return g.IsZero(p2) + } + if g.IsZero(p2) { + return g.IsZero(p1) + } + t := g.t + g.f.square(t[0], &p1[2]) + g.f.square(t[1], &p2[2]) + g.f.mul(t[2], t[0], &p2[0]) + g.f.mul(t[3], t[1], &p1[0]) + g.f.mul(t[0], t[0], &p1[2]) + g.f.mul(t[1], t[1], &p2[2]) + g.f.mul(t[1], t[1], &p1[1]) + g.f.mul(t[0], t[0], &p2[1]) + return t[0].equal(t[1]) && t[2].equal(t[3]) +} + +// InCorrectSubgroup checks whether given point is in correct subgroup. +func (g *G2) InCorrectSubgroup(p *PointG2) bool { + tmp := &PointG2{} + g.MulScalar(tmp, p, q) + return g.IsZero(tmp) +} + +// IsOnCurve checks a G2 point is on curve. +func (g *G2) IsOnCurve(p *PointG2) bool { + if g.IsZero(p) { + return true + } + t := g.t + g.f.square(t[0], &p[1]) + g.f.square(t[1], &p[0]) + g.f.mul(t[1], t[1], &p[0]) + g.f.square(t[2], &p[2]) + g.f.square(t[3], t[2]) + g.f.mul(t[2], t[2], t[3]) + g.f.mul(t[2], b2, t[2]) + g.f.add(t[1], t[1], t[2]) + return t[0].equal(t[1]) +} + +// IsAffine checks a G2 point whether it is in affine form. +func (g *G2) IsAffine(p *PointG2) bool { + return p[2].isOne() +} + +// Affine calculates affine form of given G2 point. +func (g *G2) Affine(p *PointG2) *PointG2 { + if g.IsZero(p) { + return p + } + if !g.IsAffine(p) { + t := g.t + g.f.inverse(t[0], &p[2]) + g.f.square(t[1], t[0]) + g.f.mul(&p[0], &p[0], t[1]) + g.f.mul(t[0], t[0], t[1]) + g.f.mul(&p[1], &p[1], t[0]) + p[2].one() + } + return p +} + +// Add adds two G2 points p1, p2 and assigns the result to point at first argument. +func (g *G2) Add(r, p1, p2 *PointG2) *PointG2 { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + if g.IsZero(p1) { + return r.Set(p2) + } + if g.IsZero(p2) { + return r.Set(p1) + } + t := g.t + g.f.square(t[7], &p1[2]) + g.f.mul(t[1], &p2[0], t[7]) + g.f.mul(t[2], &p1[2], t[7]) + g.f.mul(t[0], &p2[1], t[2]) + g.f.square(t[8], &p2[2]) + g.f.mul(t[3], &p1[0], t[8]) + g.f.mul(t[4], &p2[2], t[8]) + g.f.mul(t[2], &p1[1], t[4]) + if t[1].equal(t[3]) { + if t[0].equal(t[2]) { + return g.Double(r, p1) + } + return r.Zero() + } + g.f.sub(t[1], t[1], t[3]) + g.f.double(t[4], t[1]) + g.f.square(t[4], t[4]) + g.f.mul(t[5], t[1], t[4]) + g.f.sub(t[0], t[0], t[2]) + g.f.double(t[0], t[0]) + g.f.square(t[6], t[0]) + g.f.sub(t[6], t[6], t[5]) + g.f.mul(t[3], t[3], t[4]) + g.f.double(t[4], t[3]) + g.f.sub(&r[0], t[6], t[4]) + g.f.sub(t[4], t[3], &r[0]) + g.f.mul(t[6], t[2], t[5]) + g.f.double(t[6], t[6]) + g.f.mul(t[0], t[0], t[4]) + g.f.sub(&r[1], t[0], t[6]) + g.f.add(t[0], &p1[2], &p2[2]) + g.f.square(t[0], t[0]) + g.f.sub(t[0], t[0], t[7]) + g.f.sub(t[0], t[0], t[8]) + g.f.mul(&r[2], t[0], t[1]) + return r +} + +// Double doubles a G2 point p and assigns the result to the point at first argument. +func (g *G2) Double(r, p *PointG2) *PointG2 { + // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + if g.IsZero(p) { + return r.Set(p) + } + t := g.t + g.f.square(t[0], &p[0]) + g.f.square(t[1], &p[1]) + g.f.square(t[2], t[1]) + g.f.add(t[1], &p[0], t[1]) + g.f.square(t[1], t[1]) + g.f.sub(t[1], t[1], t[0]) + g.f.sub(t[1], t[1], t[2]) + g.f.double(t[1], t[1]) + g.f.double(t[3], t[0]) + g.f.add(t[0], t[3], t[0]) + g.f.square(t[4], t[0]) + g.f.double(t[3], t[1]) + g.f.sub(&r[0], t[4], t[3]) + g.f.sub(t[1], t[1], &r[0]) + g.f.double(t[2], t[2]) + g.f.double(t[2], t[2]) + g.f.double(t[2], t[2]) + g.f.mul(t[0], t[0], t[1]) + g.f.sub(t[1], t[0], t[2]) + g.f.mul(t[0], &p[1], &p[2]) + r[1].set(t[1]) + g.f.double(&r[2], t[0]) + return r +} + +// Neg negates a G2 point p and assigns the result to the point at first argument. +func (g *G2) Neg(r, p *PointG2) *PointG2 { + r[0].set(&p[0]) + g.f.neg(&r[1], &p[1]) + r[2].set(&p[2]) + return r +} + +// Sub subtracts two G2 points p1, p2 and assigns the result to point at first argument. +func (g *G2) Sub(c, a, b *PointG2) *PointG2 { + d := &PointG2{} + g.Neg(d, b) + g.Add(c, a, d) + return c +} + +// MulScalar multiplies a point by given scalar value in big.Int and assigns the result to point at first argument. +func (g *G2) MulScalar(c, p *PointG2, e *big.Int) *PointG2 { + q, n := &PointG2{}, &PointG2{} + n.Set(p) + l := e.BitLen() + for i := 0; i < l; i++ { + if e.Bit(i) == 1 { + g.Add(q, q, n) + } + g.Double(n, n) + } + return c.Set(q) +} + +// ClearCofactor maps given a G2 point to correct subgroup +func (g *G2) ClearCofactor(p *PointG2) { + g.MulScalar(p, p, cofactorEFFG2) +} + +// MultiExp calculates multi exponentiation. Given pairs of G2 point and scalar values +// (P_0, e_0), (P_1, e_1), ... (P_n, e_n) calculates r = e_0 * P_0 + e_1 * P_1 + ... + e_n * P_n +// Length of points and scalars are expected to be equal, otherwise an error is returned. +// Result is assigned to point at first argument. +func (g *G2) MultiExp(r *PointG2, points []*PointG2, powers []*big.Int) (*PointG2, error) { + if len(points) != len(powers) { + return nil, errors.New("point and scalar vectors should be in same length") + } + var c uint32 = 3 + if len(powers) >= 32 { + c = uint32(math.Ceil(math.Log10(float64(len(powers))))) + } + bucketSize, numBits := (1<= 0; i-- { + g.Add(sum, sum, bucket[i]) + g.Add(acc, acc, sum) + } + windows[j] = g.New() + windows[j].Set(acc) + j++ + cur += c + } + acc.Zero() + for i := len(windows) - 1; i >= 0; i-- { + for j := uint32(0); j < c; j++ { + g.Double(acc, acc) + } + g.Add(acc, acc, windows[i]) + } + return r.Set(acc), nil +} + +// MapToCurve given a byte slice returns a valid G2 point. +// This mapping function implements the Simplified Shallue-van de Woestijne-Ulas method. +// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-6.6.2 +// Input byte slice should be a valid field element, otherwise an error is returned. +func (g *G2) MapToCurve(in []byte) (*PointG2, error) { + fp2 := g.f + u, err := fp2.fromBytes(in) + if err != nil { + return nil, err + } + x, y := swuMapG2(fp2, u) + isogenyMapG2(fp2, x, y) + z := new(fe2).one() + q := &PointG2{*x, *y, *z} + g.ClearCofactor(q) + return g.Affine(q), nil +} diff --git a/crypto/bls12381/g2_test.go b/crypto/bls12381/g2_test.go new file mode 100644 index 0000000000..4d1f3a19ac --- /dev/null +++ b/crypto/bls12381/g2_test.go @@ -0,0 +1,287 @@ +package bls12381 + +import ( + "bytes" + "crypto/rand" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func (g *G2) one() *PointG2 { + one, _ := g.fromBytesUnchecked( + common.FromHex("" + + "13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e" + + "024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8" + + "0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be" + + "0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", + ), + ) + return one +} + +func (g *G2) rand() *PointG2 { + k, err := rand.Int(rand.Reader, q) + if err != nil { + panic(err) + } + return g.MulScalar(&PointG2{}, g.one(), k) +} + +func TestG2Serialization(t *testing.T) { + g2 := NewG2() + for i := 0; i < fuz; i++ { + a := g2.rand() + buf := g2.ToBytes(a) + b, err := g2.FromBytes(buf) + if err != nil { + t.Fatal(err) + } + if !g2.Equal(a, b) { + t.Fatal("bad serialization from/to") + } + } + for i := 0; i < fuz; i++ { + a := g2.rand() + encoded := g2.EncodePoint(a) + b, err := g2.DecodePoint(encoded) + if err != nil { + t.Fatal(err) + } + if !g2.Equal(a, b) { + t.Fatal("bad serialization encode/decode") + } + } +} + +func TestG2IsOnCurve(t *testing.T) { + g := NewG2() + zero := g.Zero() + if !g.IsOnCurve(zero) { + t.Fatal("zero must be on curve") + } + one := new(fe2).one() + p := &PointG2{*one, *one, *one} + if g.IsOnCurve(p) { + t.Fatal("(1, 1) is not on curve") + } +} + +func TestG2AdditiveProperties(t *testing.T) { + g := NewG2() + t0, t1 := g.New(), g.New() + zero := g.Zero() + for i := 0; i < fuz; i++ { + a, b := g.rand(), g.rand() + _, _, _ = b, t1, zero + g.Add(t0, a, zero) + if !g.Equal(t0, a) { + t.Fatal("a + 0 == a") + } + g.Add(t0, zero, zero) + if !g.Equal(t0, zero) { + t.Fatal("0 + 0 == 0") + } + g.Sub(t0, a, zero) + if !g.Equal(t0, a) { + t.Fatal("a - 0 == a") + } + g.Sub(t0, zero, zero) + if !g.Equal(t0, zero) { + t.Fatal("0 - 0 == 0") + } + g.Neg(t0, zero) + if !g.Equal(t0, zero) { + t.Fatal("- 0 == 0") + } + g.Sub(t0, zero, a) + g.Neg(t0, t0) + if !g.Equal(t0, a) { + t.Fatal(" - (0 - a) == a") + } + g.Double(t0, zero) + if !g.Equal(t0, zero) { + t.Fatal("2 * 0 == 0") + } + g.Double(t0, a) + g.Sub(t0, t0, a) + if !g.Equal(t0, a) || !g.IsOnCurve(t0) { + t.Fatal(" (2 * a) - a == a") + } + g.Add(t0, a, b) + g.Add(t1, b, a) + if !g.Equal(t0, t1) { + t.Fatal("a + b == b + a") + } + g.Sub(t0, a, b) + g.Sub(t1, b, a) + g.Neg(t1, t1) + if !g.Equal(t0, t1) { + t.Fatal("a - b == - ( b - a )") + } + c := g.rand() + g.Add(t0, a, b) + g.Add(t0, t0, c) + g.Add(t1, a, c) + g.Add(t1, t1, b) + if !g.Equal(t0, t1) { + t.Fatal("(a + b) + c == (a + c ) + b") + } + g.Sub(t0, a, b) + g.Sub(t0, t0, c) + g.Sub(t1, a, c) + g.Sub(t1, t1, b) + if !g.Equal(t0, t1) { + t.Fatal("(a - b) - c == (a - c) -b") + } + } +} + +func TestG2MultiplicativeProperties(t *testing.T) { + g := NewG2() + t0, t1 := g.New(), g.New() + zero := g.Zero() + for i := 0; i < fuz; i++ { + a := g.rand() + s1, s2, s3 := randScalar(q), randScalar(q), randScalar(q) + sone := big.NewInt(1) + g.MulScalar(t0, zero, s1) + if !g.Equal(t0, zero) { + t.Fatal(" 0 ^ s == 0") + } + g.MulScalar(t0, a, sone) + if !g.Equal(t0, a) { + t.Fatal(" a ^ 1 == a") + } + g.MulScalar(t0, zero, s1) + if !g.Equal(t0, zero) { + t.Fatal(" 0 ^ s == a") + } + g.MulScalar(t0, a, s1) + g.MulScalar(t0, t0, s2) + s3.Mul(s1, s2) + g.MulScalar(t1, a, s3) + if !g.Equal(t0, t1) { + t.Errorf(" (a ^ s1) ^ s2 == a ^ (s1 * s2)") + } + g.MulScalar(t0, a, s1) + g.MulScalar(t1, a, s2) + g.Add(t0, t0, t1) + s3.Add(s1, s2) + g.MulScalar(t1, a, s3) + if !g.Equal(t0, t1) { + t.Errorf(" (a ^ s1) + (a ^ s2) == a ^ (s1 + s2)") + } + } +} + +func TestG2MultiExpExpected(t *testing.T) { + g := NewG2() + one := g.one() + var scalars [2]*big.Int + var bases [2]*PointG2 + scalars[0] = big.NewInt(2) + scalars[1] = big.NewInt(3) + bases[0], bases[1] = new(PointG2).Set(one), new(PointG2).Set(one) + expected, result := g.New(), g.New() + g.MulScalar(expected, one, big.NewInt(5)) + _, _ = g.MultiExp(result, bases[:], scalars[:]) + if !g.Equal(expected, result) { + t.Fatal("bad multi-exponentiation") + } +} + +func TestG2MultiExpBatch(t *testing.T) { + g := NewG2() + one := g.one() + n := 1000 + bases := make([]*PointG2, n) + scalars := make([]*big.Int, n) + // scalars: [s0,s1 ... s(n-1)] + // bases: [P0,P1,..P(n-1)] = [s(n-1)*G, s(n-2)*G ... s0*G] + for i, j := 0, n-1; i < n; i, j = i+1, j-1 { + scalars[j], _ = rand.Int(rand.Reader, big.NewInt(100000)) + bases[i] = g.New() + g.MulScalar(bases[i], one, scalars[j]) + } + // expected: s(n-1)*P0 + s(n-2)*P1 + s0*P(n-1) + expected, tmp := g.New(), g.New() + for i := 0; i < n; i++ { + g.MulScalar(tmp, bases[i], scalars[i]) + g.Add(expected, expected, tmp) + } + result := g.New() + _, _ = g.MultiExp(result, bases, scalars) + if !g.Equal(expected, result) { + t.Fatal("bad multi-exponentiation") + } +} + +func TestG2MapToCurve(t *testing.T) { + for i, v := range []struct { + u []byte + expected []byte + }{ + { + u: make([]byte, 96), + expected: common.FromHex("0a67d12118b5a35bb02d2e86b3ebfa7e23410db93de39fb06d7025fa95e96ffa428a7a27c3ae4dd4b40bd251ac658892" + "018320896ec9eef9d5e619848dc29ce266f413d02dd31d9b9d44ec0c79cd61f18b075ddba6d7bd20b7ff27a4b324bfce" + "04c69777a43f0bda07679d5805e63f18cf4e0e7c6112ac7f70266d199b4f76ae27c6269a3ceebdae30806e9a76aadf5c" + "0260e03644d1a2c321256b3246bad2b895cad13890cbe6f85df55106a0d334604fb143c7a042d878006271865bc35941"), + }, + { + u: common.FromHex("025fbc07711ba267b7e70c82caa70a16fbb1d470ae24ceef307f5e2000751677820b7013ad4e25492dcf30052d3e5eca" + "0e775d7827adf385b83e20e4445bd3fab21d7b4498426daf3c1d608b9d41e9edb5eda0df022e753b8bb4bc3bb7db4914"), + expected: common.FromHex("0d4333b77becbf9f9dfa3ca928002233d1ecc854b1447e5a71f751c9042d000f42db91c1d6649a5e0ad22bd7bf7398b8" + "027e4bfada0b47f9f07e04aec463c7371e68f2fd0c738cd517932ea3801a35acf09db018deda57387b0f270f7a219e4d" + "0cc76dc777ea0d447e02a41004f37a0a7b1fafb6746884e8d9fc276716ccf47e4e0899548a2ec71c2bdf1a2a50e876db" + "053674cba9ef516ddc218fedb37324e6c47de27f88ab7ef123b006127d738293c0277187f7e2f80a299a24d84ed03da7"), + }, + { + u: common.FromHex("1870a7dbfd2a1deb74015a3546b20f598041bf5d5202997956a94a368d30d3f70f18cdaa1d33ce970a4e16af961cbdcb" + "045ab31ce4b5a8ba7c4b2851b64f063a66cd1223d3c85005b78e1beee65e33c90ceef0244e45fc45a5e1d6eab6644fdb"), + expected: common.FromHex("18f0f87b40af67c056915dbaf48534c592524e82c1c2b50c3734d02c0172c80df780a60b5683759298a3303c5d942778" + "09349f1cb5b2e55489dcd45a38545343451cc30a1681c57acd4fb0a6db125f8352c09f4a67eb7d1d8242cb7d3405f97b" + "10a2ba341bc689ab947b7941ce6ef39be17acaab067bd32bd652b471ab0792c53a2bd03bdac47f96aaafe96e441f63c0" + "02f2d9deb2c7742512f5b8230bf0fd83ea42279d7d39779543c1a43b61c885982b611f6a7a24b514995e8a098496b811"), + }, + { + u: common.FromHex("088fe329b054db8a6474f21a7fbfdf17b4c18044db299d9007af582c3d5f17d00e56d99921d4b5640fce44b05219b5de" + "0b6e6135a4cd31ba980ddbd115ac48abef7ec60e226f264d7befe002c165f3a496f36f76dd524efd75d17422558d10b4"), + expected: common.FromHex("19808ec5930a53c7cf5912ccce1cc33f1b3dcff24a53ce1cc4cba41fd6996dbed4843ccdd2eaf6a0cd801e562718d163" + "149fe43777d34f0d25430dea463889bd9393bdfb4932946db23671727081c629ebb98a89604f3433fba1c67d356a4af7" + "04783e391c30c83f805ca271e353582fdf19d159f6a4c39b73acbb637a9b8ac820cfbe2738d683368a7c07ad020e3e33" + "04c0d6793a766233b2982087b5f4a254f261003ccb3262ea7c50903eecef3e871d1502c293f9e063d7d293f6384f4551"), + }, + { + u: common.FromHex("03df16a66a05e4c1188c234788f43896e0565bfb64ac49b9639e6b284cc47dad73c47bb4ea7e677db8d496beb907fbb6" + "0f45b50647d67485295aa9eb2d91a877b44813677c67c8d35b2173ff3ba95f7bd0806f9ca8a1436b8b9d14ee81da4d7e"), + expected: common.FromHex("0b8e0094c886487870372eb6264613a6a087c7eb9804fab789be4e47a57b29eb19b1983a51165a1b5eb025865e9fc63a" + "0804152cbf8474669ad7d1796ab92d7ca21f32d8bed70898a748ed4e4e0ec557069003732fc86866d938538a2ae95552" + "14c80f068ece15a3936bb00c3c883966f75b4e8d9ddde809c11f781ab92d23a2d1d103ad48f6f3bb158bf3e3a4063449" + "09e5c8242dd7281ad32c03fe4af3f19167770016255fb25ad9b67ec51d62fade31a1af101e8f6172ec2ee8857662be3a"), + }, + } { + g := NewG2() + p0, err := g.MapToCurve(v.u) + if err != nil { + t.Fatal("map to curve fails", i, err) + } + if !bytes.Equal(g.ToBytes(p0), v.expected) { + t.Fatal("map to curve fails", i) + } + } +} + +func BenchmarkG2Add(t *testing.B) { + g2 := NewG2() + a, b, c := g2.rand(), g2.rand(), PointG2{} + t.ResetTimer() + for i := 0; i < t.N; i++ { + g2.Add(&c, a, b) + } +} + +func BenchmarkG2Mul(t *testing.B) { + worstCaseScalar, _ := new(big.Int).SetString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) + g2 := NewG2() + a, e, c := g2.rand(), worstCaseScalar, PointG2{} + t.ResetTimer() + for i := 0; i < t.N; i++ { + g2.MulScalar(&c, a, e) + } +} + +func BenchmarkG2SWUMap(t *testing.B) { + a := make([]byte, 96) + g2 := NewG2() + t.ResetTimer() + for i := 0; i < t.N; i++ { + _, err := g2.MapToCurve(a) + if err != nil { + t.Fatal(err) + } + } +} diff --git a/crypto/bls12381/gt.go b/crypto/bls12381/gt.go new file mode 100644 index 0000000000..2ac265e956 --- /dev/null +++ b/crypto/bls12381/gt.go @@ -0,0 +1,121 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math/big" +) + +// E is type for target group element +type E = fe12 + +// GT is type for target multiplicative group GT. +type GT struct { + fp12 *fp12 +} + +func (e *E) Set(e2 *E) *E { + return e.set(e2) +} + +// One sets a new target group element to one +func (e *E) One() *E { + e = new(fe12).one() + return e +} + +// IsOne returns true if given element equals to one +func (e *E) IsOne() bool { + return e.isOne() +} + +// Equal returns true if given two element is equal, otherwise returns false +func (g *E) Equal(g2 *E) bool { + return g.equal(g2) +} + +// NewGT constructs new target group instance. +func NewGT() *GT { + fp12 := newFp12(nil) + return >{fp12} +} + +// Q returns group order in big.Int. +func (g *GT) Q() *big.Int { + return new(big.Int).Set(q) +} + +// FromBytes expects 576 byte input and returns target group element +// FromBytes returns error if given element is not on correct subgroup. +func (g *GT) FromBytes(in []byte) (*E, error) { + e, err := g.fp12.fromBytes(in) + if err != nil { + return nil, err + } + if !g.IsValid(e) { + return e, errors.New("invalid element") + } + return e, nil +} + +// ToBytes serializes target group element. +func (g *GT) ToBytes(e *E) []byte { + return g.fp12.toBytes(e) +} + +// IsValid checks whether given target group element is in correct subgroup. +func (g *GT) IsValid(e *E) bool { + r := g.New() + g.fp12.exp(r, e, q) + return r.isOne() +} + +// New initializes a new target group element which is equal to one +func (g *GT) New() *E { + return new(E).One() +} + +// Add adds two field element `a` and `b` and assigns the result to the element in first argument. +func (g *GT) Add(c, a, b *E) { + g.fp12.add(c, a, b) +} + +// Sub subtracts two field element `a` and `b`, and assigns the result to the element in first argument. +func (g *GT) Sub(c, a, b *E) { + g.fp12.sub(c, a, b) +} + +// Mul multiplies two field element `a` and `b` and assigns the result to the element in first argument. +func (g *GT) Mul(c, a, b *E) { + g.fp12.mul(c, a, b) +} + +// Square squares an element `a` and assigns the result to the element in first argument. +func (g *GT) Square(c, a *E) { + g.fp12.cyclotomicSquare(c, a) +} + +// Exp exponents an element `a` by a scalar `s` and assigns the result to the element in first argument. +func (g *GT) Exp(c, a *E, s *big.Int) { + g.fp12.cyclotomicExp(c, a, s) +} + +// Inverse inverses an element `a` and assigns the result to the element in first argument. +func (g *GT) Inverse(c, a *E) { + g.fp12.inverse(c, a) +} diff --git a/crypto/bls12381/isogeny.go b/crypto/bls12381/isogeny.go new file mode 100644 index 0000000000..a63f585dd0 --- /dev/null +++ b/crypto/bls12381/isogeny.go @@ -0,0 +1,227 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +// isogenyMapG1 applies 11-isogeny map for BLS12-381 G1 defined at draft-irtf-cfrg-hash-to-curve-06. +func isogenyMapG1(x, y *fe) { + // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#appendix-C.2 + params := isogenyConstantsG1 + degree := 15 + xNum, xDen, yNum, yDen := new(fe), new(fe), new(fe), new(fe) + xNum.set(params[0][degree]) + xDen.set(params[1][degree]) + yNum.set(params[2][degree]) + yDen.set(params[3][degree]) + for i := degree - 1; i >= 0; i-- { + mul(xNum, xNum, x) + mul(xDen, xDen, x) + mul(yNum, yNum, x) + mul(yDen, yDen, x) + add(xNum, xNum, params[0][i]) + add(xDen, xDen, params[1][i]) + add(yNum, yNum, params[2][i]) + add(yDen, yDen, params[3][i]) + } + inverse(xDen, xDen) + inverse(yDen, yDen) + mul(xNum, xNum, xDen) + mul(yNum, yNum, yDen) + mul(yNum, yNum, y) + x.set(xNum) + y.set(yNum) +} + +// isogenyMapG2 applies 11-isogeny map for BLS12-381 G1 defined at draft-irtf-cfrg-hash-to-curve-06. +func isogenyMapG2(e *fp2, x, y *fe2) { + if e == nil { + e = newFp2() + } + // https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#appendix-C.2 + params := isogenyConstantsG2 + degree := 3 + xNum := new(fe2).set(params[0][degree]) + xDen := new(fe2).set(params[1][degree]) + yNum := new(fe2).set(params[2][degree]) + yDen := new(fe2).set(params[3][degree]) + for i := degree - 1; i >= 0; i-- { + e.mul(xNum, xNum, x) + e.mul(xDen, xDen, x) + e.mul(yNum, yNum, x) + e.mul(yDen, yDen, x) + e.add(xNum, xNum, params[0][i]) + e.add(xDen, xDen, params[1][i]) + e.add(yNum, yNum, params[2][i]) + e.add(yDen, yDen, params[3][i]) + } + e.inverse(xDen, xDen) + e.inverse(yDen, yDen) + e.mul(xNum, xNum, xDen) + e.mul(yNum, yNum, yDen) + e.mul(yNum, yNum, y) + x.set(xNum) + y.set(yNum) +} + +var isogenyConstantsG1 = [4][16]*fe{ + { + {0x4d18b6f3af00131c, 0x19fa219793fee28c, 0x3f2885f1467f19ae, 0x23dcea34f2ffb304, 0xd15b58d2ffc00054, 0x0913be200a20bef4}, + {0x898985385cdbbd8b, 0x3c79e43cc7d966aa, 0x1597e193f4cd233a, 0x8637ef1e4d6623ad, 0x11b22deed20d827b, 0x07097bc5998784ad}, + {0xa542583a480b664b, 0xfc7169c026e568c6, 0x5ba2ef314ed8b5a6, 0x5b5491c05102f0e7, 0xdf6e99707d2a0079, 0x0784151ed7605524}, + {0x494e212870f72741, 0xab9be52fbda43021, 0x26f5577994e34c3d, 0x049dfee82aefbd60, 0x65dadd7828505289, 0x0e93d431ea011aeb}, + {0x90ee774bd6a74d45, 0x7ada1c8a41bfb185, 0x0f1a8953b325f464, 0x104c24211be4805c, 0x169139d319ea7a8f, 0x09f20ead8e532bf6}, + {0x6ddd93e2f43626b7, 0xa5482c9aa1ccd7bd, 0x143245631883f4bd, 0x2e0a94ccf77ec0db, 0xb0282d480e56489f, 0x18f4bfcbb4368929}, + {0x23c5f0c953402dfd, 0x7a43ff6958ce4fe9, 0x2c390d3d2da5df63, 0xd0df5c98e1f9d70f, 0xffd89869a572b297, 0x1277ffc72f25e8fe}, + {0x79f4f0490f06a8a6, 0x85f894a88030fd81, 0x12da3054b18b6410, 0xe2a57f6505880d65, 0xbba074f260e400f1, 0x08b76279f621d028}, + {0xe67245ba78d5b00b, 0x8456ba9a1f186475, 0x7888bff6e6b33bb4, 0xe21585b9a30f86cb, 0x05a69cdcef55feee, 0x09e699dd9adfa5ac}, + {0x0de5c357bff57107, 0x0a0db4ae6b1a10b2, 0xe256bb67b3b3cd8d, 0x8ad456574e9db24f, 0x0443915f50fd4179, 0x098c4bf7de8b6375}, + {0xe6b0617e7dd929c7, 0xfe6e37d442537375, 0x1dafdeda137a489e, 0xe4efd1ad3f767ceb, 0x4a51d8667f0fe1cf, 0x054fdf4bbf1d821c}, + {0x72db2a50658d767b, 0x8abf91faa257b3d5, 0xe969d6833764ab47, 0x464170142a1009eb, 0xb14f01aadb30be2f, 0x18ae6a856f40715d}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + }, + { + {0xb962a077fdb0f945, 0xa6a9740fefda13a0, 0xc14d568c3ed6c544, 0xb43fc37b908b133e, 0x9c0b3ac929599016, 0x0165aa6c93ad115f}, + {0x23279a3ba506c1d9, 0x92cfca0a9465176a, 0x3b294ab13755f0ff, 0x116dda1c5070ae93, 0xed4530924cec2045, 0x083383d6ed81f1ce}, + {0x9885c2a6449fecfc, 0x4a2b54ccd37733f0, 0x17da9ffd8738c142, 0xa0fba72732b3fafd, 0xff364f36e54b6812, 0x0f29c13c660523e2}, + {0xe349cc118278f041, 0xd487228f2f3204fb, 0xc9d325849ade5150, 0x43a92bd69c15c2df, 0x1c2c7844bc417be4, 0x12025184f407440c}, + {0x587f65ae6acb057b, 0x1444ef325140201f, 0xfbf995e71270da49, 0xccda066072436a42, 0x7408904f0f186bb2, 0x13b93c63edf6c015}, + {0xfb918622cd141920, 0x4a4c64423ecaddb4, 0x0beb232927f7fb26, 0x30f94df6f83a3dc2, 0xaeedd424d780f388, 0x06cc402dd594bbeb}, + {0xd41f761151b23f8f, 0x32a92465435719b3, 0x64f436e888c62cb9, 0xdf70a9a1f757c6e4, 0x6933a38d5b594c81, 0x0c6f7f7237b46606}, + {0x693c08747876c8f7, 0x22c9850bf9cf80f0, 0x8e9071dab950c124, 0x89bc62d61c7baf23, 0xbc6be2d8dad57c23, 0x17916987aa14a122}, + {0x1be3ff439c1316fd, 0x9965243a7571dfa7, 0xc7f7f62962f5cd81, 0x32c6aa9af394361c, 0xbbc2ee18e1c227f4, 0x0c102cbac531bb34}, + {0x997614c97bacbf07, 0x61f86372b99192c0, 0x5b8c95fc14353fc3, 0xca2b066c2a87492f, 0x16178f5bbf698711, 0x12a6dcd7f0f4e0e8}, + {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + }, + { + {0x2b567ff3e2837267, 0x1d4d9e57b958a767, 0xce028fea04bd7373, 0xcc31a30a0b6cd3df, 0x7d7b18a682692693, 0x0d300744d42a0310}, + {0x99c2555fa542493f, 0xfe7f53cc4874f878, 0x5df0608b8f97608a, 0x14e03832052b49c8, 0x706326a6957dd5a4, 0x0a8dadd9c2414555}, + {0x13d942922a5cf63a, 0x357e33e36e261e7d, 0xcf05a27c8456088d, 0x0000bd1de7ba50f0, 0x83d0c7532f8c1fde, 0x13f70bf38bbf2905}, + {0x5c57fd95bfafbdbb, 0x28a359a65e541707, 0x3983ceb4f6360b6d, 0xafe19ff6f97e6d53, 0xb3468f4550192bf7, 0x0bb6cde49d8ba257}, + {0x590b62c7ff8a513f, 0x314b4ce372cacefd, 0x6bef32ce94b8a800, 0x6ddf84a095713d5f, 0x64eace4cb0982191, 0x0386213c651b888d}, + {0xa5310a31111bbcdd, 0xa14ac0f5da148982, 0xf9ad9cc95423d2e9, 0xaa6ec095283ee4a7, 0xcf5b1f022e1c9107, 0x01fddf5aed881793}, + {0x65a572b0d7a7d950, 0xe25c2d8183473a19, 0xc2fcebe7cb877dbd, 0x05b2d36c769a89b0, 0xba12961be86e9efb, 0x07eb1b29c1dfde1f}, + {0x93e09572f7c4cd24, 0x364e929076795091, 0x8569467e68af51b5, 0xa47da89439f5340f, 0xf4fa918082e44d64, 0x0ad52ba3e6695a79}, + {0x911429844e0d5f54, 0xd03f51a3516bb233, 0x3d587e5640536e66, 0xfa86d2a3a9a73482, 0xa90ed5adf1ed5537, 0x149c9c326a5e7393}, + {0x462bbeb03c12921a, 0xdc9af5fa0a274a17, 0x9a558ebde836ebed, 0x649ef8f11a4fae46, 0x8100e1652b3cdc62, 0x1862bd62c291dacb}, + {0x05c9b8ca89f12c26, 0x0194160fa9b9ac4f, 0x6a643d5a6879fa2c, 0x14665bdd8846e19d, 0xbb1d0d53af3ff6bf, 0x12c7e1c3b28962e5}, + {0xb55ebf900b8a3e17, 0xfedc77ec1a9201c4, 0x1f07db10ea1a4df4, 0x0dfbd15dc41a594d, 0x389547f2334a5391, 0x02419f98165871a4}, + {0xb416af000745fc20, 0x8e563e9d1ea6d0f5, 0x7c763e17763a0652, 0x01458ef0159ebbef, 0x8346fe421f96bb13, 0x0d2d7b829ce324d2}, + {0x93096bb538d64615, 0x6f2a2619951d823a, 0x8f66b3ea59514fa4, 0xf563e63704f7092f, 0x724b136c4cf2d9fa, 0x046959cfcfd0bf49}, + {0xea748d4b6e405346, 0x91e9079c2c02d58f, 0x41064965946d9b59, 0xa06731f1d2bbe1ee, 0x07f897e267a33f1b, 0x1017290919210e5f}, + {0x872aa6c17d985097, 0xeecc53161264562a, 0x07afe37afff55002, 0x54759078e5be6838, 0xc4b92d15db8acca8, 0x106d87d1b51d13b9}, + }, + { + {0xeb6c359d47e52b1c, 0x18ef5f8a10634d60, 0xddfa71a0889d5b7e, 0x723e71dcc5fc1323, 0x52f45700b70d5c69, 0x0a8b981ee47691f1}, + {0x616a3c4f5535b9fb, 0x6f5f037395dbd911, 0xf25f4cc5e35c65da, 0x3e50dffea3c62658, 0x6a33dca523560776, 0x0fadeff77b6bfe3e}, + {0x2be9b66df470059c, 0x24a2c159a3d36742, 0x115dbe7ad10c2a37, 0xb6634a652ee5884d, 0x04fe8bb2b8d81af4, 0x01c2a7a256fe9c41}, + {0xf27bf8ef3b75a386, 0x898b367476c9073f, 0x24482e6b8c2f4e5f, 0xc8e0bbd6fe110806, 0x59b0c17f7631448a, 0x11037cd58b3dbfbd}, + {0x31c7912ea267eec6, 0x1dbf6f1c5fcdb700, 0xd30d4fe3ba86fdb1, 0x3cae528fbee9a2a4, 0xb1cce69b6aa9ad9a, 0x044393bb632d94fb}, + {0xc66ef6efeeb5c7e8, 0x9824c289dd72bb55, 0x71b1a4d2f119981d, 0x104fc1aafb0919cc, 0x0e49df01d942a628, 0x096c3a09773272d4}, + {0x9abc11eb5fadeff4, 0x32dca50a885728f0, 0xfb1fa3721569734c, 0xc4b76271ea6506b3, 0xd466a75599ce728e, 0x0c81d4645f4cb6ed}, + {0x4199f10e5b8be45b, 0xda64e495b1e87930, 0xcb353efe9b33e4ff, 0x9e9efb24aa6424c6, 0xf08d33680a237465, 0x0d3378023e4c7406}, + {0x7eb4ae92ec74d3a5, 0xc341b4aa9fac3497, 0x5be603899e907687, 0x03bfd9cca75cbdeb, 0x564c2935a96bfa93, 0x0ef3c33371e2fdb5}, + {0x7ee91fd449f6ac2e, 0xe5d5bd5cb9357a30, 0x773a8ca5196b1380, 0xd0fda172174ed023, 0x6cb95e0fa776aead, 0x0d22d5a40cec7cff}, + {0xf727e09285fd8519, 0xdc9d55a83017897b, 0x7549d8bd057894ae, 0x178419613d90d8f8, 0xfce95ebdeb5b490a, 0x0467ffaef23fc49e}, + {0xc1769e6a7c385f1b, 0x79bc930deac01c03, 0x5461c75a23ede3b5, 0x6e20829e5c230c45, 0x828e0f1e772a53cd, 0x116aefa749127bff}, + {0x101c10bf2744c10a, 0xbbf18d053a6a3154, 0xa0ecf39ef026f602, 0xfc009d4996dc5153, 0xb9000209d5bd08d3, 0x189e5fe4470cd73c}, + {0x7ebd546ca1575ed2, 0xe47d5a981d081b55, 0x57b2b625b6d4ca21, 0xb0a1ba04228520cc, 0x98738983c2107ff3, 0x13dddbc4799d81d6}, + {0x09319f2e39834935, 0x039e952cbdb05c21, 0x55ba77a9a2f76493, 0xfd04e3dfc6086467, 0xfb95832e7d78742e, 0x0ef9c24eccaf5e0e}, + {0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + }, +} + +var isogenyConstantsG2 = [4][4]*fe2{ + { + { + fe{0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41}, + fe{0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41}, + }, + { + fe{0, 0, 0, 0, 0, 0}, + fe{0x5fe55555554c71d0, 0x873fffdd236aaaa3, 0x6a6b4619b26ef918, 0x21c2888408874945, 0x2836cda7028cabc5, 0x0ac73310a7fd5abd}, + }, + { + fe{0x0a0c5555555971c3, 0xdb0c00101f9eaaae, 0xb1fb2f941d797997, 0xd3960742ef416e1c, 0xb70040e2c20556f4, 0x149d7861e581393b}, + fe{0xaff2aaaaaaa638e8, 0x439fffee91b55551, 0xb535a30cd9377c8c, 0x90e144420443a4a2, 0x941b66d3814655e2, 0x0563998853fead5e}, + }, + { + fe{0x40aac71c71c725ed, 0x190955557a84e38e, 0xd817050a8f41abc3, 0xd86485d4c87f6fb1, 0x696eb479f885d059, 0x198e1a74328002d2}, + fe{0, 0, 0, 0, 0, 0}, + }, + }, + { + { + fe{0, 0, 0, 0, 0, 0}, + fe{0x1f3affffff13ab97, 0xf25bfc611da3ff3e, 0xca3757cb3819b208, 0x3e6427366f8cec18, 0x03977bc86095b089, 0x04f69db13f39a952}, + }, + { + fe{0x447600000027552e, 0xdcb8009a43480020, 0x6f7ee9ce4a6e8b59, 0xb10330b7c0a95bc6, 0x6140b1fcfb1e54b7, 0x0381be097f0bb4e1}, + fe{0x7588ffffffd8557d, 0x41f3ff646e0bffdf, 0xf7b1e8d2ac426aca, 0xb3741acd32dbb6f8, 0xe9daf5b9482d581f, 0x167f53e0ba7431b8}, + }, + { + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0, 0, 0, 0, 0, 0}, + }, + { + fe{0, 0, 0, 0, 0, 0}, + fe{0, 0, 0, 0, 0, 0}, + }, + }, + { + { + fe{0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3}, + fe{0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3}, + }, + { + fe{0, 0, 0, 0, 0, 0}, + fe{0xbf0a71c71c91b406, 0x4d6d55d28b7638fd, 0x9d82f98e5f205aee, 0xa27aa27b1d1a18d5, 0x02c3b2b2d2938e86, 0x0c7d13420b09807f}, + }, + { + fe{0xd7f9555555531c74, 0x21cffff748daaaa8, 0x5a9ad1866c9bbe46, 0x4870a2210221d251, 0x4a0db369c0a32af1, 0x02b1ccc429ff56af}, + fe{0xe205aaaaaaac8e37, 0xfcdc000768795556, 0x0c96011a8a1537dd, 0x1c06a963f163406e, 0x010df44c82a881e6, 0x174f45260f808feb}, + }, + { + fe{0xa470bda12f67f35c, 0xc0fe38e23327b425, 0xc9d3d0f2c6f0678d, 0x1c55c9935b5a982e, 0x27f6c0e2f0746764, 0x117c5e6e28aa9054}, + fe{0, 0, 0, 0, 0, 0}, + }, + }, + { + { + fe{0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151}, + fe{0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151}, + }, + { + fe{0, 0, 0, 0, 0, 0}, + fe{0x5db0fffffd3b02c5, 0xd713f52358ebfdba, 0x5ea60761a84d161a, 0xbb2c75a34ea6c44a, 0x0ac6735921c1119b, 0x0ee3d913bdacfbf6}, + }, + { + fe{0x66b10000003affc5, 0xcb1400e764ec0030, 0xa73e5eb56fa5d106, 0x8984c913a0fe09a9, 0x11e10afb78ad7f13, 0x05429d0e3e918f52}, + fe{0x534dffffffc4aae6, 0x5397ff174c67ffcf, 0xbff273eb870b251d, 0xdaf2827152870915, 0x393a9cbaca9e2dc3, 0x14be74dbfaee5748}, + }, + { + fe{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}, + fe{0, 0, 0, 0, 0, 0}, + }, + }, +} diff --git a/crypto/bls12381/pairing.go b/crypto/bls12381/pairing.go new file mode 100644 index 0000000000..d292d7c3a5 --- /dev/null +++ b/crypto/bls12381/pairing.go @@ -0,0 +1,282 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +type pair struct { + g1 *PointG1 + g2 *PointG2 +} + +func newPair(g1 *PointG1, g2 *PointG2) pair { + return pair{g1, g2} +} + +// Engine is BLS12-381 elliptic curve pairing engine +type Engine struct { + G1 *G1 + G2 *G2 + fp12 *fp12 + fp2 *fp2 + pairingEngineTemp + pairs []pair +} + +// NewPairingEngine creates new pairing engine instance. +func NewPairingEngine() *Engine { + fp2 := newFp2() + fp6 := newFp6(fp2) + fp12 := newFp12(fp6) + g1 := NewG1() + g2 := newG2(fp2) + return &Engine{ + fp2: fp2, + fp12: fp12, + G1: g1, + G2: g2, + pairingEngineTemp: newEngineTemp(), + } +} + +type pairingEngineTemp struct { + t2 [10]*fe2 + t12 [9]fe12 +} + +func newEngineTemp() pairingEngineTemp { + t2 := [10]*fe2{} + for i := 0; i < 10; i++ { + t2[i] = &fe2{} + } + t12 := [9]fe12{} + return pairingEngineTemp{t2, t12} +} + +// AddPair adds a g1, g2 point pair to pairing engine +func (e *Engine) AddPair(g1 *PointG1, g2 *PointG2) *Engine { + p := newPair(g1, g2) + if !e.isZero(p) { + e.affine(p) + e.pairs = append(e.pairs, p) + } + return e +} + +// AddPairInv adds a G1, G2 point pair to pairing engine. G1 point is negated. +func (e *Engine) AddPairInv(g1 *PointG1, g2 *PointG2) *Engine { + e.G1.Neg(g1, g1) + e.AddPair(g1, g2) + return e +} + +// Reset deletes added pairs. +func (e *Engine) Reset() *Engine { + e.pairs = []pair{} + return e +} + +func (e *Engine) isZero(p pair) bool { + return e.G1.IsZero(p.g1) || e.G2.IsZero(p.g2) +} + +func (e *Engine) affine(p pair) { + e.G1.Affine(p.g1) + e.G2.Affine(p.g2) +} + +func (e *Engine) doublingStep(coeff *[3]fe2, r *PointG2) { + // Adaptation of Formula 3 in https://eprint.iacr.org/2010/526.pdf + fp2 := e.fp2 + t := e.t2 + fp2.mul(t[0], &r[0], &r[1]) + fp2.mulByFq(t[0], t[0], twoInv) + fp2.square(t[1], &r[1]) + fp2.square(t[2], &r[2]) + fp2.double(t[7], t[2]) + fp2.add(t[7], t[7], t[2]) + fp2.mulByB(t[3], t[7]) + fp2.double(t[4], t[3]) + fp2.add(t[4], t[4], t[3]) + fp2.add(t[5], t[1], t[4]) + fp2.mulByFq(t[5], t[5], twoInv) + fp2.add(t[6], &r[1], &r[2]) + fp2.square(t[6], t[6]) + fp2.add(t[7], t[2], t[1]) + fp2.sub(t[6], t[6], t[7]) + fp2.sub(&coeff[0], t[3], t[1]) + fp2.square(t[7], &r[0]) + fp2.sub(t[4], t[1], t[4]) + fp2.mul(&r[0], t[4], t[0]) + fp2.square(t[2], t[3]) + fp2.double(t[3], t[2]) + fp2.add(t[3], t[3], t[2]) + fp2.square(t[5], t[5]) + fp2.sub(&r[1], t[5], t[3]) + fp2.mul(&r[2], t[1], t[6]) + fp2.double(t[0], t[7]) + fp2.add(&coeff[1], t[0], t[7]) + fp2.neg(&coeff[2], t[6]) +} + +func (e *Engine) additionStep(coeff *[3]fe2, r, q *PointG2) { + // Algorithm 12 in https://eprint.iacr.org/2010/526.pdf + fp2 := e.fp2 + t := e.t2 + fp2.mul(t[0], &q[1], &r[2]) + fp2.neg(t[0], t[0]) + fp2.add(t[0], t[0], &r[1]) + fp2.mul(t[1], &q[0], &r[2]) + fp2.neg(t[1], t[1]) + fp2.add(t[1], t[1], &r[0]) + fp2.square(t[2], t[0]) + fp2.square(t[3], t[1]) + fp2.mul(t[4], t[1], t[3]) + fp2.mul(t[2], &r[2], t[2]) + fp2.mul(t[3], &r[0], t[3]) + fp2.double(t[5], t[3]) + fp2.sub(t[5], t[4], t[5]) + fp2.add(t[5], t[5], t[2]) + fp2.mul(&r[0], t[1], t[5]) + fp2.sub(t[2], t[3], t[5]) + fp2.mul(t[2], t[2], t[0]) + fp2.mul(t[3], &r[1], t[4]) + fp2.sub(&r[1], t[2], t[3]) + fp2.mul(&r[2], &r[2], t[4]) + fp2.mul(t[2], t[1], &q[1]) + fp2.mul(t[3], t[0], &q[0]) + fp2.sub(&coeff[0], t[3], t[2]) + fp2.neg(&coeff[1], t[0]) + coeff[2].set(t[1]) +} + +func (e *Engine) preCompute(ellCoeffs *[68][3]fe2, twistPoint *PointG2) { + // Algorithm 5 in https://eprint.iacr.org/2019/077.pdf + if e.G2.IsZero(twistPoint) { + return + } + r := new(PointG2).Set(twistPoint) + j := 0 + for i := x.BitLen() - 2; i >= 0; i-- { + e.doublingStep(&ellCoeffs[j], r) + if x.Bit(i) != 0 { + j++ + ellCoeffs[j] = fe6{} + e.additionStep(&ellCoeffs[j], r, twistPoint) + } + j++ + } +} + +func (e *Engine) millerLoop(f *fe12) { + pairs := e.pairs + ellCoeffs := make([][68][3]fe2, len(pairs)) + for i := 0; i < len(pairs); i++ { + e.preCompute(&ellCoeffs[i], pairs[i].g2) + } + fp12, fp2 := e.fp12, e.fp2 + t := e.t2 + f.one() + j := 0 + for i := 62; /* x.BitLen() - 2 */ i >= 0; i-- { + if i != 62 { + fp12.square(f, f) + } + for i := 0; i <= len(pairs)-1; i++ { + fp2.mulByFq(t[0], &ellCoeffs[i][j][2], &pairs[i].g1[1]) + fp2.mulByFq(t[1], &ellCoeffs[i][j][1], &pairs[i].g1[0]) + fp12.mulBy014Assign(f, &ellCoeffs[i][j][0], t[1], t[0]) + } + if x.Bit(i) != 0 { + j++ + for i := 0; i <= len(pairs)-1; i++ { + fp2.mulByFq(t[0], &ellCoeffs[i][j][2], &pairs[i].g1[1]) + fp2.mulByFq(t[1], &ellCoeffs[i][j][1], &pairs[i].g1[0]) + fp12.mulBy014Assign(f, &ellCoeffs[i][j][0], t[1], t[0]) + } + } + j++ + } + fp12.conjugate(f, f) +} + +func (e *Engine) exp(c, a *fe12) { + fp12 := e.fp12 + fp12.cyclotomicExp(c, a, x) + fp12.conjugate(c, c) +} + +func (e *Engine) finalExp(f *fe12) { + fp12 := e.fp12 + t := e.t12 + // easy part + fp12.frobeniusMap(&t[0], f, 6) + fp12.inverse(&t[1], f) + fp12.mul(&t[2], &t[0], &t[1]) + t[1].set(&t[2]) + fp12.frobeniusMapAssign(&t[2], 2) + fp12.mulAssign(&t[2], &t[1]) + fp12.cyclotomicSquare(&t[1], &t[2]) + fp12.conjugate(&t[1], &t[1]) + // hard part + e.exp(&t[3], &t[2]) + fp12.cyclotomicSquare(&t[4], &t[3]) + fp12.mul(&t[5], &t[1], &t[3]) + e.exp(&t[1], &t[5]) + e.exp(&t[0], &t[1]) + e.exp(&t[6], &t[0]) + fp12.mulAssign(&t[6], &t[4]) + e.exp(&t[4], &t[6]) + fp12.conjugate(&t[5], &t[5]) + fp12.mulAssign(&t[4], &t[5]) + fp12.mulAssign(&t[4], &t[2]) + fp12.conjugate(&t[5], &t[2]) + fp12.mulAssign(&t[1], &t[2]) + fp12.frobeniusMapAssign(&t[1], 3) + fp12.mulAssign(&t[6], &t[5]) + fp12.frobeniusMapAssign(&t[6], 1) + fp12.mulAssign(&t[3], &t[0]) + fp12.frobeniusMapAssign(&t[3], 2) + fp12.mulAssign(&t[3], &t[1]) + fp12.mulAssign(&t[3], &t[6]) + fp12.mul(f, &t[3], &t[4]) +} + +func (e *Engine) calculate() *fe12 { + f := e.fp12.one() + if len(e.pairs) == 0 { + return f + } + e.millerLoop(f) + e.finalExp(f) + return f +} + +// Check computes pairing and checks if result is equal to one +func (e *Engine) Check() bool { + return e.calculate().isOne() +} + +// Result computes pairing and returns target group element as result. +func (e *Engine) Result() *E { + r := e.calculate() + e.Reset() + return r +} + +// GT returns target group instance. +func (e *Engine) GT() *GT { + return NewGT() +} diff --git a/crypto/bls12381/pairing_test.go b/crypto/bls12381/pairing_test.go new file mode 100644 index 0000000000..77676fe9b1 --- /dev/null +++ b/crypto/bls12381/pairing_test.go @@ -0,0 +1,230 @@ +package bls12381 + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestPairingExpected(t *testing.T) { + bls := NewPairingEngine() + G1, G2 := bls.G1, bls.G2 + GT := bls.GT() + expected, err := GT.FromBytes( + common.FromHex("" + + "0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631" + + "04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef" + + "03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a2" + + "11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba57" + + "06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a" + + "19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d" + + "018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b6" + + "01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5" + + "193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f" + + "1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87" + + "089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f" + + "1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6", + ), + ) + if err != nil { + t.Fatal(err) + } + r := bls.AddPair(G1.One(), G2.One()).Result() + if !r.Equal(expected) { + t.Fatal("bad pairing") + } + if !GT.IsValid(r) { + t.Fatal("element is not in correct subgroup") + } +} + +func TestPairingNonDegeneracy(t *testing.T) { + bls := NewPairingEngine() + G1, G2 := bls.G1, bls.G2 + g1Zero, g2Zero, g1One, g2One := G1.Zero(), G2.Zero(), G1.One(), G2.One() + GT := bls.GT() + // e(g1^a, g2^b) != 1 + bls.Reset() + { + bls.AddPair(g1One, g2One) + e := bls.Result() + if e.IsOne() { + t.Fatal("pairing result is not expected to be one") + } + if !GT.IsValid(e) { + t.Fatal("pairing result is not valid") + } + } + // e(g1^a, 0) == 1 + bls.Reset() + { + bls.AddPair(g1One, g2Zero) + e := bls.Result() + if !e.IsOne() { + t.Fatal("pairing result is expected to be one") + } + } + // e(0, g2^b) == 1 + bls.Reset() + { + bls.AddPair(g1Zero, g2One) + e := bls.Result() + if !e.IsOne() { + t.Fatal("pairing result is expected to be one") + } + } + // + bls.Reset() + { + bls.AddPair(g1Zero, g2One) + bls.AddPair(g1One, g2Zero) + bls.AddPair(g1Zero, g2Zero) + e := bls.Result() + if !e.IsOne() { + t.Fatal("pairing result is expected to be one") + } + } + // + bls.Reset() + { + expected, err := GT.FromBytes( + common.FromHex("" + + "0f41e58663bf08cf068672cbd01a7ec73baca4d72ca93544deff686bfd6df543d48eaa24afe47e1efde449383b676631" + + "04c581234d086a9902249b64728ffd21a189e87935a954051c7cdba7b3872629a4fafc05066245cb9108f0242d0fe3ef" + + "03350f55a7aefcd3c31b4fcb6ce5771cc6a0e9786ab5973320c806ad360829107ba810c5a09ffdd9be2291a0c25a99a2" + + "11b8b424cd48bf38fcef68083b0b0ec5c81a93b330ee1a677d0d15ff7b984e8978ef48881e32fac91b93b47333e2ba57" + + "06fba23eb7c5af0d9f80940ca771b6ffd5857baaf222eb95a7d2809d61bfe02e1bfd1b68ff02f0b8102ae1c2d5d5ab1a" + + "19f26337d205fb469cd6bd15c3d5a04dc88784fbb3d0b2dbdea54d43b2b73f2cbb12d58386a8703e0f948226e47ee89d" + + "018107154f25a764bd3c79937a45b84546da634b8f6be14a8061e55cceba478b23f7dacaa35c8ca78beae9624045b4b6" + + "01b2f522473d171391125ba84dc4007cfbf2f8da752f7c74185203fcca589ac719c34dffbbaad8431dad1c1fb597aaa5" + + "193502b86edb8857c273fa075a50512937e0794e1e65a7617c90d8bd66065b1fffe51d7a579973b1315021ec3c19934f" + + "1368bb445c7c2d209703f239689ce34c0378a68e72a6b3b216da0e22a5031b54ddff57309396b38c881c4c849ec23e87" + + "089a1c5b46e5110b86750ec6a532348868a84045483c92b7af5af689452eafabf1a8943e50439f1d59882a98eaa0170f" + + "1250ebd871fc0a92a7b2d83168d0d727272d441befa15c503dd8e90ce98db3e7b6d194f60839c508a84305aaca1789b6", + ), + ) + if err != nil { + t.Fatal(err) + } + bls.AddPair(g1Zero, g2One) + bls.AddPair(g1One, g2Zero) + bls.AddPair(g1Zero, g2Zero) + bls.AddPair(g1One, g2One) + e := bls.Result() + if !e.Equal(expected) { + t.Fatal("bad pairing") + } + } +} + +func TestPairingBilinearity(t *testing.T) { + bls := NewPairingEngine() + g1, g2 := bls.G1, bls.G2 + gt := bls.GT() + // e(a*G1, b*G2) = e(G1, G2)^c + { + a, b := big.NewInt(17), big.NewInt(117) + c := new(big.Int).Mul(a, b) + G1, G2 := g1.One(), g2.One() + e0 := bls.AddPair(G1, G2).Result() + P1, P2 := g1.New(), g2.New() + g1.MulScalar(P1, G1, a) + g2.MulScalar(P2, G2, b) + e1 := bls.AddPair(P1, P2).Result() + gt.Exp(e0, e0, c) + if !e0.Equal(e1) { + t.Fatal("bad pairing, 1") + } + } + // e(a * G1, b * G2) = e((a + b) * G1, G2) + { + // scalars + a, b := big.NewInt(17), big.NewInt(117) + c := new(big.Int).Mul(a, b) + // LHS + G1, G2 := g1.One(), g2.One() + g1.MulScalar(G1, G1, c) + bls.AddPair(G1, G2) + // RHS + P1, P2 := g1.One(), g2.One() + g1.MulScalar(P1, P1, a) + g2.MulScalar(P2, P2, b) + bls.AddPairInv(P1, P2) + // should be one + if !bls.Check() { + t.Fatal("bad pairing, 2") + } + } + // e(a * G1, b * G2) = e((a + b) * G1, G2) + { + // scalars + a, b := big.NewInt(17), big.NewInt(117) + c := new(big.Int).Mul(a, b) + // LHS + G1, G2 := g1.One(), g2.One() + g2.MulScalar(G2, G2, c) + bls.AddPair(G1, G2) + // RHS + H1, H2 := g1.One(), g2.One() + g1.MulScalar(H1, H1, a) + g2.MulScalar(H2, H2, b) + bls.AddPairInv(H1, H2) + // should be one + if !bls.Check() { + t.Fatal("bad pairing, 3") + } + } +} + +func TestPairingMulti(t *testing.T) { + // e(G1, G2) ^ t == e(a01 * G1, a02 * G2) * e(a11 * G1, a12 * G2) * ... * e(an1 * G1, an2 * G2) + // where t = sum(ai1 * ai2) + bls := NewPairingEngine() + g1, g2 := bls.G1, bls.G2 + numOfPair := 100 + targetExp := new(big.Int) + // RHS + for i := 0; i < numOfPair; i++ { + // (ai1 * G1, ai2 * G2) + a1, a2 := randScalar(q), randScalar(q) + P1, P2 := g1.One(), g2.One() + g1.MulScalar(P1, P1, a1) + g2.MulScalar(P2, P2, a2) + bls.AddPair(P1, P2) + // accumulate targetExp + // t += (ai1 * ai2) + a1.Mul(a1, a2) + targetExp.Add(targetExp, a1) + } + // LHS + // e(t * G1, G2) + T1, T2 := g1.One(), g2.One() + g1.MulScalar(T1, T1, targetExp) + bls.AddPairInv(T1, T2) + if !bls.Check() { + t.Fatal("fail multi pairing") + } +} + +func TestPairingEmpty(t *testing.T) { + bls := NewPairingEngine() + if !bls.Check() { + t.Fatal("empty check should be accepted") + } + if !bls.Result().IsOne() { + t.Fatal("empty pairing result should be one") + } +} + +func BenchmarkPairing(t *testing.B) { + bls := NewPairingEngine() + g1, g2, gt := bls.G1, bls.G2, bls.GT() + bls.AddPair(g1.One(), g2.One()) + e := gt.New() + t.ResetTimer() + for i := 0; i < t.N; i++ { + e = bls.calculate() + } + _ = e +} diff --git a/crypto/bls12381/swu.go b/crypto/bls12381/swu.go new file mode 100644 index 0000000000..e78753b240 --- /dev/null +++ b/crypto/bls12381/swu.go @@ -0,0 +1,158 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +// swuMapG1 is implementation of Simplified Shallue-van de Woestijne-Ulas Method +// follows the implementation at draft-irtf-cfrg-hash-to-curve-06. +func swuMapG1(u *fe) (*fe, *fe) { + var params = swuParamsForG1 + var tv [4]*fe + for i := 0; i < 4; i++ { + tv[i] = new(fe) + } + square(tv[0], u) + mul(tv[0], tv[0], params.z) + square(tv[1], tv[0]) + x1 := new(fe) + add(x1, tv[0], tv[1]) + inverse(x1, x1) + e1 := x1.isZero() + one := new(fe).one() + add(x1, x1, one) + if e1 { + x1.set(params.zInv) + } + mul(x1, x1, params.minusBOverA) + gx1 := new(fe) + square(gx1, x1) + add(gx1, gx1, params.a) + mul(gx1, gx1, x1) + add(gx1, gx1, params.b) + x2 := new(fe) + mul(x2, tv[0], x1) + mul(tv[1], tv[0], tv[1]) + gx2 := new(fe) + mul(gx2, gx1, tv[1]) + e2 := !isQuadraticNonResidue(gx1) + x, y2 := new(fe), new(fe) + if e2 { + x.set(x1) + y2.set(gx1) + } else { + x.set(x2) + y2.set(gx2) + } + y := new(fe) + sqrt(y, y2) + if y.sign() != u.sign() { + neg(y, y) + } + return x, y +} + +// swuMapG2 is implementation of Simplified Shallue-van de Woestijne-Ulas Method +// defined at draft-irtf-cfrg-hash-to-curve-06. +func swuMapG2(e *fp2, u *fe2) (*fe2, *fe2) { + if e == nil { + e = newFp2() + } + params := swuParamsForG2 + var tv [4]*fe2 + for i := 0; i < 4; i++ { + tv[i] = e.new() + } + e.square(tv[0], u) + e.mul(tv[0], tv[0], params.z) + e.square(tv[1], tv[0]) + x1 := e.new() + e.add(x1, tv[0], tv[1]) + e.inverse(x1, x1) + e1 := x1.isZero() + e.add(x1, x1, e.one()) + if e1 { + x1.set(params.zInv) + } + e.mul(x1, x1, params.minusBOverA) + gx1 := e.new() + e.square(gx1, x1) + e.add(gx1, gx1, params.a) + e.mul(gx1, gx1, x1) + e.add(gx1, gx1, params.b) + x2 := e.new() + e.mul(x2, tv[0], x1) + e.mul(tv[1], tv[0], tv[1]) + gx2 := e.new() + e.mul(gx2, gx1, tv[1]) + e2 := !e.isQuadraticNonResidue(gx1) + x, y2 := e.new(), e.new() + if e2 { + x.set(x1) + y2.set(gx1) + } else { + x.set(x2) + y2.set(gx2) + } + y := e.new() + e.sqrt(y, y2) + if y.sign() != u.sign() { + e.neg(y, y) + } + return x, y +} + +var swuParamsForG1 = struct { + z *fe + zInv *fe + a *fe + b *fe + minusBOverA *fe +}{ + a: &fe{0x2f65aa0e9af5aa51, 0x86464c2d1e8416c3, 0xb85ce591b7bd31e2, 0x27e11c91b5f24e7c, 0x28376eda6bfc1835, 0x155455c3e5071d85}, + b: &fe{0xfb996971fe22a1e0, 0x9aa93eb35b742d6f, 0x8c476013de99c5c4, 0x873e27c3a221e571, 0xca72b5e45a52d888, 0x06824061418a386b}, + z: &fe{0x886c00000023ffdc, 0x0f70008d3090001d, 0x77672417ed5828c3, 0x9dac23e943dc1740, 0x50553f1b9c131521, 0x078c712fbe0ab6e8}, + zInv: &fe{0x0e8a2e8ba2e83e10, 0x5b28ba2ca4d745d1, 0x678cd5473847377a, 0x4c506dd8a8076116, 0x9bcb227d79284139, 0x0e8d3154b0ba099a}, + minusBOverA: &fe{0x052583c93555a7fe, 0x3b40d72430f93c82, 0x1b75faa0105ec983, 0x2527e7dc63851767, 0x99fffd1f34fc181d, 0x097cab54770ca0d3}, +} + +var swuParamsForG2 = struct { + z *fe2 + zInv *fe2 + a *fe2 + b *fe2 + minusBOverA *fe2 +}{ + a: &fe2{ + fe{0, 0, 0, 0, 0, 0}, + fe{0xe53a000003135242, 0x01080c0fdef80285, 0xe7889edbe340f6bd, 0x0b51375126310601, 0x02d6985717c744ab, 0x1220b4e979ea5467}, + }, + b: &fe2{ + fe{0x22ea00000cf89db2, 0x6ec832df71380aa4, 0x6e1b94403db5a66e, 0x75bf3c53a79473ba, 0x3dd3a569412c0a34, 0x125cdb5e74dc4fd1}, + fe{0x22ea00000cf89db2, 0x6ec832df71380aa4, 0x6e1b94403db5a66e, 0x75bf3c53a79473ba, 0x3dd3a569412c0a34, 0x125cdb5e74dc4fd1}, + }, + z: &fe2{ + fe{0x87ebfffffff9555c, 0x656fffe5da8ffffa, 0x0fd0749345d33ad2, 0xd951e663066576f4, 0xde291a3d41e980d3, 0x0815664c7dfe040d}, + fe{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206}, + }, + zInv: &fe2{ + fe{0xacd0000000011110, 0x9dd9999dc88ccccd, 0xb5ca2ac9b76352bf, 0xf1b574bcf4bc90ce, 0x42dab41f28a77081, 0x132fc6ac14cd1e12}, + fe{0xe396ffffffff2223, 0x4fbf332fcd0d9998, 0x0c4bbd3c1aff4cc4, 0x6b9c91267926ca58, 0x29ae4da6aef7f496, 0x10692e942f195791}, + }, + minusBOverA: &fe2{ + fe{0x903c555555474fb3, 0x5f98cc95ce451105, 0x9f8e582eefe0fade, 0xc68946b6aebbd062, 0x467a4ad10ee6de53, 0x0e7146f483e23a05}, + fe{0x29c2aaaaaab85af8, 0xbf133368e30eeefa, 0xc7a27a7206cffb45, 0x9dee04ce44c9425c, 0x04a15ce53464ce83, 0x0b8fcaf5b59dac95}, + }, +} diff --git a/crypto/bls12381/utils.go b/crypto/bls12381/utils.go new file mode 100644 index 0000000000..de8bf495fe --- /dev/null +++ b/crypto/bls12381/utils.go @@ -0,0 +1,45 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package bls12381 + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +func bigFromHex(hex string) *big.Int { + return new(big.Int).SetBytes(common.FromHex(hex)) +} + +// decodeFieldElement expects 64 byte input with zero top 16 bytes, +// returns lower 48 bytes. +func decodeFieldElement(in []byte) ([]byte, error) { + if len(in) != 64 { + return nil, errors.New("invalid field element length") + } + // check top bytes + for i := 0; i < 16; i++ { + if in[i] != byte(0x00) { + return nil, errors.New("invalid field element top bytes") + } + } + out := make([]byte, 48) + copy(out[:], in[16:]) + return out, nil +}