diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 521e64e..9bd5430 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,21 +11,23 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout code + uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.23.0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + cache-dependency-path: "**/*.sum" - - name: Checks - run: make checks + - name: Checks + run: make checks - - name: Unit Tests - run: make unit-tests + - name: Unit Tests + run: make unit-tests - - name: Benchmarks - run: make perf + - name: Benchmarks + run: make perf - - name: Race test - run: make unit-tests-race + - name: Race test + run: make unit-tests-race diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..740d698 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,31 @@ +name: golangci-lint + +on: + push: + branches: + - master + - main + pull_request: + # The branches below must be a subset of the branches above + branches: [ main ] + +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v8 + with: + version: v2.10.1 + args: --timeout=30m \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..fd5402c --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,246 @@ +version: "2" +linters: + enable: + - canonicalheader # Canonicalheader checks whether net/http.Header uses canonical header. [auto-fix] + - dupword # Checks for duplicate words in the source code. [fast, auto-fix] + - errorlint # Find code that can cause problems with the error wrapping scheme introduced in Go 1.13. [auto-fix] + - exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. [auto-fix] + - ginkgolinter # Enforces standards of using ginkgo and gomega. [auto-fix] + - goheader # Check if file header matches to pattern. [fast, auto-fix] + - govet # Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [auto-fix] + - importas # Enforces consistent import aliases. [auto-fix] + - mirror # Reports wrong mirror patterns of bytes/strings usage. [auto-fix] + - misspell # Finds commonly misspelled English words. [fast, auto-fix] + - nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast, auto-fix] + - nolintlint # Reports ill-formed or insufficient nolint directives. [fast, auto-fix] + - sloglint # Ensure consistent code style when using log/slog. [auto-fix] + - staticcheck # It's the set of rules from staticcheck. [auto-fix] + - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast, auto-fix] + - usetesting # Reports uses of functions with replacement inside the testing package. [auto-fix] + - whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast, auto-fix] + - copyloopvar # A linter detects places where loop variables are copied. [fast, auto-fix] +# - err113 # Check errors handling expressions. [auto-fix] +# - fatcontext # Detects nested contexts in loops and function literals. [auto-fix] +# - gocritic # Provides diagnostics that check for bugs, performance and style issues. [auto-fix] +# - iface # Detect the incorrect use of interfaces, helping developers avoid interface pollution. [auto-fix] + - intrange # Intrange is a linter to find places where for loops could make use of an integer range. [auto-fix] + - nlreturn # Checks for a new line before return and branch statements to increase code clarity. [fast, auto-fix] + - perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [auto-fix] +# - protogetter # Reports direct reads from proto message fields when getters should be used. [auto-fix] +# - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [auto-fix] + - tagalign # Check that struct tags are well aligned. [fast, auto-fix] + - testifylint # Checks usage of github.com/stretchr/testify. [auto-fix] +# - wsl_v5 # Add or remove empty lines. [auto-fix] +# - containedctx +# - contextcheck + - depguard +# - dupl +# - errname + - errorlint +# - fatcontext +# - forcetypeassert + - ginkgolinter +# - gocognit +# - goconst +# - gocritic +# - godot + - gosec +# - intrange +# - ireturn +# - lll +# - maintidx + - mirror + - misspell + - nilerr + - nilnesserr + - nolintlint +# - paralleltest +# - prealloc +# - promlinter +# - revive +# - rowserrcheck +# - sqlclosecheck +# - testifylint + - thelper + - unconvert +# - unparam + - usetesting + - wastedassign + - whitespace + - govet +# - staticcheck + - canonicalheader +# - copyloopvar + - dupword +# - err113 + - exptostd + - goheader + - importas +# - intrange + - nakedret + - sloglint + - usestdlibvars + # wrapcheck is not working as expected. It is categorizing internal packages as external. + #- wrapcheck + settings: + depguard: + rules: + main: + deny: + - pkg: github.com/pkg/errors + desc: github.com/pkg/errors is no longer maintained + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. +# Default: false + check-type-assertions: false + errorlint: + # Check whether fmt.Errorf uses the %w verb for formatting errors. + # See the https://github.com/polyfloyd/go-errorlint for caveats. + # Default: true + errorf: true + # Check for plain type assertions and type switches. + # Default: true + asserts: false + # Check for plain error comparisons. + # Default: true + comparison: true + fatcontext: + check-struct-pointers: true + gocognit: + min-complexity: 15 + gosec: + excludes: + - G204 + - G404 + - G306 + govet: + disable: + - fieldalignment + # Enable all analyzers. + # Default: false + enable-all: false + iface: + enable: + - identical # Identifies interfaces in the same package that have identical method sets. + - unused # Identifies interfaces that are not used anywhere in the same package where the interface is defined. + - opaque # Identifies functions that return interfaces, but the actual returned value is always a single concrete implementation. + ireturn: + # By default, it allows using errors, empty interfaces, anonymous interfaces, + # and interfaces provided by the standard library. + allow: + - error + - empty + - anon + - stdlib + - generic + - (or|er)$ + - T + - github.com/prometheus/client_golang/prometheus.Counter + - github.com/prometheus/client_golang/prometheus.Gauge + - github.com/prometheus/client_golang/prometheus.Histogram + lll: + # Max line length, lines longer will be reported. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option. + # Default: 120. + line-length: 240 + maintidx: + under: 20 + nolintlint: + require-specific: true + revive: + enable-all-rules: true + rules: + - name: argument-limit + arguments: + - 5 + - name: line-length-limit + arguments: + - 240 + - name: file-header + disabled: true + - name: package-comments + disabled: true + - name: max-public-structs + disabled: true + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: function-length + disabled: true + - name: function-result-limit + arguments: + - 3 + - name: add-constant + disabled: true + - name: unhandled-error + arguments: + - fmt.Printf + - fmt.Println + - name: confusing-naming + disabled: true + - name: comment-spacings + arguments: + - 'nolint:' + rowserrcheck: + packages: + - github.com/jackc/pgx/v5 + - github.com/jackc/pgx/v4 + wrapcheck: + # An array of strings specifying additional substrings of signatures to ignore. + # Unlike 'ignoreSigs', this option extends the default set (or the set specified in 'ignoreSigs') without replacing it entirely. + # This allows you to add specific signatures to the ignore list + # while retaining the defaults or any items in 'ignoreSigs'. + # Default: [] + extra-ignore-sigs: + - .CustomError( + - .SpecificWrap( + + # An array of strings that specify substrings of signatures to ignore. + # If this set, it will override the default set of ignored signatures. + # See https://github.com/tomarrell/wrapcheck#configuration for more information. + # Default: [".Errorf(", "errors.New(", "errors.Unwrap(", "errors.Join(", ".Wrap(", ".Wrapf(", ".WithMessage(", ".WithMessagef(", ".WithStack("] + ignore-sigs: + - errors.New( + - errors.Newf( + - errors.Unwrap( + - errors.Join( + - .Wrap( + - .Wrapf( + - status.Error( + - .Wait() # The error reported by errorgroup.Wait() (external) actually + # originates from the internal code. + # An array of strings that specify regular expressions of signatures to ignore. + # Default: [] + ignore-sig-regexps: + - \.New.*Error\( + # An array of strings that specify globs of packages to ignore. + # Default: [] + ignore-package-globs: + - github.ibm.com/decentralized-trust-research/scalable-committer/utils/grpcerror + # An array of strings that specify regular expressions of interfaces to ignore. + # Default: [] + # ignoreInterfaceRegexps: + # - ^(?i)c(?-i)ach(ing|e) + exclusions: + # Mode of the generated files analysis. + # + # - `strict`: sources are excluded by strictly following the Go generated file convention. + # Source files that have lines matching only the following regular expression will be excluded: `^// Code generated .* DO NOT EDIT\.$` + # This line must appear before the first non-comment, non-blank text in the file. + # https://go.dev/s/generatedcode + # - `lax`: sources are excluded if they contain lines like `autogenerated file`, `code generated`, `do not edit`, etc. + # - `disable`: disable the generated files exclusion. + # + # Default: lax + generated: lax + # Log a warning if an exclusion rule is unused. + # Default: false + warn-unused: true + # Predefined exclusion rules. + # Default: [] + presets: + - common-false-positives \ No newline at end of file diff --git a/Makefile b/Makefile index e4df705..2a6b380 100644 --- a/Makefile +++ b/Makefile @@ -23,3 +23,26 @@ perf: .PHONY: check-deps check-deps: @go install github.com/google/addlicense@latest + +.PHONY: lint +# run various linters +lint: + @echo "Running Go Linters..." + golangci-lint run --color=always --timeout=4m + +.PHONY: lint-auto-fix +# run linters with auto-fix +lint-auto-fix: + @echo "Running Go Linters with auto-fix..." + golangci-lint run --color=always --timeout=4m --fix + +.PHONY: install-linter-tool +# install golangci-lint +install-linter-tool: + @echo "Installing golangci Linter" + @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(HOME)/go/bin v2.10.1 + +.PHONY: fmt +fmt: ## Run gofmt on the entire project + @echo "Running gofmt..." + @gofmt -l -s -w . diff --git a/README.md b/README.md index cc1774e..d4cd8a2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,328 @@ # mathlib + [![License](https://img.shields.io/badge/license-Apache%202-blue)](LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/IBM/mathlib)](https://goreportcard.com/badge/github.com/IBM/mathlib) [![Go](https://github.com/IBM/mathlib/actions/workflows/go.yml/badge.svg)](https://github.com/IBM/mathlib/actions/workflows/go.yml/badge.svg) +[![GoDoc](https://godoc.org/github.com/IBM/mathlib?status.svg)](https://godoc.org/github.com/IBM/mathlib) + +A high-performance Go library for pairing-based cryptography operations over elliptic curve groups. + +## Overview + +`mathlib` provides a unified interface for performing cryptographic operations on pairing-friendly elliptic curves. It supports multiple curve implementations and backends, making it suitable for various cryptographic protocols including: + +- Zero-knowledge proofs +- Anonymous credentials + +The library abstracts the complexity of pairing operations while providing flexibility to choose different curve types and backend implementations based on performance and security requirements. + +## Features + +- **Multiple Curve Support**: FP256BN, BN254, BLS12-381, BLS12-377, and BBS+ variants +- **Pluggable Backends**: Support for AMCL, Gurvy, and Kilic implementations +- **Type-Safe API**: Strongly-typed group elements (G1, G2, Gt, Zr) +- **Efficient Operations**: Optimized pairing computations and multi-scalar multiplications +- **Serialization**: Support for both compressed and uncompressed point representations +- **Hash-to-Curve**: Secure hashing to curve points with optional domain separation +- **Modular Arithmetic**: Comprehensive scalar field operations + +## Installation + +```bash +go get github.com/IBM/mathlib +``` + +**Requirements:** +- Go 1.25 or higher + +## Quick Start + +```go +package main + +import ( + "fmt" + "github.com/IBM/mathlib" +) + +func main() { + // Select a curve (BLS12-381 in this example) + curve := math.Curves[math.BLS12_381] + + // Generate random scalars + rng, _ := curve.Rand() + a := curve.NewRandomZr(rng) + b := curve.NewRandomZr(rng) + + // Perform scalar multiplication on G1 + P := curve.GenG1.Mul(a) + Q := curve.GenG1.Mul(b) + + // Compute pairing + e1 := curve.Pairing(curve.GenG2, P) + e2 := curve.Pairing(curve.GenG2, Q) + + // Multiply in target group + e1.Mul(e2) + + fmt.Printf("Pairing result: %s\n", e1.String()) +} +``` + +## Supported Curves + +| Curve ID | Description | Backend | Use Case | +|----------|-------------|---------|----------| +| `FP256BN_AMCL` | 256-bit Barreto-Naehrig curve | AMCL | General-purpose pairing operations | +| `FP256BN_AMCL_MIRACL` | 256-bit BN curve (MIRACL variant) | AMCL | Legacy compatibility | +| `BN254` | 254-bit Barreto-Naehrig curve | Gurvy | High-performance applications | +| `BLS12_381` | BLS12-381 curve | Kilic | Modern protocols, BLS signatures | +| `BLS12_381_GURVY` | BLS12-381 curve | Gurvy | Performance-optimized BLS12-381 | +| `BLS12_377_GURVY` | BLS12-377 curve | Gurvy | Recursive proof systems | +| `BLS12_381_BBS` | BLS12-381 for BBS+ signatures | Kilic | Anonymous credentials | +| `BLS12_381_BBS_GURVY` | BLS12-381 for BBS+ signatures | Gurvy | High-performance BBS+ | + +### Choosing a Curve + +- **BLS12-381**: Recommended for new projects, widely standardized, excellent security margins +- **BN254**: Good performance, but security margins are tighter than BLS12-381 +- **BLS12-377**: Specialized for recursive proof composition (e.g., zk-SNARKs) +- **BBS+ variants**: Specifically optimized for BBS+ signature schemes + +## API Overview + +### Core Types + +- **`Curve`**: Main interface for curve operations, provides factory methods for group elements +- **`Zr`**: Elements in the scalar field (integers modulo curve order) +- **`G1`**: Points on the first elliptic curve group +- **`G2`**: Points on the second elliptic curve group (twisted curve) +- **`Gt`**: Elements in the target group (result of pairing operations) + +### Key Operations + +```go +// Curve selection +curve := math.Curves[math.BLS12_381] + +// Scalar operations +a := curve.NewZrFromInt(42) +b := curve.HashToZr([]byte("some data")) +c := a.Plus(b) + +// G1 operations +P := curve.GenG1.Mul(a) +Q := curve.HashToG1([]byte("hash to point")) +P.Add(Q) + +// G2 operations +R := curve.GenG2.Mul(b) + +// Pairing +e := curve.Pairing(R, P) + +// Target group operations +e2 := e.Exp(c) +``` + +## Usage Examples + +### Example 1: Basic Pairing Operation + +```go +curve := math.Curves[math.BLS12_381] + +// Create scalars +a := curve.NewZrFromInt(5) +b := curve.NewZrFromInt(7) + +// Compute [a]G1 and [b]G2 +P := curve.GenG1.Mul(a) +Q := curve.GenG2.Mul(b) + +// Compute pairing e([b]G2, [a]G1) +result := curve.Pairing(Q, P) + +// Verify bilinearity: e(G2, [ab]G1) == e([b]G2, [a]G1) +ab := a.Mul(b) +expected := curve.Pairing(curve.GenG2, curve.GenG1.Mul(ab)) + +if result.Equals(expected) { + fmt.Println("Pairing bilinearity verified!") +} +``` + +### Example 2: Serialization and Deserialization + +```go +curve := math.Curves[math.BLS12_381] + +// Create a point +rng, _ := curve.Rand() +scalar := curve.NewRandomZr(rng) +point := curve.GenG1.Mul(scalar) + +// Serialize (uncompressed) +bytes := point.Bytes() + +// Deserialize +recovered, err := curve.NewG1FromBytes(bytes) +if err != nil { + panic(err) +} + +// Serialize (compressed) +compressed := point.Compressed() +recoveredCompressed, err := curve.NewG1FromCompressed(compressed) +if err != nil { + panic(err) +} + +fmt.Printf("Original and recovered points match: %v\n", + point.Equals(recovered) && point.Equals(recoveredCompressed)) +``` + +### Example 3: Hash-to-Curve with Domain Separation + +```go +curve := math.Curves[math.BLS12_381] + +// Hash to G1 with domain separation +message := []byte("sign this message") +domain := []byte("my-application-v1") + +point := curve.HashToG1WithDomain(message, domain) + +// Use in signature scheme +rng, _ := curve.Rand() +secretKey := curve.NewRandomZr(rng) +signature := point.Mul(secretKey) + +fmt.Printf("Signature: %x\n", signature.Compressed()) +``` + +### Example 4: Multi-Scalar Multiplication + +```go +curve := math.Curves[math.BLS12_381] + +// Create multiple points and scalars +points := []*math.G1{ + curve.GenG1, + curve.HashToG1([]byte("point2")), + curve.HashToG1([]byte("point3")), +} + +scalars := []*math.Zr{ + curve.NewZrFromInt(2), + curve.NewZrFromInt(3), + curve.NewZrFromInt(5), +} + +// Efficient multi-scalar multiplication: [2]P1 + [3]P2 + [5]P3 +result := curve.MultiScalarMul(points, scalars) + +fmt.Printf("Multi-scalar multiplication result: %s\n", result.String()) +``` + +## Architecture + +### Driver Pattern + +`mathlib` uses a driver pattern to support multiple backend implementations: + +``` +┌─────────────────────────────────────┐ +│ mathlib (Public API) │ +│ Curve, G1, G2, Gt, Zr types │ +└─────────────────┬───────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ driver (Interface Layer) │ +│ Curve, G1, G2, Gt, Zr interfaces │ +└─────────────────┬───────────────────┘ + │ + ┌─────────┼─────────┐ + ▼ ▼ ▼ + ┌──────┐ ┌──────┐ ┌──────┐ + │ AMCL │ │Gurvy │ │Kilic │ + └──────┘ └──────┘ └──────┘ +``` + +This design allows: +- **Flexibility**: Easy addition of new curve implementations +- **Performance**: Choose the fastest backend for your use case +- **Compatibility**: Support for different cryptographic libraries + +### Backend Implementations + +- **AMCL (Apache Milagro Crypto Library)**: Mature, well-tested implementation +- **Gurvy**: High-performance Go-native implementation with assembly optimizations +- **Kilic**: Optimized BLS12-381 implementation with focus on BLS signatures + +## Performance Considerations + +- Use compressed point serialization when bandwidth is limited +- Prefer `Pairing2` for double pairings (more efficient than two separate pairings) +- Use `MultiScalarMul` for multiple scalar multiplications (faster than individual operations) +- Consider `Mul2` and `Mul2InPlace` for combined operations on G1 +- BLS12-381 with Gurvy backend offers excellent performance for most applications + +## Testing + +Run the test suite: + +```bash +make unit-tests +``` + +Run benchmarks: + +```bash +make perf +``` + +## Contributing + +Contributions are welcome! Please: + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +### Development Guidelines + +- Follow Go coding conventions and style guidelines +- Add tests for new functionality +- Update documentation for API changes +- Run `make checks` and `make lint` before committing +- Ensure all tests pass and linters are satisfied + +## Security Considerations + +- Always use cryptographically secure random number generators +- Validate all deserialized points (the library does this automatically) +- Use appropriate curve parameters for your security requirements +- Consider timing attack mitigations for sensitive operations +- Keep dependencies up to date + +## License + +This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. + +## References + +- [Pairing-Based Cryptography](https://en.wikipedia.org/wiki/Pairing-based_cryptography) +- [BLS12-381 Specification](https://github.com/zkcrypto/bls12_381) +- [BLS Signatures](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature) +- [Apache Milagro](https://milagro.apache.org/) + +## Acknowledgments -Library to perform operations over elements of pairing-friendly elliptic curve groups +This library builds upon the excellent work of: +- Apache Milagro Crypto Library (AMCL) +- ConsenSys Gurvy library +- Kilic BLS12-381 implementation \ No newline at end of file diff --git a/driver/amcl/custom.go b/driver/amcl/custom.go index 6e627b6..3023c0a 100644 --- a/driver/amcl/custom.go +++ b/driver/amcl/custom.go @@ -27,5 +27,6 @@ func bls_hash_to_point_miracl(M, DST []byte) *FP256BN.ECP { P.Add(P1) P.Cfp() P.Affine() + return P } diff --git a/driver/amcl/fp256bn.go b/driver/amcl/fp256bn.go index 0e970c9..bd8df82 100644 --- a/driver/amcl/fp256bn.go +++ b/driver/amcl/fp256bn.go @@ -25,7 +25,7 @@ type fp256bnGt struct { } func (a *fp256bnGt) Exp(x driver.Zr) driver.Gt { - return &fp256bnGt{*a.FP12.Pow(bigToMiraclBIGCore(&x.(*common.BaseZr).Int))} + return &fp256bnGt{*a.Pow(bigToMiraclBIGCore(&x.(*common.BaseZr).Int))} } func (a *fp256bnGt) Equals(b driver.Gt) bool { @@ -33,7 +33,7 @@ func (a *fp256bnGt) Equals(b driver.Gt) bool { } func (a *fp256bnGt) IsUnity() bool { - return a.FP12.Isunity() + return a.Isunity() } func (a *fp256bnGt) Inverse() { @@ -50,7 +50,8 @@ func (b *fp256bnGt) ToString() string { func (b *fp256bnGt) Bytes() []byte { bytes := make([]byte, 12*int(FP256BN.MODBYTES)) - b.FP12.ToBytes(bytes) + b.ToBytes(bytes) + return bytes } @@ -66,9 +67,10 @@ type Fp256bn struct { func (p *Fp256bn) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { g1 := p.NewG1() - for i := 0; i < len(a); i++ { + for i := range a { g1.Add(a[i].Mul(b[i])) } + return g1 } @@ -171,6 +173,7 @@ func (p *Fp256bn) HashToG1(data []byte) driver.G1 { func (p *Fp256bn) HashToG1WithDomain(data, domain []byte) driver.G1 { mac := hmac.New(sha256.New, domain) mac.Write(data) + return &fp256bnG1{*FP256BN.Bls_hash(string(mac.Sum(nil)))} } @@ -195,6 +198,7 @@ func (e *fp256bnG1) Clone(a driver.G1) { func (e *fp256bnG1) Copy() driver.G1 { c := FP256BN.NewECP() c.Copy(&e.ECP) + return &fp256bnG1{*c} } @@ -220,18 +224,20 @@ func (e *fp256bnG1) Equals(a driver.G1) bool { } func (e *fp256bnG1) IsInfinity() bool { - return e.ECP.Is_infinity() + return e.Is_infinity() } func (e *fp256bnG1) Bytes() []byte { b := make([]byte, 2*int(FP256BN.MODBYTES)+1) - e.ECP.ToBytes(b, false) + e.ToBytes(b, false) + return b } func (e *fp256bnG1) Compressed() []byte { b := make([]byte, int(FP256BN.MODBYTES)+1) - e.ECP.ToBytes(b, true) + e.ToBytes(b, true) + return b } @@ -242,8 +248,9 @@ func (e *fp256bnG1) Sub(a driver.G1) { var g1StrRegexp *regexp.Regexp = regexp.MustCompile(`^\(([0-9a-f]+),([0-9a-f]+)\)$`) func (b *fp256bnG1) String() string { - rawstr := b.ECP.ToString() + rawstr := b.ToString() m := g1StrRegexp.FindAllStringSubmatch(rawstr, -1) + return "(" + strings.TrimLeft(m[0][1], "0") + "," + strings.TrimLeft(m[0][2], "0") + ")" } @@ -269,6 +276,7 @@ func (e *fp256bnG2) Clone(a driver.G2) { func (e *fp256bnG2) Copy() driver.G2 { c := FP256BN.NewECP2() c.Copy(&e.ECP2) + return &fp256bnG2{*c} } @@ -290,16 +298,18 @@ func (e *fp256bnG2) Affine() { func (e *fp256bnG2) Bytes() []byte { b := make([]byte, 4*int(FP256BN.MODBYTES)) - e.ECP2.ToBytes(b) + e.ToBytes(b) + return b } func (e *fp256bnG2) Compressed() []byte { b := make([]byte, 4*int(FP256BN.MODBYTES)) - e.ECP2.ToBytes(b) + e.ToBytes(b) + return b } func (b *fp256bnG2) String() string { - return b.ECP2.ToString() + return b.ToString() } diff --git a/driver/amcl/fp256bn_miracl.go b/driver/amcl/fp256bn_miracl.go index faf009a..2e4a517 100644 --- a/driver/amcl/fp256bn_miracl.go +++ b/driver/amcl/fp256bn_miracl.go @@ -29,7 +29,7 @@ type fp256bnMiraclGt struct { } func (a *fp256bnMiraclGt) Exp(x driver.Zr) driver.Gt { - return &fp256bnMiraclGt{*a.FP12.Pow(bigToMiraclBIG(&x.(*common.BaseZr).Int))} + return &fp256bnMiraclGt{*a.Pow(bigToMiraclBIG(&x.(*common.BaseZr).Int))} } func (a *fp256bnMiraclGt) Equals(b driver.Gt) bool { @@ -37,7 +37,7 @@ func (a *fp256bnMiraclGt) Equals(b driver.Gt) bool { } func (a *fp256bnMiraclGt) IsUnity() bool { - return a.FP12.Isunity() + return a.Isunity() } func (a *fp256bnMiraclGt) Inverse() { @@ -54,7 +54,8 @@ func (b *fp256bnMiraclGt) ToString() string { func (b *fp256bnMiraclGt) Bytes() []byte { bytes := make([]byte, 12*int(FP256BN.MODBYTES)) - b.FP12.ToBytes(bytes) + b.ToBytes(bytes) + return bytes } @@ -70,9 +71,10 @@ type Fp256Miraclbn struct { func (p *Fp256Miraclbn) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { g1 := p.NewG1() - for i := 0; i < len(a); i++ { + for i := range a { g1.Add(a[i].Mul(b[i])) } + return g1 } @@ -197,6 +199,7 @@ func (e *fp256bnMiraclG1) Clone(a driver.G1) { func (e *fp256bnMiraclG1) Copy() driver.G1 { c := FP256BN.NewECP() c.Copy(&e.ECP) + return &fp256bnMiraclG1{*c} } @@ -221,18 +224,20 @@ func (e *fp256bnMiraclG1) Equals(a driver.G1) bool { } func (e *fp256bnMiraclG1) IsInfinity() bool { - return e.ECP.Is_infinity() + return e.Is_infinity() } func (e *fp256bnMiraclG1) Bytes() []byte { b := make([]byte, 2*int(FP256BN.MODBYTES)+1) - e.ECP.ToBytes(b, false) + e.ToBytes(b, false) + return b } func (e *fp256bnMiraclG1) Compressed() []byte { b := make([]byte, int(FP256BN.MODBYTES)+1) - e.ECP.ToBytes(b, true) + e.ToBytes(b, true) + return b } @@ -241,8 +246,9 @@ func (e *fp256bnMiraclG1) Sub(a driver.G1) { } func (b *fp256bnMiraclG1) String() string { - rawstr := b.ECP.ToString() + rawstr := b.ToString() m := g1StrRegexp.FindAllStringSubmatch(rawstr, -1) + return "(" + strings.TrimLeft(m[0][1], "0") + "," + strings.TrimLeft(m[0][2], "0") + ")" } @@ -267,6 +273,7 @@ func (e *fp256bnMiraclG2) Clone(a driver.G2) { func (e *fp256bnMiraclG2) Copy() driver.G2 { c := FP256BN.NewECP2() c.Copy(e.ECP2) + return &fp256bnMiraclG2{c} } @@ -288,16 +295,18 @@ func (e *fp256bnMiraclG2) Affine() { func (e *fp256bnMiraclG2) Bytes() []byte { b := make([]byte, 4*int(FP256BN.MODBYTES)+1) - e.ECP2.ToBytes(b, false) + e.ToBytes(b, false) + return b } func (e *fp256bnMiraclG2) Compressed() []byte { b := make([]byte, 2*int(FP256BN.MODBYTES)+1) - e.ECP2.ToBytes(b, true) + e.ToBytes(b, true) + return b } func (b *fp256bnMiraclG2) String() string { - return b.ECP2.ToString() + return b.ToString() } diff --git a/driver/common/big.go b/driver/common/big.go index 15fd21e..f7ddebb 100644 --- a/driver/common/big.go +++ b/driver/common/big.go @@ -34,6 +34,7 @@ func BigToBytes(bi *big.Int) []byte { twoscomp = twoscomp.Sub(twoscomp, pos) twoscomp = twoscomp.Add(twoscomp, big.NewInt(1)) b = twoscomp.Bytes() + return append(onebytes[:ScalarByteSize-len(b)], b...) } @@ -42,15 +43,31 @@ type BaseZr struct { Modulus big.Int } +func (b *BaseZr) IsZero() bool { + return b.BitLen() == 0 +} + +func (b *BaseZr) IsOne() bool { + bits := b.Bits() + + return len(bits) == 1 && bits[0] == 1 && b.Sign() > 0 +} + +func (b *BaseZr) BigInt() *big.Int { + return &b.Int +} + func (b *BaseZr) Plus(a driver.Zr) driver.Zr { rv := &BaseZr{Modulus: b.Modulus} rv.Add(&b.Int, &a.(*BaseZr).Int) + return rv } func (b *BaseZr) Minus(a driver.Zr) driver.Zr { rv := &BaseZr{Modulus: b.Modulus} rv.Sub(&b.Int, &a.(*BaseZr).Int) + return rv } @@ -58,12 +75,14 @@ func (b *BaseZr) Mul(a driver.Zr) driver.Zr { rv := &BaseZr{Modulus: b.Modulus} rv.Int.Mul(&b.Int, &a.(*BaseZr).Int) rv.Int.Mod(&rv.Int, &b.Modulus) + return rv } func (b *BaseZr) PowMod(x driver.Zr) driver.Zr { rv := &BaseZr{Modulus: b.Modulus} rv.Exp(&b.Int, &x.(*BaseZr).Int, &b.Modulus) + return rv } @@ -72,17 +91,17 @@ func (b *BaseZr) Mod(a driver.Zr) { } func (b *BaseZr) InvModP(p driver.Zr) { - b.Int.ModInverse(&b.Int, &p.(*BaseZr).Int) + b.ModInverse(&b.Int, &p.(*BaseZr).Int) } func (b *BaseZr) InvModOrder() { - b.Int.ModInverse(&b.Int, &b.Modulus) + b.ModInverse(&b.Int, &b.Modulus) } func (b *BaseZr) Bytes() []byte { target := b.Int - if b.Int.Sign() < 0 || b.Int.Cmp(&b.Modulus) > 0 { + if b.Sign() < 0 || b.Cmp(&b.Modulus) > 0 { target = *new(big.Int).Set(&b.Int) target = *target.Mod(&target, &b.Modulus) if target.Sign() < 0 { @@ -94,22 +113,23 @@ func (b *BaseZr) Bytes() []byte { } func (b *BaseZr) Equals(p driver.Zr) bool { - return b.Int.Cmp(&p.(*BaseZr).Int) == 0 + return b.Cmp(&p.(*BaseZr).Int) == 0 } func (b *BaseZr) Copy() driver.Zr { rv := &BaseZr{Modulus: b.Modulus} rv.Set(&b.Int) + return rv } func (b *BaseZr) Clone(a driver.Zr) { raw := a.(*BaseZr).Int.Bytes() - b.Int.SetBytes(raw) + b.SetBytes(raw) } func (b *BaseZr) String() string { - return b.Int.Text(16) + return b.Text(16) } func (b *BaseZr) Neg() { diff --git a/driver/common/curve.go b/driver/common/curve.go index 07d7d9d..90d6617 100644 --- a/driver/common/curve.go +++ b/driver/common/curve.go @@ -21,7 +21,7 @@ type CurveBase struct { func (c *CurveBase) ModNeg(a1, m driver.Zr) driver.Zr { res := &BaseZr{Modulus: c.Modulus} - res.Int.Sub(&m.(*BaseZr).Int, &a1.(*BaseZr).Int) + res.Sub(&m.(*BaseZr).Int, &a1.(*BaseZr).Int) res.Int.Mod(&res.Int, &m.(*BaseZr).Int) return res @@ -37,7 +37,7 @@ func (c *CurveBase) ModMul(a1, b1, m driver.Zr) driver.Zr { func (c *CurveBase) ModSub(a1, b1, m driver.Zr) driver.Zr { res := &BaseZr{Modulus: c.Modulus} - res.Int.Sub(&a1.(*BaseZr).Int, &b1.(*BaseZr).Int) + res.Sub(&a1.(*BaseZr).Int, &b1.(*BaseZr).Int) res.Int.Mod(&res.Int, &m.(*BaseZr).Int) return res @@ -45,7 +45,7 @@ func (c *CurveBase) ModSub(a1, b1, m driver.Zr) driver.Zr { func (c *CurveBase) ModAdd(a1, b1, m driver.Zr) driver.Zr { res := &BaseZr{Modulus: c.Modulus} - res.Int.Add(&a1.(*BaseZr).Int, &b1.(*BaseZr).Int) + res.Add(&a1.(*BaseZr).Int, &b1.(*BaseZr).Int) res.Int.Mod(&res.Int, &m.(*BaseZr).Int) return res @@ -57,7 +57,8 @@ func (c *CurveBase) GroupOrder() driver.Zr { func (c *CurveBase) NewZrFromBytes(b []byte) driver.Zr { res := &BaseZr{Modulus: c.Modulus} - res.Int.SetBytes(b) + res.SetBytes(b) + return res } @@ -69,6 +70,10 @@ func (c *CurveBase) NewZrFromUint64(i uint64) driver.Zr { return &BaseZr{Int: *new(big.Int).SetUint64(i), Modulus: c.Modulus} } +func (c *CurveBase) NewZrFromBigInt(i *big.Int) driver.Zr { + return &BaseZr{Int: *i, Modulus: c.Modulus} +} + func (c *CurveBase) NewRandomZr(rng io.Reader) driver.Zr { bi, err := rand.Int(rng, &c.Modulus) if err != nil { @@ -82,6 +87,7 @@ func (c *CurveBase) HashToZr(data []byte) driver.Zr { digest := sha256.Sum256(data) digestBig := new(big.Int).SetBytes(digest[:]) digestBig.Mod(digestBig, &c.Modulus) + return &BaseZr{Int: *digestBig, Modulus: c.Modulus} } @@ -91,12 +97,25 @@ func (p *CurveBase) Rand() (io.Reader, error) { func (p *CurveBase) ModAddMul(a1 []driver.Zr, b1 []driver.Zr, modulo driver.Zr) driver.Zr { sum := p.NewZrFromInt64(0) - for i := 0; i < len(a1); i++ { + for i := range a1 { sum = p.ModAdd(sum, p.ModMul(a1[i], b1[i], modulo), modulo) } + return sum } func (p *CurveBase) ModAddMul2(a1 driver.Zr, c1 driver.Zr, b1 driver.Zr, c2 driver.Zr, m driver.Zr) driver.Zr { return p.ModAdd(p.ModMul(a1, c1, m), p.ModMul(b1, c2, m), m) } + +func (p *CurveBase) ModAddMul3(a1 driver.Zr, a2 driver.Zr, b1 driver.Zr, b2 driver.Zr, c1 driver.Zr, c2 driver.Zr, m driver.Zr) driver.Zr { + return p.ModAdd( + p.ModMul(c1, c2, m), + p.ModAdd( + p.ModMul(a1, a2, m), + p.ModMul(b1, b2, m), + m, + ), + m, + ) +} diff --git a/driver/gurvy/bls12-377.go b/driver/gurvy/bls12-377.go index a16202b..3c9c8e0 100644 --- a/driver/gurvy/bls12-377.go +++ b/driver/gurvy/bls12-377.go @@ -34,19 +34,20 @@ func (g *bls12377G1) Clone(a driver.G1) { func (e *bls12377G1) Copy() driver.G1 { c := &bls12377G1{} c.Set(&e.G1Affine) + return c } func (g *bls12377G1) Add(a driver.G1) { j := bls12377.G1Jac{} j.FromAffine(&g.G1Affine) - j.AddMixed((*bls12377.G1Affine)(&a.(*bls12377G1).G1Affine)) - g.G1Affine.FromJacobian(&j) + j.AddMixed(&a.(*bls12377G1).G1Affine) + g.FromJacobian(&j) } func (g *bls12377G1) Mul(a driver.Zr) driver.G1 { ret := &bls12377G1{} - ret.G1Affine.ScalarMultiplication(&g.G1Affine, &a.(*common.BaseZr).Int) + ret.ScalarMultiplication(&g.G1Affine, &a.(*common.BaseZr).Int) return ret } @@ -68,16 +69,18 @@ func (g *bls12377G1) Mul2InPlace(e driver.Zr, Q driver.G1, f driver.Zr) { } func (g *bls12377G1) Equals(a driver.G1) bool { - return g.G1Affine.Equal(&a.(*bls12377G1).G1Affine) + return g.Equal(&a.(*bls12377G1).G1Affine) } func (g *bls12377G1) Bytes() []byte { - raw := g.G1Affine.RawBytes() + raw := g.RawBytes() + return raw[:] } func (g *bls12377G1) Compressed() []byte { raw := g.G1Affine.Bytes() + return raw[:] } @@ -86,7 +89,7 @@ func (g *bls12377G1) Sub(a driver.G1) { j.FromAffine(&g.G1Affine) k.FromAffine(&a.(*bls12377G1).G1Affine) j.SubAssign(&k) - g.G1Affine.FromJacobian(&j) + g.FromJacobian(&j) } func (g *bls12377G1) IsInfinity() bool { @@ -96,6 +99,7 @@ func (g *bls12377G1) IsInfinity() bool { func (g *bls12377G1) String() string { rawstr := g.G1Affine.String() m := g1StrRegexp.FindAllStringSubmatch(rawstr, -1) + return "(" + strings.TrimLeft(m[0][1], "0") + "," + strings.TrimLeft(m[0][2], "0") + ")" } @@ -120,12 +124,13 @@ func (g *bls12377G2) Clone(a driver.G2) { func (e *bls12377G2) Copy() driver.G2 { c := &bls12377G2{} c.Set(&e.G2Affine) + return c } func (g *bls12377G2) Mul(a driver.Zr) driver.G2 { gc := &bls12377G2{} - gc.G2Affine.ScalarMultiplication(&g.G2Affine, &a.(*common.BaseZr).Int) + gc.ScalarMultiplication(&g.G2Affine, &a.(*common.BaseZr).Int) return gc } @@ -133,17 +138,17 @@ func (g *bls12377G2) Mul(a driver.Zr) driver.G2 { func (g *bls12377G2) Add(a driver.G2) { j := bls12377.G2Jac{} j.FromAffine(&g.G2Affine) - j.AddMixed((*bls12377.G2Affine)(&a.(*bls12377G2).G2Affine)) - g.G2Affine.FromJacobian(&j) + j.AddMixed(&a.(*bls12377G2).G2Affine) + g.FromJacobian(&j) } func (g *bls12377G2) Sub(a driver.G2) { j := bls12377.G2Jac{} j.FromAffine(&g.G2Affine) aJac := bls12377.G2Jac{} - aJac.FromAffine((*bls12377.G2Affine)(&a.(*bls12377G2).G2Affine)) + aJac.FromAffine(&a.(*bls12377G2).G2Affine) j.SubAssign(&aJac) - g.G2Affine.FromJacobian(&j) + g.FromJacobian(&j) } func (g *bls12377G2) Affine() { @@ -151,12 +156,14 @@ func (g *bls12377G2) Affine() { } func (g *bls12377G2) Bytes() []byte { - raw := g.G2Affine.RawBytes() + raw := g.RawBytes() + return raw[:] } func (g *bls12377G2) Compressed() []byte { raw := g.G2Affine.Bytes() + return raw[:] } @@ -165,7 +172,7 @@ func (g *bls12377G2) String() string { } func (g *bls12377G2) Equals(a driver.G2) bool { - return g.G2Affine.Equal(&a.(*bls12377G2).G2Affine) + return g.Equal(&a.(*bls12377G2).G2Affine) } /*********************************************************************/ @@ -176,11 +183,12 @@ type bls12377Gt struct { func (g *bls12377Gt) Exp(x driver.Zr) driver.Gt { copy := bls12377.GT{} + return &bls12377Gt{*copy.Exp(g.GT, &x.(*common.BaseZr).Int)} } func (g *bls12377Gt) Equals(a driver.Gt) bool { - return g.GT.Equal(&a.(*bls12377Gt).GT) + return g.Equal(&a.(*bls12377Gt).GT) } func (g *bls12377Gt) Inverse() { @@ -199,11 +207,12 @@ func (g *bls12377Gt) IsUnity() bool { } func (g *bls12377Gt) ToString() string { - return g.GT.String() + return g.String() } func (g *bls12377Gt) Bytes() []byte { raw := g.GT.Bytes() + return raw[:] } @@ -222,12 +231,13 @@ func (c *Bls12_377) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { affinePoints := make([]bls12377.G1Affine, len(a)) scalars := make([]fr.Element, len(b)) - for i := range len(a) { + for i := range a { affinePoints[i] = a[i].(*bls12377G1).G1Affine scalars[i].SetBigInt(&b[i].(*common.BaseZr).Int) } _, _ = result.MultiExp(affinePoints, scalars, ecc.MultiExpConfig{}) + return &bls12377G1{result} } @@ -287,6 +297,7 @@ func (c *Bls12_377) GenGt() driver.Gt { g2 := c.GenG2() gengt := c.Pairing(g2, g1) gengt = c.FExp(gengt) + return gengt } @@ -324,7 +335,7 @@ func (c *Bls12_377) NewG2() driver.G2 { func (c *Bls12_377) NewG1FromBytes(b []byte) driver.G1 { v := &bls12377G1{} - _, err := v.G1Affine.SetBytes(b) + _, err := v.SetBytes(b) if err != nil { panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) } @@ -334,7 +345,7 @@ func (c *Bls12_377) NewG1FromBytes(b []byte) driver.G1 { func (c *Bls12_377) NewG2FromBytes(b []byte) driver.G2 { v := &bls12377G2{} - _, err := v.G2Affine.SetBytes(b) + _, err := v.SetBytes(b) if err != nil { panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) } @@ -344,7 +355,7 @@ func (c *Bls12_377) NewG2FromBytes(b []byte) driver.G2 { func (c *Bls12_377) NewG1FromCompressed(b []byte) driver.G1 { v := &bls12377G1{} - _, err := v.G1Affine.SetBytes(b) + _, err := v.SetBytes(b) if err != nil { panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) } @@ -354,7 +365,7 @@ func (c *Bls12_377) NewG1FromCompressed(b []byte) driver.G1 { func (c *Bls12_377) NewG2FromCompressed(b []byte) driver.G2 { v := &bls12377G2{} - _, err := v.G2Affine.SetBytes(b) + _, err := v.SetBytes(b) if err != nil { panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) } diff --git a/driver/gurvy/bls12381/bls12-381.go b/driver/gurvy/bls12381/bls12-381.go index ecca872..b53ae99 100644 --- a/driver/gurvy/bls12381/bls12-381.go +++ b/driver/gurvy/bls12381/bls12-381.go @@ -48,12 +48,28 @@ type Zr struct { func (b *Zr) Plus(a driver.Zr) driver.Zr { rv := &Zr{Modulus: b.Modulus} rv.Add(&b.Int, &a.(*Zr).Int) + return rv } +func (b *Zr) IsZero() bool { + return b.BitLen() == 0 +} + +func (b *Zr) BigInt() *big.Int { + return &b.Int +} + +func (b *Zr) IsOne() bool { + bits := b.Bits() + + return len(bits) == 1 && bits[0] == 1 && b.Sign() > 0 +} + func (b *Zr) Minus(a driver.Zr) driver.Zr { rv := &Zr{Modulus: b.Modulus} rv.Sub(&b.Int, &a.(*Zr).Int) + return rv } @@ -69,6 +85,7 @@ func (b *Zr) Mul(x driver.Zr) driver.Zr { rv := &Zr{Modulus: b.Modulus} fr.BigInt(&rv.Int) + return rv } @@ -81,6 +98,7 @@ func (b *Zr) PowMod(x driver.Zr) driver.Zr { rv := &Zr{Modulus: b.Modulus} fr.BigInt(&rv.Int) + return rv } @@ -89,7 +107,7 @@ func (b *Zr) Mod(a driver.Zr) { } func (b *Zr) InvModP(p driver.Zr) { - b.Int.ModInverse(&b.Int, &p.(*Zr).Int) + b.ModInverse(&b.Int, &p.(*Zr).Int) } func (b *Zr) InvModOrder() { @@ -103,7 +121,7 @@ func (b *Zr) InvModOrder() { func (b *Zr) Bytes() []byte { target := b.Int - if b.Int.Sign() < 0 || b.Int.Cmp(&b.Modulus) > 0 { + if b.Sign() < 0 || b.Cmp(&b.Modulus) > 0 { target = *new(big.Int).Set(&b.Int) target = *target.Mod(&target, &b.Modulus) if target.Sign() < 0 { @@ -115,22 +133,23 @@ func (b *Zr) Bytes() []byte { } func (b *Zr) Equals(p driver.Zr) bool { - return b.Int.Cmp(&p.(*Zr).Int) == 0 + return b.Cmp(&p.(*Zr).Int) == 0 } func (b *Zr) Copy() driver.Zr { rv := &Zr{Modulus: b.Modulus} rv.Set(&b.Int) + return rv } func (b *Zr) Clone(a driver.Zr) { raw := a.(*Zr).Int.Bytes() - b.Int.SetBytes(raw) + b.SetBytes(raw) } func (b *Zr) String() string { - return b.Int.Text(16) + return b.Text(16) } func (b *Zr) Neg() { @@ -154,6 +173,7 @@ func (g *G1) Clone(a driver.G1) { func (e *G1) Copy() driver.G1 { c := &G1{} c.Set(&e.G1Affine) + return c } @@ -162,12 +182,12 @@ func (g *G1) Add(a driver.G1) { defer G1Jacs.Put(j) j.FromAffine(&g.G1Affine) j.AddMixed(&a.(*G1).G1Affine) - g.G1Affine.FromJacobian(j) + g.FromJacobian(j) } func (g *G1) Mul(a driver.Zr) driver.G1 { gc := &G1{} - gc.G1Affine.ScalarMultiplication(&g.G1Affine, &a.(*Zr).Int) + gc.ScalarMultiplication(&g.G1Affine, &a.(*Zr).Int) return gc } @@ -177,7 +197,8 @@ func (g *G1) Mul2(e driver.Zr, Q driver.G1, f driver.Zr) driver.G1 { defer G1Jacs.Put(first) first = JointScalarMultiplication(first, &g.G1Affine, &Q.(*G1).G1Affine, &e.(*Zr).Int, &f.(*Zr).Int) gc := &G1{} - gc.G1Affine.FromJacobian(first) + gc.FromJacobian(first) + return gc } @@ -185,20 +206,22 @@ func (g *G1) Mul2InPlace(e driver.Zr, Q driver.G1, f driver.Zr) { first := G1Jacs.Get() defer G1Jacs.Put(first) first = JointScalarMultiplication(first, &g.G1Affine, &Q.(*G1).G1Affine, &e.(*Zr).Int, &f.(*Zr).Int) - g.G1Affine.FromJacobian(first) + g.FromJacobian(first) } func (g *G1) Equals(a driver.G1) bool { - return g.G1Affine.Equal(&a.(*G1).G1Affine) + return g.Equal(&a.(*G1).G1Affine) } func (g *G1) Bytes() []byte { - raw := g.G1Affine.RawBytes() + raw := g.RawBytes() + return raw[:] } func (g *G1) Compressed() []byte { raw := g.G1Affine.Bytes() + return raw[:] } @@ -207,7 +230,7 @@ func (g *G1) Sub(a driver.G1) { j.FromAffine(&g.G1Affine) k.FromAffine(&a.(*G1).G1Affine) j.SubAssign(&k) - g.G1Affine.FromJacobian(&j) + g.FromJacobian(&j) } func (g *G1) IsInfinity() bool { @@ -217,6 +240,7 @@ func (g *G1) IsInfinity() bool { func (g *G1) String() string { rawstr := g.G1Affine.String() m := g1StrRegexp.FindAllStringSubmatch(rawstr, -1) + return "(" + strings.TrimLeft(m[0][1], "0") + "," + strings.TrimLeft(m[0][2], "0") + ")" } @@ -241,12 +265,13 @@ func (g *G2) Clone(a driver.G2) { func (e *G2) Copy() driver.G2 { c := &G2{} c.Set(&e.G2Affine) + return c } func (g *G2) Mul(a driver.Zr) driver.G2 { gc := &G2{} - gc.G2Affine.ScalarMultiplication(&g.G2Affine, &a.(*Zr).Int) + gc.ScalarMultiplication(&g.G2Affine, &a.(*Zr).Int) return gc } @@ -254,17 +279,17 @@ func (g *G2) Mul(a driver.Zr) driver.G2 { func (g *G2) Add(a driver.G2) { j := bls12381.G2Jac{} j.FromAffine(&g.G2Affine) - j.AddMixed((*bls12381.G2Affine)(&a.(*G2).G2Affine)) - g.G2Affine.FromJacobian(&j) + j.AddMixed(&a.(*G2).G2Affine) + g.FromJacobian(&j) } func (g *G2) Sub(a driver.G2) { j := bls12381.G2Jac{} j.FromAffine(&g.G2Affine) aJac := bls12381.G2Jac{} - aJac.FromAffine((*bls12381.G2Affine)(&a.(*G2).G2Affine)) + aJac.FromAffine(&a.(*G2).G2Affine) j.SubAssign(&aJac) - g.G2Affine.FromJacobian(&j) + g.FromJacobian(&j) } func (g *G2) Affine() { @@ -272,12 +297,14 @@ func (g *G2) Affine() { } func (g *G2) Bytes() []byte { - raw := g.G2Affine.RawBytes() + raw := g.RawBytes() + return raw[:] } func (g *G2) Compressed() []byte { raw := g.G2Affine.Bytes() + return raw[:] } @@ -286,7 +313,7 @@ func (g *G2) String() string { } func (g *G2) Equals(a driver.G2) bool { - return g.G2Affine.Equal(&a.(*G2).G2Affine) + return g.Equal(&a.(*G2).G2Affine) } /*********************************************************************/ @@ -297,11 +324,12 @@ type Gt struct { func (g *Gt) Exp(x driver.Zr) driver.Gt { c := bls12381.GT{} + return &Gt{*c.Exp(g.GT, &x.(*Zr).Int)} } func (g *Gt) Equals(a driver.Gt) bool { - return g.GT.Equal(&a.(*Gt).GT) + return g.Equal(&a.(*Gt).GT) } func (g *Gt) Inverse() { @@ -320,11 +348,12 @@ func (g *Gt) IsUnity() bool { } func (g *Gt) ToString() string { - return g.GT.String() + return g.String() } func (g *Gt) Bytes() []byte { raw := g.GT.Bytes() + return raw[:] } @@ -385,6 +414,7 @@ func (c *Curve) GenGt() driver.Gt { g2 := c.GenG2() gengt := c.Pairing(g2, g1) gengt = c.FExp(gengt) + return gengt } @@ -422,7 +452,7 @@ func (c *Curve) NewG2() driver.G2 { func (c *Curve) NewG1FromBytes(b []byte) driver.G1 { v := &G1{} - _, err := v.G1Affine.SetBytes(b) + _, err := v.SetBytes(b) if err != nil { panic(fmt.Sprintf("set bytes failed [%s]", err.Error())) } @@ -472,7 +502,7 @@ func (c *Curve) NewGtFromBytes(b []byte) driver.Gt { func (c *Curve) ModNeg(a1, m driver.Zr) driver.Zr { res := &Zr{Modulus: c.Modulus} - res.Int.Sub(&m.(*Zr).Int, &a1.(*Zr).Int) + res.Sub(&m.(*Zr).Int, &a1.(*Zr).Int) res.Int.Mod(&res.Int, &m.(*Zr).Int) return res @@ -491,6 +521,7 @@ func (c *Curve) ModSub(a1, b1, m driver.Zr) driver.Zr { res := &Zr{Modulus: c.Modulus} a1Fr.BigInt(&res.Int) + return res } @@ -500,7 +531,8 @@ func (c *Curve) GroupOrder() driver.Zr { func (c *Curve) NewZrFromBytes(b []byte) driver.Zr { res := &Zr{Modulus: c.Modulus} - res.Int.SetBytes(b) + res.SetBytes(b) + return res } @@ -512,6 +544,10 @@ func (c *Curve) NewZrFromUint64(i uint64) driver.Zr { return &Zr{Int: *new(big.Int).SetUint64(i), Modulus: c.Modulus} } +func (c *Curve) NewZrFromBigInt(i *big.Int) driver.Zr { + return &Zr{Int: *i, Modulus: c.Modulus} +} + func (c *Curve) NewRandomZr(rng io.Reader) driver.Zr { e := frElements.Get() defer frElements.Put(e) @@ -522,6 +558,7 @@ func (c *Curve) NewRandomZr(rng io.Reader) driver.Zr { res := &Zr{Modulus: c.Modulus} e.BigInt(&res.Int) + return res } @@ -529,6 +566,7 @@ func (c *Curve) HashToZr(data []byte) driver.Zr { digest := sha256.Sum256(data) digestBig := new(big.Int).SetBytes(digest[:]) digestBig.Mod(digestBig, &c.Modulus) + return &Zr{Int: *digestBig, Modulus: c.Modulus} } @@ -585,6 +623,7 @@ func (c *Curve) ModMul(a1, b1, m driver.Zr) driver.Zr { res := &Zr{Modulus: c.Modulus} a1Fr.BigInt(&res.Int) + return res } @@ -597,7 +636,7 @@ func (c *Curve) ModAddMul(a1, b1 []driver.Zr, m driver.Zr) driver.Zr { defer frElements.Put(sum) sum.SetZero() - for i := 0; i < len(a1); i++ { + for i := range a1 { a1Fr.SetBigInt(&a1[i].(*Zr).Int) b1Fr.SetBigInt(&b1[i].(*Zr).Int) a1Fr.Mul(a1Fr, b1Fr) @@ -606,6 +645,7 @@ func (c *Curve) ModAddMul(a1, b1 []driver.Zr, m driver.Zr) driver.Zr { res := &Zr{Modulus: c.Modulus} sum.BigInt(&res.Int) + return res } @@ -631,6 +671,46 @@ func (c *Curve) ModAddMul2(a1 driver.Zr, c1 driver.Zr, b1 driver.Zr, c2 driver.Z res := &Zr{Modulus: c.Modulus} sum.BigInt(&res.Int) + + return res +} + +func (c *Curve) ModAddMul3( + a1 driver.Zr, + a2 driver.Zr, + b1 driver.Zr, + b2 driver.Zr, + d1 driver.Zr, + d2 driver.Zr, + m driver.Zr, +) driver.Zr { + tmp1 := frElements.Get() + defer frElements.Put(tmp1) + tmp2 := frElements.Get() + defer frElements.Put(tmp2) + + sum := frElements.Get() + defer frElements.Put(sum) + + sum.SetZero() + tmp1.SetBigInt(&a1.(*Zr).Int) + tmp2.SetBigInt(&a2.(*Zr).Int) + tmp1.Mul(tmp1, tmp2) + sum.Add(sum, tmp1) + + tmp1.SetBigInt(&b1.(*Zr).Int) + tmp2.SetBigInt(&b2.(*Zr).Int) + tmp1.Mul(tmp1, tmp2) + sum.Add(sum, tmp1) + + tmp1.SetBigInt(&d1.(*Zr).Int) + tmp2.SetBigInt(&d2.(*Zr).Int) + tmp1.Mul(tmp1, tmp2) + sum.Add(sum, tmp1) + + res := &Zr{Modulus: c.Modulus} + sum.BigInt(&res.Int) + return res } @@ -647,6 +727,7 @@ func (c *Curve) ModAdd(a1, b1, m driver.Zr) driver.Zr { res := &Zr{Modulus: c.Modulus} a1Fr.BigInt(&res.Int) + return res } @@ -671,7 +752,7 @@ func (c *Curve) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { affinePoints := make([]bls12381.G1Affine, len(a)) scalars := make([]fr.Element, len(b)) - for i := range len(a) { + for i := range a { affinePoints[i] = a[i].(*G1).G1Affine scalars[i].SetBigInt(&b[i].(*Zr).Int) } @@ -681,7 +762,8 @@ func (c *Curve) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { _, _ = first.MultiExp(affinePoints, scalars, ecc.MultiExpConfig{}) gc := &G1{} - gc.G1Affine.FromJacobian(first) + gc.FromJacobian(first) + return gc } @@ -696,7 +778,8 @@ func NewBBSCurve() *BBSCurve { func (c *BBSCurve) HashToG1(data []byte) driver.G1 { hashFunc := func() hash.Hash { // We pass a null key so error is impossible here. - h, _ := blake2b.New512(nil) //nolint:errcheck + h, _ := blake2b.New512(nil) + return h } @@ -720,7 +803,8 @@ func (c *BBSCurve) HashToG2(data []byte) driver.G2 { func (c *BBSCurve) HashToG1WithDomain(data, domain []byte) driver.G1 { hashFunc := func() hash.Hash { // We pass a null key so error is impossible here. - h, _ := blake2b.New512(nil) //nolint:errcheck + h, _ := blake2b.New512(nil) + return h } @@ -794,7 +878,7 @@ func JointScalarMultiplication(p *bls12381.G1Jac, a1, a2 *bls12381.G1Affine, s1, for i := hiWordIndex; i >= 0; i-- { mask := uint64(3) << 62 - for j := 0; j < 32; j++ { + for j := range 32 { res.Double(&res).Double(&res) b1 := (s[0][i] & mask) >> (62 - 2*j) b2 := (s[1][i] & mask) >> (62 - 2*j) @@ -807,6 +891,6 @@ func JointScalarMultiplication(p *bls12381.G1Jac, a1, a2 *bls12381.G1Affine, s1, } p.Set(&res) - return p + return p } diff --git a/driver/gurvy/bn254.go b/driver/gurvy/bn254.go index 639d89e..004444c 100644 --- a/driver/gurvy/bn254.go +++ b/driver/gurvy/bn254.go @@ -35,19 +35,20 @@ func (g *bn254G1) Clone(a driver.G1) { func (e *bn254G1) Copy() driver.G1 { c := &bn254G1{} c.Set(&e.G1Affine) + return c } func (g *bn254G1) Add(a driver.G1) { j := bn254.G1Jac{} j.FromAffine(&g.G1Affine) - j.AddMixed((*bn254.G1Affine)(&a.(*bn254G1).G1Affine)) - g.G1Affine.FromJacobian(&j) + j.AddMixed(&a.(*bn254G1).G1Affine) + g.FromJacobian(&j) } func (g *bn254G1) Mul(a driver.Zr) driver.G1 { res := &bn254G1{} - res.G1Affine.ScalarMultiplication(&g.G1Affine, &a.(*common.BaseZr).Int) + res.ScalarMultiplication(&g.G1Affine, &a.(*common.BaseZr).Int) return res } @@ -69,16 +70,18 @@ func (g *bn254G1) Mul2InPlace(e driver.Zr, Q driver.G1, f driver.Zr) { } func (g *bn254G1) Equals(a driver.G1) bool { - return g.G1Affine.Equal(&a.(*bn254G1).G1Affine) + return g.Equal(&a.(*bn254G1).G1Affine) } func (g *bn254G1) Bytes() []byte { - raw := g.G1Affine.RawBytes() + raw := g.RawBytes() + return raw[:] } func (g *bn254G1) Compressed() []byte { raw := g.G1Affine.Bytes() + return raw[:] } @@ -87,7 +90,7 @@ func (g *bn254G1) Sub(a driver.G1) { j.FromAffine(&g.G1Affine) k.FromAffine(&a.(*bn254G1).G1Affine) j.SubAssign(&k) - g.G1Affine.FromJacobian(&j) + g.FromJacobian(&j) } func (g *bn254G1) IsInfinity() bool { @@ -99,6 +102,7 @@ var g1StrRegexp *regexp.Regexp = regexp.MustCompile(`^E\([[]([0-9]+),([0-9]+)[]] func (g *bn254G1) String() string { rawstr := g.G1Affine.String() m := g1StrRegexp.FindAllStringSubmatch(rawstr, -1) + return "(" + strings.TrimLeft(m[0][1], "0") + "," + strings.TrimLeft(m[0][2], "0") + ")" } @@ -123,12 +127,13 @@ func (g *bn254G2) Clone(a driver.G2) { func (e *bn254G2) Copy() driver.G2 { c := &bn254G2{} c.Set(&e.G2Affine) + return c } func (g *bn254G2) Mul(a driver.Zr) driver.G2 { gc := &bn254G2{} - gc.G2Affine.ScalarMultiplication(&g.G2Affine, &a.(*common.BaseZr).Int) + gc.ScalarMultiplication(&g.G2Affine, &a.(*common.BaseZr).Int) return gc } @@ -136,17 +141,17 @@ func (g *bn254G2) Mul(a driver.Zr) driver.G2 { func (g *bn254G2) Add(a driver.G2) { j := bn254.G2Jac{} j.FromAffine(&g.G2Affine) - j.AddMixed((*bn254.G2Affine)(&a.(*bn254G2).G2Affine)) - g.G2Affine.FromJacobian(&j) + j.AddMixed(&a.(*bn254G2).G2Affine) + g.FromJacobian(&j) } func (g *bn254G2) Sub(a driver.G2) { j := bn254.G2Jac{} j.FromAffine(&g.G2Affine) aJac := bn254.G2Jac{} - aJac.FromAffine((*bn254.G2Affine)(&a.(*bn254G2).G2Affine)) + aJac.FromAffine(&a.(*bn254G2).G2Affine) j.SubAssign(&aJac) - g.G2Affine.FromJacobian(&j) + g.FromJacobian(&j) } func (g *bn254G2) Affine() { @@ -154,12 +159,14 @@ func (g *bn254G2) Affine() { } func (g *bn254G2) Bytes() []byte { - raw := g.G2Affine.RawBytes() + raw := g.RawBytes() + return raw[:] } func (g *bn254G2) Compressed() []byte { raw := g.G2Affine.Bytes() + return raw[:] } @@ -168,7 +175,7 @@ func (g *bn254G2) String() string { } func (g *bn254G2) Equals(a driver.G2) bool { - return g.G2Affine.Equal(&a.(*bn254G2).G2Affine) + return g.Equal(&a.(*bn254G2).G2Affine) } /*********************************************************************/ @@ -179,11 +186,12 @@ type bn254Gt struct { func (g *bn254Gt) Exp(x driver.Zr) driver.Gt { copy := bn254.GT{} + return &bn254Gt{*copy.Exp(g.GT, &x.(*common.BaseZr).Int)} } func (g *bn254Gt) Equals(a driver.Gt) bool { - return g.GT.Equal(&a.(*bn254Gt).GT) + return g.Equal(&a.(*bn254Gt).GT) } func (g *bn254Gt) Inverse() { @@ -202,11 +210,12 @@ func (g *bn254Gt) IsUnity() bool { } func (g *bn254Gt) ToString() string { - return g.GT.String() + return g.String() } func (g *bn254Gt) Bytes() []byte { raw := g.GT.Bytes() + return raw[:] } @@ -225,12 +234,13 @@ func (c *Bn254) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { affinePoints := make([]bn254.G1Affine, len(a)) scalars := make([]fr.Element, len(b)) - for i := range len(a) { + for i := range a { affinePoints[i] = a[i].(*bn254G1).G1Affine scalars[i].SetBigInt(&b[i].(*common.BaseZr).Int) } _, _ = result.MultiExp(affinePoints, scalars, ecc.MultiExpConfig{}) + return &bn254G1{result} } @@ -290,6 +300,7 @@ func (c *Bn254) GenGt() driver.Gt { g2 := c.GenG2() gengt := c.Pairing(g2, g1) gengt = c.FExp(gengt) + return gengt } diff --git a/driver/gurvy/custom.go b/driver/gurvy/custom.go index e09fa8e..2c9c237 100644 --- a/driver/gurvy/custom.go +++ b/driver/gurvy/custom.go @@ -43,6 +43,7 @@ func min(a, b int) int { if a < b { return a } + return b } @@ -56,10 +57,11 @@ func ExpandMsgXmd(msg, dst []byte, lenInBytes int, hashFunc func() hash.Hash) ([ if ell > 255 { return nil, errors.New("invalid lenInBytes") } - if len(dst) > 255 { + dstLen := len(dst) + if dstLen > 255 { return nil, errors.New("invalid domain size (>255 bytes)") } - sizeDomain := uint8(len(dst)) + sizeDomain := uint8(dstLen) // Z_pad = I2OSP(0, r_in_bytes) // l_i_b_str = I2OSP(len_in_bytes, 2) @@ -72,7 +74,7 @@ func ExpandMsgXmd(msg, dst []byte, lenInBytes int, hashFunc func() hash.Hash) ([ if _, err := h.Write(msg); err != nil { return nil, err } - if _, err := h.Write([]byte{uint8(lenInBytes >> 8), uint8(lenInBytes), uint8(0)}); err != nil { + if _, err := h.Write([]byte{uint8(lenInBytes >> 8), uint8(lenInBytes), uint8(0)}); err != nil { // #nosec G115 return nil, err } if _, err := h.Write(dst); err != nil { @@ -106,7 +108,7 @@ func ExpandMsgXmd(msg, dst []byte, lenInBytes int, hashFunc func() hash.Hash) ([ // b_i = H(strxor(b₀, b_(i - 1)) ∥ I2OSP(i, 1) ∥ DST_prime) h.Reset() strxor := make([]byte, h.Size()) - for j := 0; j < h.Size(); j++ { + for j := range h.Size() { strxor[j] = b0[j] ^ b1[j] } if _, err := h.Write(strxor); err != nil { @@ -124,6 +126,7 @@ func ExpandMsgXmd(msg, dst []byte, lenInBytes int, hashFunc func() hash.Hash) ([ b1 = h.Sum(nil) copy(res[h.Size()*(i-1):min(h.Size()*i, len(res))], b1) } + return res, nil } @@ -145,7 +148,7 @@ func Hash(msg, dst []byte, count int, hashFunc func() hash.Hash) ([]fp.Element, vv := pool.BigInt.Get() res := make([]fp.Element, count) - for i := 0; i < count; i++ { + for i := range count { vv.SetBytes(pseudoRandomBytes[i*L : (i+1)*L]) res[i].SetBigInt(vv) } @@ -185,5 +188,6 @@ func HashToG1GenericBESwu(msg, dst []byte, hashFunc func() hash.Hash) (bls12381. toGurvyAffine(&Q1).FromJacobian(&_Q1) res := toGurvyAffine(&Q1) + return *res, nil } diff --git a/driver/kilic/bls12-381.go b/driver/kilic/bls12-381.go index 85d71d8..a69c14e 100644 --- a/driver/kilic/bls12-381.go +++ b/driver/kilic/bls12-381.go @@ -29,6 +29,7 @@ func (g *bls12_381G1) Clone(a driver.G1) { func (e *bls12_381G1) Copy() driver.G1 { c := &bls12_381G1{G1: *bls12381.NewG1()} c.Set(&e.PointG1) + return c } @@ -66,17 +67,20 @@ func (g *bls12_381G1) Mul2InPlace(e driver.Zr, Q driver.G1, f driver.Zr) { func (g *bls12_381G1) Equals(a driver.G1) bool { g1 := bls12381.NewG1() + return g1.Equal(&a.(*bls12_381G1).PointG1, &g.PointG1) } func (g *bls12_381G1) Bytes() []byte { g1 := bls12381.NewG1() raw := g1.ToUncompressed(&g.PointG1) + return raw[:] } func (g *bls12_381G1) Compressed() []byte { - raw := g.G1.ToCompressed(&g.PointG1) + raw := g.ToCompressed(&g.PointG1) + return raw[:] } @@ -85,7 +89,7 @@ func (g *bls12_381G1) Sub(a driver.G1) { } func (g *bls12_381G1) IsInfinity() bool { - return g.G1.IsZero(&g.PointG1) + return g.IsZero(&g.PointG1) } func (g *bls12_381G1) String() string { @@ -116,6 +120,7 @@ func (e *bls12_381G2) Copy() driver.G2 { G2: *bls12381.NewG2(), } c.Set(&e.PointG2) + return c } @@ -147,12 +152,14 @@ func (g *bls12_381G2) Affine() { func (g *bls12_381G2) Bytes() []byte { g2 := bls12381.NewG2() raw := g2.ToUncompressed(&g.PointG2) + return raw[:] } func (g *bls12_381G2) Compressed() []byte { g2 := bls12381.NewG2() raw := g2.ToCompressed(&g.PointG2) + return raw[:] } @@ -163,6 +170,7 @@ func (g *bls12_381G2) String() string { func (g *bls12_381G2) Equals(a driver.G2) bool { g2 := bls12381.NewG2() + return g2.Equal(&a.(*bls12_381G2).PointG2, &g.PointG2) } @@ -187,7 +195,7 @@ func (g *bls12_381Gt) Exp(x driver.Zr) driver.Gt { } func (g *bls12_381Gt) Equals(a driver.Gt) bool { - return a.(*bls12_381Gt).E.Equal(&g.E) + return g.Equal(&a.(*bls12_381Gt).E) } func (g *bls12_381Gt) Inverse() { @@ -205,7 +213,7 @@ func (g *bls12_381Gt) Mul(a driver.Gt) { } func (g *bls12_381Gt) IsUnity() bool { - return g.E.IsOne() + return g.IsOne() } func (g *bls12_381Gt) ToString() string { @@ -217,7 +225,8 @@ func (g *bls12_381Gt) Bytes() []byte { if !g.GTInitialised { g.GT = *bls12381.NewGT() } - raw := g.GT.ToBytes(&g.E) + raw := g.ToBytes(&g.E) + return raw[:] } @@ -237,9 +246,10 @@ type Bls12_381 struct { func (c *Bls12_381) MultiScalarMul(a []driver.G1, b []driver.Zr) driver.G1 { g1 := c.NewG1() - for i := 0; i < len(a); i++ { + for i := range a { g1.Add(a[i].Mul(b[i])) } + return g1 } @@ -273,6 +283,7 @@ func (c *Bls12_381) FExp(a driver.Gt) driver.Gt { func (c *Bls12_381) GenG1() driver.G1 { g := bls12381.NewG1() g1 := g.One() + return &bls12_381G1{ G1: *g, PointG1: *g1, @@ -282,6 +293,7 @@ func (c *Bls12_381) GenG1() driver.G1 { func (c *Bls12_381) GenG2() driver.G2 { g := bls12381.NewG2() g2 := g.One() + return &bls12_381G2{ G2: *g, PointG2: *g2, @@ -293,6 +305,7 @@ func (c *Bls12_381) GenGt() driver.Gt { g2 := c.GenG2() gengt := c.Pairing(g2, g1) gengt = c.FExp(gengt) + return gengt } diff --git a/driver/kilic/custom.go b/driver/kilic/custom.go index 38a86a0..a8a266d 100644 --- a/driver/kilic/custom.go +++ b/driver/kilic/custom.go @@ -50,13 +50,14 @@ func (fe *Fe) setBytes(in []byte) *Fe { padded := make([]byte, fpByteSize) copy(padded[fpByteSize-l:], in[:]) var a int - for i := 0; i < fpNumberOfLimbs; i++ { + for i := range fpNumberOfLimbs { a = fpByteSize - 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 } @@ -72,6 +73,7 @@ func (fe *Fe) cmp(fe2 *Fe) int { return -1 } } + return 0 } @@ -90,6 +92,7 @@ func (fe *Fe) set(fe2 *Fe) *Fe { fe[3] = fe2[3] fe[4] = fe2[4] fe[5] = fe2[5] + return fe } @@ -97,6 +100,7 @@ func (e *Fe) signBE() bool { negZ, z := new(Fe), new(Fe) fromMont(z, e) neg(negZ, z) + return negZ.cmp(z) > -1 } @@ -130,7 +134,7 @@ func isogenyMapG1(x, y *Fe) func swuMapG1Pre(u *Fe) (*Fe, *Fe, *Fe) { var params = swuParamsForG1 var tv [4]*Fe - for i := 0; i < 4; i++ { + for i := range 4 { tv[i] = new(Fe) } square(tv[0], u) @@ -181,6 +185,7 @@ func SwuMapG1BE(u *Fe) (*Fe, *Fe) { if y.signBE() != u.signBE() { neg(y, y) } + return x, y } @@ -197,7 +202,8 @@ func feAtPos(pos int, p *bls12381.PointG1) *Fe { func HashToG1GenericBESwu(data, domain []byte) (*bls12381.PointG1, error) { hashFunc := func() hash.Hash { // We pass a null key so error is impossible here. - h, _ := blake2b.New512(nil) //nolint:errcheck + h, _ := blake2b.New512(nil) + return h } @@ -226,6 +232,7 @@ func HashToCurveGenericBESwu(msg, domain []byte, hashFunc func() hash.Hash) (*bl g.Affine(p0) isogenyMapG1(feAtPos(0, p0), feAtPos(1, p0)) g.ClearCofactor(p0) + return g.Affine(p0), nil } @@ -236,7 +243,7 @@ func hashToFpXMD(f func() hash.Hash, msg []byte, domain []byte, count int) ([]*F } els := make([]*Fe, count) - for i := 0; i < count; i++ { + for i := range count { var err error els[i], err = from64Bytes(randBytes[i*64 : (i+1)*64]) @@ -244,21 +251,25 @@ func hashToFpXMD(f func() hash.Hash, msg []byte, domain []byte, count int) ([]*F return nil, err } } + return els, nil } func expandMsgXMD(f func() hash.Hash, msg []byte, domain []byte, outLen int) ([]byte, error) { h := f() - domainLen := uint8(len(domain)) - if domainLen > 255 { + if len(domain) > 255 { return nil, errors.New("invalid domain length") } + if outLen > 65535 { + return nil, errors.New("invalid outLen") + } + domainLen := uint8(len(domain)) // #nosec G115 // DST_prime = DST || I2OSP(len(DST), 1) // b_0 = H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime) _, _ = h.Write(make([]byte, h.BlockSize())) _, _ = h.Write(msg) - _, _ = h.Write([]byte{uint8(outLen >> 8), uint8(outLen)}) + _, _ = h.Write([]byte{uint8(outLen >> 8), uint8(outLen)}) // #nosec G115 _, _ = h.Write([]byte{0}) _, _ = h.Write(domain) _, _ = h.Write([]byte{domainLen}) @@ -280,7 +291,7 @@ func expandMsgXMD(f func() hash.Hash, msg []byte, domain []byte, outLen int) ([] h.Reset() // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) tmp := make([]byte, h.Size()) - for j := 0; j < h.Size(); j++ { + for j := range h.Size() { tmp[j] = b0[j] ^ bi[j] } _, _ = h.Write(tmp) @@ -326,6 +337,7 @@ func from64Bytes(in []byte) (*Fe, error) { mul(e0, e0, &F) add(e1, e1, e0) + return e1, nil } @@ -339,5 +351,6 @@ func fromBytes(in []byte) (*Fe, error) { return nil, errors.New("must be less than modulus") } toMont(fe, fe) + return fe, nil } diff --git a/driver/kilic/custom_generic.go b/driver/kilic/custom_generic.go index 860a92e..6fa670b 100644 --- a/driver/kilic/custom_generic.go +++ b/driver/kilic/custom_generic.go @@ -17,6 +17,7 @@ func madd0(a, b, c uint64) (hi uint64) { hi, lo = bits.Mul64(a, b) _, carry = bits.Add64(lo, c, 0) hi, _ = bits.Add64(hi, 0, carry) + return } @@ -26,6 +27,7 @@ func madd1(a, b, c uint64) (hi uint64, lo uint64) { hi, lo = bits.Mul64(a, b) lo, carry = bits.Add64(lo, c, 0) hi, _ = bits.Add64(hi, 0, carry) + return } @@ -37,6 +39,7 @@ func madd2(a, b, c, d uint64) (hi uint64, lo uint64) { hi, _ = bits.Add64(hi, 0, carry) lo, carry = bits.Add64(lo, c, 0) hi, _ = bits.Add64(hi, 0, carry) + return } @@ -47,11 +50,11 @@ func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) { hi, _ = bits.Add64(hi, 0, carry) lo, carry = bits.Add64(lo, c, 0) hi, _ = bits.Add64(hi, e, carry) + return } func mul(z, x, y *Fe) { - var t [6]uint64 var c [3]uint64 { @@ -159,6 +162,7 @@ func mul(z, x, y *Fe) { // if z > q --> z -= q // note: this is NOT constant time + //nolint:staticcheck 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) diff --git a/driver/math.go b/driver/math.go index 97e9e52..f2a6c20 100644 --- a/driver/math.go +++ b/driver/math.go @@ -4,103 +4,348 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ +// Package driver defines the interface layer for pairing-based cryptography implementations. +// This package uses a driver pattern to support multiple backend implementations (AMCL, Gurvy, Kilic) +// while providing a consistent API to the higher-level math package. +// +// # Architecture +// +// The driver package defines interfaces for: +// - Curve: Factory and operations for a specific elliptic curve +// - Zr: Scalar field elements (integers modulo curve order) +// - G1: Points on the first curve group +// - G2: Points on the second curve group (twisted curve) +// - Gt: Elements in the target group (pairing results) +// +// # Implementing a New Backend +// +// To add a new backend implementation: +// 1. Implement all five interfaces (Curve, Zr, G1, G2, Gt) +// 2. Ensure thread-safety if required by your use case +// 3. Handle edge cases (point at infinity, zero scalar, etc.) +// 4. Validate all inputs in deserialization methods +// 5. Register your implementation in the math package +// +// # Thread Safety +// +// Implementations are not required to be thread-safe. Users should implement +// their own synchronization when sharing instances across goroutines. package driver import ( "io" + "math/big" ) +// Curve defines the interface for a pairing-friendly elliptic curve implementation. +// It provides factory methods for creating group elements, pairing operations, +// and various cryptographic primitives. +// +// Implementations must handle: +// - Point validation during deserialization +// - Proper error handling (may panic on invalid input) +// - Consistent serialization formats +// - Efficient pairing computations type Curve interface { + // Pairing computes the bilinear pairing e(G2, G1) → Gt. Pairing(G2, G1) Gt + + // Pairing2 efficiently computes e(p2a, p1a) * e(p2b, p1b). Pairing2(p2a, p2b G2, p1a, p1b G1) Gt + + // FExp performs the final exponentiation in pairing computation. FExp(Gt) Gt + + // ModMul computes (a1 * b1) mod m. ModMul(a1, b1, m Zr) Zr + + // ModNeg computes (-a1) mod m. ModNeg(a1, m Zr) Zr + + // GenG1 returns the generator point for the G1 group. GenG1() G1 + + // GenG2 returns the generator point for the G2 group. GenG2() G2 + + // GenGt returns the generator (identity) element for the Gt group. GenGt() Gt + + // GroupOrder returns the order of the curve groups as a Zr element. GroupOrder() Zr + + // CoordinateByteSize returns the size of a single coordinate in bytes. CoordinateByteSize() int + + // G1ByteSize returns the size of an uncompressed G1 point in bytes. G1ByteSize() int + + // CompressedG1ByteSize returns the size of a compressed G1 point in bytes. CompressedG1ByteSize() int + + // G2ByteSize returns the size of an uncompressed G2 point in bytes. G2ByteSize() int + + // CompressedG2ByteSize returns the size of a compressed G2 point in bytes. CompressedG2ByteSize() int + + // ScalarByteSize returns the size of a scalar (Zr) in bytes. ScalarByteSize() int + + // NewG1 creates a new G1 point at the identity (point at infinity). NewG1() G1 + + // NewG2 creates a new G2 point at the identity (point at infinity). NewG2() G2 + + // NewZrFromBytes deserializes a Zr scalar from bytes. NewZrFromBytes(b []byte) Zr + + // NewZrFromInt64 creates a Zr scalar from an int64 value. NewZrFromInt64(i int64) Zr + + // NewZrFromUint64 creates a Zr scalar from a uint64 value. NewZrFromUint64(i uint64) Zr + + // NewZrFromBigInt creates a Zr scalar from a *big.Int, reducing modulo the curve order. + NewZrFromBigInt(i *big.Int) Zr + + // NewG1FromBytes deserializes a G1 point from uncompressed bytes. + // May panic if bytes are invalid. NewG1FromBytes(b []byte) G1 + + // NewG1FromCompressed deserializes a G1 point from compressed bytes. + // May panic if bytes are invalid. NewG1FromCompressed(b []byte) G1 + + // NewG2FromBytes deserializes a G2 point from uncompressed bytes. + // May panic if bytes are invalid. NewG2FromBytes(b []byte) G2 + + // NewG2FromCompressed deserializes a G2 point from compressed bytes. + // May panic if bytes are invalid. NewG2FromCompressed(b []byte) G2 + + // NewGtFromBytes deserializes a Gt element from bytes. + // May panic if bytes are invalid. NewGtFromBytes(b []byte) Gt + + // ModAdd computes (a + b) mod m. ModAdd(a, b, m Zr) Zr + + // ModSub computes (a - b) mod m. ModSub(a, b, m Zr) Zr + + // HashToZr hashes data to a scalar using a cryptographic hash function. HashToZr(data []byte) Zr + + // HashToG1 hashes data to a G1 point using a hash-to-curve algorithm. HashToG1(data []byte) G1 + + // HashToG1WithDomain hashes data to G1 with domain separation. HashToG1WithDomain(data, domain []byte) G1 + + // HashToG2 hashes data to a G2 point using a hash-to-curve algorithm. HashToG2(data []byte) G2 + + // HashToG2WithDomain hashes data to G2 with domain separation. HashToG2WithDomain(data, domain []byte) G2 + + // NewRandomZr generates a random scalar using the provided RNG. NewRandomZr(rng io.Reader) Zr + + // Rand returns a cryptographically secure random number generator. Rand() (io.Reader, error) + + // ModAddMul computes sum of products: (driver[0]*driver2[0] + ... + driver[n]*driver2[n]) mod zr. ModAddMul(driver []Zr, driver2 []Zr, zr Zr) Zr + + // ModAddMul2 computes (a1*c1 + b1*c2) mod m. ModAddMul2(a1 Zr, c1 Zr, b1 Zr, c2 Zr, m Zr) Zr + + // ModAddMul3 computes (a1*a2 + b1*b2 + c1*c2) mod m. + ModAddMul3(a1 Zr, a2 Zr, b1 Zr, b2 Zr, c1 Zr, c2 Zr, m Zr) Zr + + // MultiScalarMul computes multi-scalar multiplication: [b[0]]a[0] + [b[1]]a[1] + ... + [b[n]]a[n]. MultiScalarMul(a []G1, b []Zr) G1 } +// Zr represents an element in the scalar field of an elliptic curve. +// Scalars are integers modulo the curve's group order and are used for +// scalar multiplication and other arithmetic operations. +// +// Implementations must: +// - Handle modular arithmetic correctly +// - Support conversion to/from various numeric types +// - Provide efficient arithmetic operations +// - Handle edge cases (zero, one, etc.) type Zr interface { + // IsZero returns true if this scalar is zero. + IsZero() bool + + // IsOne returns true if this scalar is one. + IsOne() bool + + // BigInt returns the scalar as a *big.Int. + BigInt() *big.Int + + // Plus returns a new Zr representing (this + Zr) mod order. Plus(Zr) Zr + + // Minus returns a new Zr representing (this - Zr) mod order. Minus(Zr) Zr + + // Mul returns a new Zr representing (this * Zr) mod order. Mul(Zr) Zr + + // Mod sets this scalar to (this mod Zr) in place. Mod(Zr) + + // PowMod returns a new Zr representing this^Zr mod order. PowMod(Zr) Zr + + // InvModP sets this scalar to its modular inverse modulo Zr in place. InvModP(Zr) + + // Bytes returns the byte representation of this scalar. Bytes() []byte + + // Equals returns true if this scalar equals the given scalar. Equals(Zr) bool + + // Copy returns a deep copy of this scalar. Copy() Zr + + // Clone copies the value of a into this scalar. Clone(a Zr) + + // String returns a string representation of this scalar. String() string + + // Neg negates this scalar in place (this = -this mod order). Neg() + + // InvModOrder sets this scalar to its modular inverse modulo the curve order in place. InvModOrder() } +// G1 represents a point on the first elliptic curve group. +// G1 is typically used for signatures and commitments in pairing-based protocols. +// +// Implementations must: +// - Handle the point at infinity (identity element) correctly +// - Validate points during deserialization +// - Support both compressed and uncompressed serialization +// - Provide efficient group operations type G1 interface { + // Clone copies the value of the given G1 point into this point. Clone(G1) + + // Copy returns a deep copy of this G1 point. Copy() G1 + + // Add adds the given G1 point to this point in place (this = this + G1). Add(G1) + + // Mul returns a new G1 point representing scalar multiplication [Zr]this. Mul(Zr) G1 + + // Mul2 computes [e]this + [f]Q and returns the result as a new G1 point. Mul2(e Zr, Q G1, f Zr) G1 + + // Mul2InPlace computes [e]this + [f]Q and stores the result in this point. Mul2InPlace(e Zr, Q G1, f Zr) + + // Equals returns true if this point equals the given point. Equals(G1) bool + + // Bytes returns the uncompressed byte representation of this point. Bytes() []byte + + // Compressed returns the compressed byte representation of this point. Compressed() []byte + + // Sub subtracts the given G1 point from this point in place (this = this - G1). Sub(G1) + + // IsInfinity returns true if this point is the point at infinity (identity element). IsInfinity() bool + + // String returns a string representation of this point. String() string + + // Neg negates this point in place (this = -this). Neg() } +// G2 represents a point on the second elliptic curve group (twisted curve). +// G2 is typically used for public keys in pairing-based protocols. +// +// Implementations must: +// - Handle the point at infinity (identity element) correctly +// - Validate points during deserialization +// - Support both compressed and uncompressed serialization +// - Provide efficient group operations +// - Handle affine coordinate conversion when needed type G2 interface { + // Clone copies the value of the given G2 point into this point. Clone(G2) + + // Copy returns a deep copy of this G2 point. Copy() G2 + + // Mul returns a new G2 point representing scalar multiplication [Zr]this. Mul(Zr) G2 + + // Add adds the given G2 point to this point in place (this = this + G2). Add(G2) + + // Sub subtracts the given G2 point from this point in place (this = this - G2). Sub(G2) + + // Affine converts this point to affine coordinates in place. Affine() + + // Bytes returns the uncompressed byte representation of this point. Bytes() []byte + + // Compressed returns the compressed byte representation of this point. Compressed() []byte + + // String returns a string representation of this point. String() string + + // Equals returns true if this point equals the given point. Equals(G2) bool } +// Gt represents an element in the target group of a pairing operation. +// Gt is a multiplicative group that results from pairing G1 and G2 elements. +// +// Implementations must: +// - Handle the identity element (unity) correctly +// - Provide efficient multiplication and exponentiation +// - Support inversion operations +// - Ensure consistent serialization type Gt interface { + // Equals returns true if this element equals the given element. Equals(Gt) bool + + // Inverse computes the multiplicative inverse of this element in place (this = this^-1). Inverse() + + // Mul multiplies this element by the given element in place (this = this * Gt). Mul(Gt) + + // IsUnity returns true if this element is the identity element (unity). IsUnity() bool + + // ToString returns a string representation of this element. ToString() string + + // Bytes returns the byte representation of this element. Bytes() []byte + + // Exp returns a new Gt element representing this^Zr (exponentiation). Exp(Zr) Gt } diff --git a/go.mod b/go.mod index aac44f2..37b742c 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,12 @@ module github.com/IBM/mathlib -go 1.24.0 - -toolchain go1.24.11 +go 1.25.7 require ( - github.com/consensys/gnark-crypto v0.19.2 + github.com/consensys/gnark-crypto v0.20.1 github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 - github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.11.1 - golang.org/x/crypto v0.47.0 + golang.org/x/crypto v0.49.0 ) require ( @@ -19,6 +16,6 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - golang.org/x/sys v0.40.0 + golang.org/x/sys v0.42.0 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 23403af..ea1a47b 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= -github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= +github.com/consensys/gnark-crypto v0.20.1 h1:PXDUBvk8AzhvWowHLWBEAfUQcV1/aZgWIqD6eMpXmDg= +github.com/consensys/gnark-crypto v0.20.1/go.mod h1:RBWrSgy+IDbGR69RRV313th3M/aZU1ubk2om+qHuTSc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,19 +15,17 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/marshaler.go b/marshaler.go index 9dadae7..edf9852 100644 --- a/marshaler.go +++ b/marshaler.go @@ -9,7 +9,7 @@ package math import "encoding/json" type curveElement struct { - CurveID CurveID `json:"curve" validate:"required"` + CurveID CurveID `json:"curve" validate:"required"` ElementBytes []byte `json:"element" validate:"required"` } @@ -47,6 +47,7 @@ func (g *G1) UnmarshalJSON(raw []byte) error { } g.g1 = g1.g1 + return nil } @@ -71,6 +72,7 @@ func (g *G2) UnmarshalJSON(raw []byte) error { } g.g2 = g2.g2 + return nil } @@ -95,6 +97,7 @@ func (g *Gt) UnmarshalJSON(raw []byte) error { } g.gt = gt.gt + return nil } diff --git a/math.go b/math.go index 6732830..8489e80 100644 --- a/math.go +++ b/math.go @@ -4,35 +4,107 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ +// Package math provides a high-level interface for pairing-based cryptography operations +// on elliptic curves. It supports multiple pairing-friendly curves including BN254, BLS12-381, +// BLS12-377, and FP256BN variants. +// +// # Overview +// +// This package implements operations on three main groups used in pairing-based cryptography: +// - G1: Points on the first elliptic curve group +// - G2: Points on the second elliptic curve group (twisted curve) +// - Gt: Elements in the target group (result of pairing operations) +// - Zr: Scalars in the field (integers modulo the curve order) +// +// The library uses a driver pattern to support multiple backend implementations (AMCL, Gurvy, Kilic), +// allowing users to choose the best performance/compatibility trade-off for their use case. +// +// # Basic Usage +// +// Select a curve and perform operations: +// +// curve := math.Curves[math.BLS12_381] +// rng, _ := curve.Rand() +// scalar := curve.NewRandomZr(rng) +// point := curve.GenG1.Mul(scalar) +// result := curve.Pairing(curve.GenG2, point) +// +// # Supported Curves +// +// The package provides pre-configured curves accessible via the Curves slice: +// - FP256BN_AMCL: 256-bit Barreto-Naehrig curve (AMCL backend) +// - BN254: 254-bit Barreto-Naehrig curve (Gurvy backend) +// - FP256BN_AMCL_MIRACL: 256-bit BN curve MIRACL variant (AMCL backend) +// - BLS12_381: BLS12-381 curve (Kilic backend) +// - BLS12_377_GURVY: BLS12-377 curve (Gurvy backend) +// - BLS12_381_GURVY: BLS12-381 curve (Gurvy backend) +// - BLS12_381_BBS: BLS12-381 optimized for BBS+ signatures (Kilic backend) +// - BLS12_381_BBS_GURVY: BLS12-381 for BBS+ (Gurvy backend) +// +// # Thread Safety +// +// The types in this package are not thread-safe. Users should implement their own +// synchronization when sharing instances across goroutines. package math import ( "bytes" "encoding/binary" + "errors" "fmt" "io" + "math/big" "github.com/IBM/mathlib/driver" "github.com/IBM/mathlib/driver/amcl" "github.com/IBM/mathlib/driver/gurvy" "github.com/IBM/mathlib/driver/gurvy/bls12381" "github.com/IBM/mathlib/driver/kilic" - "github.com/pkg/errors" ) +// CurveID identifies a specific elliptic curve configuration and its backend implementation. +// Each curve ID represents a unique combination of curve parameters and the underlying +// cryptographic library used for operations. type CurveID int const ( + // FP256BN_AMCL represents a 256-bit Barreto-Naehrig curve using the AMCL backend. + // Suitable for general-purpose pairing operations with good performance. FP256BN_AMCL CurveID = iota + + // BN254 represents a 254-bit Barreto-Naehrig curve using the Gurvy backend. + // Offers high performance but has tighter security margins than BLS12-381. BN254 + + // FP256BN_AMCL_MIRACL represents a 256-bit BN curve MIRACL variant using AMCL. + // Provided for legacy compatibility with MIRACL-based systems. FP256BN_AMCL_MIRACL + + // BLS12_381 represents the BLS12-381 curve using the Kilic backend. + // Recommended for new projects due to excellent security margins and wide adoption. + // Suitable for BLS signatures and modern cryptographic protocols. BLS12_381 + + // BLS12_377_GURVY represents the BLS12-377 curve using the Gurvy backend. + // Optimized for recursive proof composition in zk-SNARK systems. BLS12_377_GURVY + + // BLS12_381_GURVY represents the BLS12-381 curve using the Gurvy backend. + // Performance-optimized implementation of BLS12-381 with assembly optimizations. BLS12_381_GURVY + + // BLS12_381_BBS is equivalent to BLS12_381 up to HashToG1 and HashToG2. + // Those functions follow the rules of the standard draft. BLS12_381_BBS + + // BLS12_381_BBS_GURVY is equivalent to BLS12_381_GURVY up to HashToG1 and HashToG2. + // Those functions follow the rules of the standard draft. BLS12_381_BBS_GURVY ) +// CurveIDToString converts a CurveID to its string representation. +// Returns a human-readable name for the curve, useful for logging and debugging. +// Panics if the curve ID is unknown. func CurveIDToString(id CurveID) string { switch id { case FP256BN_AMCL: @@ -56,8 +128,17 @@ func CurveIDToString(id CurveID) string { } } -// Curves defines the default available curves. -// They are instantiated either via the NewCurve function or directly. +// Curves provides pre-configured instances of all supported elliptic curves. +// Each curve is fully initialized and ready to use. Access curves by their index +// using the CurveID constants (e.g., Curves[BLS12_381]). +// +// Example: +// +// curve := math.Curves[math.BLS12_381] +// point := curve.GenG1.Mul(curve.NewZrFromInt(42)) +// +// The curves are instantiated at package initialization and can be used directly +// or via the NewCurve function for custom configurations. var Curves []*Curve = []*Curve{ NewCurve( amcl.NewFp256bn(), @@ -175,68 +256,116 @@ var Curves []*Curve = []*Curve{ /*********************************************************************/ +// Zr represents an element in the scalar field of an elliptic curve. +// These are integers modulo the curve's group order, used for scalar multiplication +// and other arithmetic operations in pairing-based cryptography. +// +// Zr elements support standard arithmetic operations (addition, subtraction, +// multiplication) as well as modular operations and conversions to/from various +// numeric types. +// +// Example: +// +// curve := math.Curves[math.BLS12_381] +// a := curve.NewZrFromInt(5) +// b := curve.NewZrFromInt(7) +// c := a.Plus(b) // c = 12 (mod curve order) type Zr struct { zr driver.Zr curveID CurveID } -// NewZr return a new Zr for the given arguments +// NewZr creates a new Zr element from a driver.Zr implementation and curve ID. +// This is typically used internally; users should use Curve methods like +// NewZrFromInt, NewZrFromBytes, or NewRandomZr instead. func NewZr(zr driver.Zr, curveID CurveID) *Zr { return &Zr{zr: zr, curveID: curveID} } +// IsZero returns true if this scalar is zero. +func (z *Zr) IsZero() bool { + return z.zr.IsZero() +} + +// IsOne returns true if this scalar is one. +func (z *Zr) IsOne() bool { + return z.zr.IsOne() +} + +// BigInt returns the scalar as a *big.Int. +// BigInt assumes that its output will not be altered by the caller. +// It responsibility of the caller to clone the output of BigInt if needed. +func (z *Zr) BigInt() *big.Int { + return z.zr.BigInt() +} + +// CurveID returns the curve identifier for this scalar. func (z *Zr) CurveID() CurveID { return z.curveID } +// Plus returns a new Zr representing (z + a) mod order. func (z *Zr) Plus(a *Zr) *Zr { return &Zr{zr: z.zr.Plus(a.zr), curveID: z.curveID} } +// Minus returns a new Zr representing (z - a) mod order. func (z *Zr) Minus(a *Zr) *Zr { return &Zr{zr: z.zr.Minus(a.zr), curveID: z.curveID} } +// Mul returns a new Zr representing (z * a) mod order. func (z *Zr) Mul(a *Zr) *Zr { return &Zr{zr: z.zr.Mul(a.zr), curveID: z.curveID} } +// Mod sets z to z mod a in place. func (z *Zr) Mod(a *Zr) { z.zr.Mod(a.zr) } +// PowMod returns a new Zr representing z^a mod order. func (z *Zr) PowMod(a *Zr) *Zr { return &Zr{zr: z.zr.PowMod(a.zr), curveID: z.curveID} } +// InvModP sets z to its modular inverse modulo a in place. func (z *Zr) InvModP(a *Zr) { z.zr.InvModP(a.zr) } +// InvModOrder sets z to its modular inverse modulo the curve order in place. func (z *Zr) InvModOrder() { z.zr.InvModOrder() } +// Bytes returns the byte representation of this scalar. +// The format is backend-specific but typically big-endian. func (z *Zr) Bytes() []byte { return z.zr.Bytes() } +// Equals returns true if z and a represent the same scalar value. func (z *Zr) Equals(a *Zr) bool { return z.zr.Equals(a.zr) } +// Copy returns a deep copy of this scalar. func (z *Zr) Copy() *Zr { return &Zr{zr: z.zr.Copy(), curveID: z.curveID} } +// Clone copies the value of a into z. func (z *Zr) Clone(a *Zr) { z.zr.Clone(a.zr) } +// String returns a string representation of this scalar. func (z *Zr) String() string { return z.zr.String() } +// Neg negates z in place (z = -z mod order). func (z *Zr) Neg() { z.zr.Neg() } @@ -244,210 +373,336 @@ func (z *Zr) Neg() { var zerobytes = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var onebytes = []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} +// Uint converts the scalar to a uint64. +// Returns an error if the value is out of the uint64 range. func (z *Zr) Uint() (uint64, error) { b := z.Bytes() if !bytes.Equal(zerobytes, b[:32-8]) && !bytes.Equal(onebytes, b[:32-8]) { - return 0, fmt.Errorf("out of range") + return 0, errors.New("out of range") } - return uint64(binary.BigEndian.Uint64(b[32-8:])), nil + return binary.BigEndian.Uint64(b[32-8:]), nil } +// Int converts the scalar to an int64. +// Returns an error if the value is out of the int64 range. func (z *Zr) Int() (int64, error) { b := z.Bytes() if !bytes.Equal(zerobytes, b[:32-8]) && !bytes.Equal(onebytes, b[:32-8]) { - return 0, fmt.Errorf("out of range") + return 0, errors.New("out of range") } - return int64(binary.BigEndian.Uint64(b[32-8:])), nil + u := binary.BigEndian.Uint64(b[32-8:]) + + return int64(u), nil // #nosec G115 } /*********************************************************************/ +// G1 represents a point on the first elliptic curve group in pairing-based cryptography. +// G1 is typically the "smaller" group in terms of representation size and is used for +// signatures, commitments, and as the first argument in pairing operations. +// +// G1 points support group operations (addition, scalar multiplication) and can be +// serialized in both compressed and uncompressed formats. +// +// Example: +// +// curve := math.Curves[math.BLS12_381] +// scalar := curve.NewZrFromInt(42) +// point := curve.GenG1.Mul(scalar) // [42]G1 +// point.Add(curve.GenG1) // [43]G1 type G1 struct { g1 driver.G1 curveID CurveID } -// NewG1 return a new G1 for the given arguments +// NewG1 creates a new G1 point from a driver.G1 implementation and curve ID. +// This is typically used internally; users should use Curve methods like +// NewG1, NewG1FromBytes, or HashToG1 instead. func NewG1(g1 driver.G1, curveID CurveID) *G1 { return &G1{g1: g1, curveID: curveID} } +// CurveID returns the curve identifier for this G1 point. func (g *G1) CurveID() CurveID { return g.curveID } +// Clone copies the value of a into g. func (g *G1) Clone(a *G1) { g.g1.Clone(a.g1) } +// Copy returns a deep copy of this G1 point. func (g *G1) Copy() *G1 { return &G1{g1: g.g1.Copy(), curveID: g.curveID} } +// Add adds point a to g in place (g = g + a). func (g *G1) Add(a *G1) { g.g1.Add(a.g1) } +// Mul returns a new G1 point representing scalar multiplication [a]g. func (g *G1) Mul(a *Zr) *G1 { return &G1{g1: g.g1.Mul(a.zr), curveID: g.curveID} } +// Mul2 computes [e]g + [f]Q and returns the result as a new G1 point. +// This is more efficient than computing the two scalar multiplications separately. func (g *G1) Mul2(e *Zr, Q *G1, f *Zr) *G1 { return &G1{g1: g.g1.Mul2(e.zr, Q.g1, f.zr), curveID: g.curveID} } +// Mul2InPlace computes [e]g + [f]Q and stores the result in g. +// This is more efficient than Mul2 when the result can overwrite g. func (g *G1) Mul2InPlace(e *Zr, Q *G1, f *Zr) { g.g1.Mul2InPlace(e.zr, Q.g1, f.zr) } +// Equals returns true if g and a represent the same point. func (g *G1) Equals(a *G1) bool { return g.g1.Equals(a.g1) } +// Bytes returns the uncompressed byte representation of this G1 point. +// The format is backend-specific but typically includes both coordinates. func (g *G1) Bytes() []byte { return g.g1.Bytes() } +// Compressed returns the compressed byte representation of this G1 point. +// Compressed format uses roughly half the space of uncompressed format. func (g *G1) Compressed() []byte { return g.g1.Compressed() } +// Sub subtracts point a from g in place (g = g - a). func (g *G1) Sub(a *G1) { g.g1.Sub(a.g1) } +// IsInfinity returns true if this point is the point at infinity (identity element). func (g *G1) IsInfinity() bool { return g.g1.IsInfinity() } +// String returns a string representation of this G1 point. func (g *G1) String() string { return g.g1.String() } +// Neg negates g in place (g = -g). func (g *G1) Neg() { g.g1.Neg() } /*********************************************************************/ +// G2 represents a point on the second elliptic curve group in pairing-based cryptography. +// G2 is typically the "larger" group (on a twisted curve) and is used as the second +// argument in pairing operations. In many protocols, public keys reside in G2 while +// signatures are in G1. +// +// G2 points support group operations (addition, scalar multiplication) and can be +// serialized in both compressed and uncompressed formats. +// +// Example: +// +// curve := math.Curves[math.BLS12_381] +// scalar := curve.NewZrFromInt(42) +// point := curve.GenG2.Mul(scalar) // [42]G2 type G2 struct { g2 driver.G2 curveID CurveID } -// NewG2 return a new G2 for the given arguments +// NewG2 creates a new G2 point from a driver.G2 implementation and curve ID. +// This is typically used internally; users should use Curve methods like +// NewG2, NewG2FromBytes, or HashToG2 instead. func NewG2(g2 driver.G2, curveID CurveID) *G2 { return &G2{g2: g2, curveID: curveID} } +// CurveID returns the curve identifier for this G2 point. func (g *G2) CurveID() CurveID { return g.curveID } +// Clone copies the value of a into g. func (g *G2) Clone(a *G2) { g.g2.Clone(a.g2) } +// Copy returns a deep copy of this G2 point. func (g *G2) Copy() *G2 { return &G2{g2: g.g2.Copy(), curveID: g.curveID} } +// Mul returns a new G2 point representing scalar multiplication [a]g. func (g *G2) Mul(a *Zr) *G2 { return &G2{g2: g.g2.Mul(a.zr), curveID: g.curveID} } +// Add adds point a to g in place (g = g + a). func (g *G2) Add(a *G2) { g.g2.Add(a.g2) } +// Sub subtracts point a from g in place (g = g - a). func (g *G2) Sub(a *G2) { g.g2.Sub(a.g2) } +// Affine converts g to affine coordinates in place. +// This may be required by some operations or for serialization. func (g *G2) Affine() { g.g2.Affine() } +// Bytes returns the uncompressed byte representation of this G2 point. +// The format is backend-specific but typically includes all coordinates. func (g *G2) Bytes() []byte { return g.g2.Bytes() } +// Compressed returns the compressed byte representation of this G2 point. +// Compressed format uses roughly half the space of uncompressed format. func (g *G2) Compressed() []byte { return g.g2.Compressed() } +// String returns a string representation of this G2 point. func (g *G2) String() string { return g.g2.String() } +// Equals returns true if g and a represent the same point. func (g *G2) Equals(a *G2) bool { return g.g2.Equals(a.g2) } /*********************************************************************/ +// Gt represents an element in the target group of a pairing operation. +// Gt is the multiplicative group that results from pairing G1 and G2 elements. +// In pairing-based cryptography, the pairing function e: G2 × G1 → Gt has the +// bilinearity property: e([a]G2, [b]G1) = e(G2, G1)^(ab). +// +// Gt elements support multiplication, exponentiation, and inversion operations. +// +// Example: +// +// curve := math.Curves[math.BLS12_381] +// gt1 := curve.Pairing(curve.GenG2, curve.GenG1) +// scalar := curve.NewZrFromInt(5) +// gt2 := gt1.Exp(scalar) // gt1^5 type Gt struct { gt driver.Gt curveID CurveID } -// NewGt return a new Gt for the given arguments +// NewGt creates a new Gt element from a driver.Gt implementation and curve ID. +// This is typically used internally; users should use Curve.Pairing or +// Curve.NewGtFromBytes instead. func NewGt(gt driver.Gt, curveID CurveID) *Gt { return &Gt{gt: gt, curveID: curveID} } +// CurveID returns the curve identifier for this Gt element. func (g *Gt) CurveID() CurveID { return g.curveID } +// Equals returns true if g and a represent the same target group element. func (g *Gt) Equals(a *Gt) bool { return g.gt.Equals(a.gt) } +// Inverse computes the multiplicative inverse of g in place (g = g^-1). func (g *Gt) Inverse() { g.gt.Inverse() } +// Mul multiplies g by a in place (g = g * a). func (g *Gt) Mul(a *Gt) { g.gt.Mul(a.gt) } +// Exp returns a new Gt element representing g^z (exponentiation). func (g *Gt) Exp(z *Zr) *Gt { return &Gt{gt: g.gt.Exp(z.zr), curveID: g.curveID} } +// IsUnity returns true if g is the identity element (unity) in Gt. func (g *Gt) IsUnity() bool { return g.gt.IsUnity() } +// String returns a string representation of this Gt element. func (g *Gt) String() string { return g.gt.ToString() } +// Bytes returns the byte representation of this Gt element. +// The format is backend-specific. func (g *Gt) Bytes() []byte { return g.gt.Bytes() } /*********************************************************************/ +// Curve represents a pairing-friendly elliptic curve and provides the main interface +// for cryptographic operations. It encapsulates the curve parameters, generator points, +// and factory methods for creating group elements. +// +// A Curve instance provides: +// - Generator points (GenG1, GenG2, GenGt) for each group +// - The group order (GroupOrder) as a Zr element +// - Size information for serialization +// - Factory methods for creating and deserializing group elements +// - Pairing operations +// - Hash-to-curve operations +// - Modular arithmetic operations +// +// Example: +// +// curve := math.Curves[math.BLS12_381] +// rng, _ := curve.Rand() +// secretKey := curve.NewRandomZr(rng) +// publicKey := curve.GenG1.Mul(secretKey) +// message := []byte("sign this") +// signature := curve.HashToG1(message).Mul(secretKey) type Curve struct { c driver.Curve - GenG1 *G1 - GenG2 *G2 - GenGt *Gt - GroupOrder *Zr - CoordByteSize int - G1ByteSize int - CompressedG1ByteSize int - G2ByteSize int - CompressedG2ByteSize int - ScalarByteSize int + GenG1 *G1 // Generator point for the G1 group + GenG2 *G2 // Generator point for the G2 group + GenGt *Gt // Generator (identity) element for the Gt group + GroupOrder *Zr // Order of the curve groups + CoordByteSize int // Size of a single coordinate in bytes + G1ByteSize int // Size of uncompressed G1 point in bytes + CompressedG1ByteSize int // Size of compressed G1 point in bytes + G2ByteSize int // Size of uncompressed G2 point in bytes + CompressedG2ByteSize int // Size of compressed G2 point in bytes + ScalarByteSize int // Size of scalar (Zr) in bytes curveID CurveID } -// NewCurve returns a new instance of Curve for the given arguments +// NewCurve creates a new Curve instance with the specified parameters. +// This is typically used internally during package initialization to create +// the pre-configured curves in the Curves slice. Most users should use +// the pre-configured curves rather than creating custom instances. +// +// Parameters: +// - c: The underlying driver implementation +// - genG1, genG2, genGt: Generator points for each group +// - groupOrder: The order of the curve groups +// - coordByteSize: Size of a coordinate in bytes +// - g1ByteSize, compressedG1ByteSize: Sizes for G1 serialization +// - g2ByteSize, compressedG2ByteSize: Sizes for G2 serialization +// - scalarByteSize: Size of scalars in bytes +// - curveID: Identifier for this curve configuration func NewCurve( c driver.Curve, genG1 *G1, @@ -478,166 +733,237 @@ func NewCurve( } } +// ID returns the curve identifier for this curve. func (c *Curve) ID() CurveID { return c.curveID } +// Rand returns a cryptographically secure random number generator. +// Returns an error if the RNG cannot be initialized. func (c *Curve) Rand() (io.Reader, error) { return c.c.Rand() } +// NewRandomZr generates a random scalar using the provided random number generator. +// The scalar is uniformly distributed in the range [0, group order). func (c *Curve) NewRandomZr(rng io.Reader) *Zr { return &Zr{zr: c.c.NewRandomZr(rng), curveID: c.curveID} } +// NewZrFromBytes creates a Zr scalar from its byte representation. +// The byte slice should be in the format produced by Zr.Bytes(). func (c *Curve) NewZrFromBytes(b []byte) *Zr { return &Zr{zr: c.c.NewZrFromBytes(b), curveID: c.curveID} } +// NewG1FromBytes deserializes a G1 point from its uncompressed byte representation. +// Returns an error if the bytes are invalid or don't represent a valid point. func (c *Curve) NewG1FromBytes(b []byte) (p *G1, err error) { defer func() { if r := recover(); r != nil { - err = errors.Errorf("failure [%s]", r) + err = fmt.Errorf("failure [%s]", r) p = nil } }() p = &G1{g1: c.c.NewG1FromBytes(b), curveID: c.curveID} + return } +// NewG2FromBytes deserializes a G2 point from its uncompressed byte representation. +// Returns an error if the bytes are invalid or don't represent a valid point. func (c *Curve) NewG2FromBytes(b []byte) (p *G2, err error) { defer func() { if r := recover(); r != nil { - err = errors.Errorf("failure [%s]", r) + err = fmt.Errorf("failure [%s]", r) p = nil } }() p = &G2{g2: c.c.NewG2FromBytes(b), curveID: c.curveID} + return } +// NewG1FromCompressed deserializes a G1 point from its compressed byte representation. +// Returns an error if the bytes are invalid or don't represent a valid point. func (c *Curve) NewG1FromCompressed(b []byte) (p *G1, err error) { defer func() { if r := recover(); r != nil { - err = errors.Errorf("failure [%s]", r) + err = fmt.Errorf("failure [%s]", r) p = nil } }() p = &G1{g1: c.c.NewG1FromCompressed(b), curveID: c.curveID} + return } +// NewG2FromCompressed deserializes a G2 point from its compressed byte representation. +// Returns an error if the bytes are invalid or don't represent a valid point. func (c *Curve) NewG2FromCompressed(b []byte) (p *G2, err error) { defer func() { if r := recover(); r != nil { - err = errors.Errorf("failure [%s]", r) + err = fmt.Errorf("failure [%s]", r) p = nil } }() p = &G2{g2: c.c.NewG2FromCompressed(b), curveID: c.curveID} + return } +// NewGtFromBytes deserializes a Gt element from its byte representation. +// Returns an error if the bytes are invalid or don't represent a valid element. func (c *Curve) NewGtFromBytes(b []byte) (p *Gt, err error) { defer func() { if r := recover(); r != nil { - err = errors.Errorf("failure [%s]", r) + err = fmt.Errorf("failure [%s]", r) p = nil } }() p = &Gt{gt: c.c.NewGtFromBytes(b), curveID: c.curveID} + return } +// NewZrFromInt creates a Zr scalar from an int64 value. func (c *Curve) NewZrFromInt(i int64) *Zr { return &Zr{zr: c.c.NewZrFromInt64(i), curveID: c.curveID} } +// NewZrFromUint64 creates a Zr scalar from a uint64 value. func (c *Curve) NewZrFromUint64(i uint64) *Zr { return &Zr{zr: c.c.NewZrFromUint64(i), curveID: c.curveID} } +// NewZrFromBigInt creates a Zr scalar from a *big.Int value. +// The value is reduced modulo the curve order. +func (c *Curve) NewZrFromBigInt(i *big.Int) *Zr { + return &Zr{zr: c.c.NewZrFromBigInt(i), curveID: c.curveID} +} + +// NewG2 creates a new G2 point initialized to the identity element (point at infinity). func (c *Curve) NewG2() *G2 { return &G2{g2: c.c.NewG2(), curveID: c.curveID} } +// NewG1 creates a new G1 point initialized to the identity element (point at infinity). func (c *Curve) NewG1() *G1 { return &G1{g1: c.c.NewG1(), curveID: c.curveID} } +// Pairing computes the bilinear pairing e(a, b) where a ∈ G2 and b ∈ G1. +// The result is an element in the target group Gt. +// The pairing satisfies the bilinearity property: e([x]a, [y]b) = e(a, b)^(xy). func (c *Curve) Pairing(a *G2, b *G1) *Gt { return &Gt{gt: c.c.Pairing(a.g2, b.g1), curveID: c.curveID} } +// Pairing2 efficiently computes e(p, q) * e(r, s) where p, r ∈ G2 and q, s ∈ G1. +// This is more efficient than computing two separate pairings and multiplying them. func (c *Curve) Pairing2(p *G2, q *G1, r *G2, s *G1) *Gt { return &Gt{gt: c.c.Pairing2(p.g2, r.g2, q.g1, s.g1), curveID: c.curveID} } +// FExp performs the final exponentiation in the pairing computation. +// This is typically used internally but exposed for advanced use cases. func (c *Curve) FExp(a *Gt) *Gt { return &Gt{gt: c.c.FExp(a.gt), curveID: c.curveID} } +// HashToZr hashes arbitrary data to a scalar in Zr using a cryptographic hash function. +// The output is uniformly distributed in the scalar field. func (c *Curve) HashToZr(data []byte) *Zr { return &Zr{zr: c.c.HashToZr(data), curveID: c.curveID} } +// HashToG1 hashes arbitrary data to a point in G1 using a hash-to-curve algorithm. +// This is useful for creating deterministic points from messages. func (c *Curve) HashToG1(data []byte) *G1 { return &G1{g1: c.c.HashToG1(data), curveID: c.curveID} } +// HashToG1WithDomain hashes data to a G1 point with domain separation. +// The domain parameter prevents hash collisions across different protocols or contexts. func (c *Curve) HashToG1WithDomain(data, domain []byte) *G1 { return &G1{g1: c.c.HashToG1WithDomain(data, domain), curveID: c.curveID} } +// HashToG2 hashes arbitrary data to a point in G2 using a hash-to-curve algorithm. func (c *Curve) HashToG2(data []byte) *G2 { return &G2{g2: c.c.HashToG2(data), curveID: c.curveID} } +// HashToG2WithDomain hashes data to a G2 point with domain separation. +// The domain parameter prevents hash collisions across different protocols or contexts. func (c *Curve) HashToG2WithDomain(data, domain []byte) *G2 { return &G2{g2: c.c.HashToG2WithDomain(data, domain), curveID: c.curveID} } +// ModSub computes (a - b) mod m. func (c *Curve) ModSub(a, b, m *Zr) *Zr { return &Zr{zr: c.c.ModSub(a.zr, b.zr, m.zr), curveID: c.curveID} } +// ModAdd computes (a + b) mod m. func (c *Curve) ModAdd(a, b, m *Zr) *Zr { return &Zr{zr: c.c.ModAdd(a.zr, b.zr, m.zr), curveID: c.curveID} } +// ModMul computes (a1 * b1) mod m. func (c *Curve) ModMul(a1, b1, m *Zr) *Zr { return &Zr{zr: c.c.ModMul(a1.zr, b1.zr, m.zr), curveID: c.curveID} } +// ModNeg computes (-a1) mod m. func (c *Curve) ModNeg(a1, m *Zr) *Zr { return &Zr{zr: c.c.ModNeg(a1.zr, m.zr), curveID: c.curveID} } +// ModAddMul computes the sum of products: (a1[0]*b1[0] + a1[1]*b1[1] + ... + a1[n]*b1[n]) mod m. +// This is more efficient than computing each product separately. +// The slices a1 and b1 must have the same length. func (c *Curve) ModAddMul(a1, b1 []*Zr, m *Zr) *Zr { a1Driver := make([]driver.Zr, len(a1)) b1Driver := make([]driver.Zr, len(b1)) - for i := 0; i < len(a1); i++ { + for i := range a1 { a1Driver[i] = a1[i].zr b1Driver[i] = b1[i].zr } + return &Zr{zr: c.c.ModAddMul(a1Driver, b1Driver, m.zr), curveID: c.curveID} } -func (c *Curve) ModAddMul2(a, b, cc, d *Zr, m *Zr) *Zr { - return &Zr{zr: c.c.ModAddMul2(a.zr, b.zr, cc.zr, d.zr, m.zr), curveID: c.curveID} +// ModAddMul2 computes (a1*a2 + b1*b2) mod m. +// This is more efficient than computing the products separately. +func (c *Curve) ModAddMul2(a1, a2, b1, b2 *Zr, m *Zr) *Zr { + return &Zr{zr: c.c.ModAddMul2(a1.zr, a2.zr, b1.zr, b2.zr, m.zr), curveID: c.curveID} +} + +// ModAddMul3 computes (a1*a2 + b1*b2 + c1*c2) mod m. +// This is more efficient than computing the products separately. +func (c *Curve) ModAddMul3(a1, a2, b1, b2, c1, c2 *Zr, m *Zr) *Zr { + return &Zr{ + zr: c.c.ModAddMul3(a1.zr, a2.zr, b1.zr, b2.zr, c1.zr, c2.zr, m.zr), + curveID: c.curveID, + } } +// MultiScalarMul computes a multi-scalar multiplication: [b[0]]a[0] + [b[1]]a[1] + ... + [b[n]]a[n]. +// This is significantly more efficient than computing each scalar multiplication separately. +// The slices a and b must have the same length. func (c *Curve) MultiScalarMul(a []*G1, b []*Zr) *G1 { aDriver := make([]driver.G1, len(a)) bDriver := make([]driver.Zr, len(b)) - for i := 0; i < len(a); i++ { + for i := range a { aDriver[i] = a[i].g1 bDriver[i] = b[i].zr } + return &G1{g1: c.c.MultiScalarMul(aDriver, bDriver), curveID: c.curveID} } diff --git a/math_test.go b/math_test.go index 317391c..aab51de 100644 --- a/math_test.go +++ b/math_test.go @@ -7,11 +7,14 @@ SPDX-License-Identifier: Apache-2.0 package math import ( + "crypto/rand" "encoding/json" "fmt" "io" "math" - "math/rand" + "math/big" + mathrand "math/rand" + "strconv" "testing" "time" @@ -24,7 +27,7 @@ var seed = time.Now().Unix() func TestImmutability(t *testing.T) { for _, curve := range Curves { rng, err := curve.Rand() - assert.NoError(t, err) + require.NoError(t, err) testImmutabilityZr(t, curve, rng) testImmutabilityG1(t, curve, rng) @@ -36,14 +39,15 @@ func TestImmutability(t *testing.T) { func TestCurveId(t *testing.T) { for _, curve := range Curves { rng, err := curve.Rand() - assert.NoError(t, err) + require.NoError(t, err) runCurveIdTest(t, curve, rng) } } func runCurveIdTest(t *testing.T, c *Curve, rng io.Reader) { - r := c.NewRandomZr(rng) + t.Helper() + r = c.NewRandomZr(rng) assert.Equal(t, r.CurveID(), c.ID()) assert.Equal(t, c.GenG1.Mul(r).CurveID(), c.ID()) @@ -57,9 +61,10 @@ var g2 *G2 var gt *Gt func testImmutabilityGt(t *testing.T, c *Curve, rng io.Reader) { + t.Helper() g := c.GenGt.Exp(c.NewRandomZr(rng)) orig, err := c.NewGtFromBytes(g.Bytes()) - assert.NoError(t, err) + require.NoError(t, err) // Exp(Zr) Gt gt = g.Exp(c.NewRandomZr(rng)) @@ -67,6 +72,7 @@ func testImmutabilityGt(t *testing.T, c *Curve, rng io.Reader) { } func testImmutabilityG2(t *testing.T, c *Curve, rng io.Reader) { + t.Helper() g := c.GenG2.Mul(c.NewRandomZr(rng)) orig := g.Copy() @@ -80,6 +86,7 @@ func testImmutabilityG2(t *testing.T, c *Curve, rng io.Reader) { } func testImmutabilityG1(t *testing.T, c *Curve, rng io.Reader) { + t.Helper() g := c.GenG1.Mul(c.NewRandomZr(rng)) orig := g.Copy() @@ -97,6 +104,7 @@ func testImmutabilityG1(t *testing.T, c *Curve, rng io.Reader) { } func testImmutabilityZr(t *testing.T, c *Curve, rng io.Reader) { + t.Helper() _r := c.NewRandomZr(rng) orig := _r.Copy() @@ -122,40 +130,47 @@ func testImmutabilityZr(t *testing.T, c *Curve, rng io.Reader) { } func runZrTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) maxint64 := c.NewZrFromInt(math.MaxInt64) + maxint64FromBigInt := c.NewZrFromBigInt(big.NewInt(math.MaxInt64)) + assert.True(t, maxint64.Equals(maxint64FromBigInt)) + assert.Zero(t, maxint64FromBigInt.BigInt().Cmp(big.NewInt(math.MaxInt64))) + maxuint64 := c.NewZrFromUint64(math.MaxUint64) iu, err := maxint64.Uint() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, uint64(math.MaxInt64), iu) i64, err := maxint64.Int() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, int64(math.MaxInt64), i64) ui, err := maxuint64.Int() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, int64(-1), ui) u64, err := maxuint64.Uint() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, uint64(math.MaxUint64), u64) - a, b := rand.Int63(), rand.Int63() + testRng := mathrand.New(mathrand.NewSource(seed)) + a, b := testRng.Int63(), testRng.Int63() cr, err := c.NewZrFromInt(a).Plus(c.NewZrFromInt(b)).Int() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, a+b, cr) - au, bu := uint64(rand.Int63()), uint64(rand.Int63()) + v1, v2 := testRng.Int63(), testRng.Int63() + au, bu := uint64(v1), uint64(v2) // #nosec G115 cru, err := c.NewZrFromUint64(au).Plus(c.NewZrFromUint64(bu)).Uint() - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, au+bu, cru) assert.Equal(t, fmt.Sprintf("%x", math.MaxInt64), maxint64.String()) - assert.Equal(t, fmt.Sprintf("%x", uint64(math.MaxUint64)), maxuint64.String()) + assert.Equal(t, strconv.FormatUint(uint64(math.MaxUint64), 16), maxuint64.String()) // serialising and deserialising negative numbers rr := c.NewRandomZr(rng) @@ -164,7 +179,7 @@ func runZrTest(t *testing.T, c *Curve) { rr1b := rr1.Bytes() rr11 := c.NewZrFromBytes(rr1b) res := c.ModAdd(rr, rr11, c.GroupOrder) - assert.True(t, res.Equals(c.NewZrFromInt(0)), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, res.Equals(c.NewZrFromInt(0)), "failed with curve %T", c.c) assert.True(t, c.NewZrFromInt(35).Plus(c.NewZrFromInt(1)).Equals(c.NewZrFromInt(36))) assert.True(t, c.NewZrFromInt(36).Copy().Equals(c.NewZrFromInt(36))) @@ -174,15 +189,13 @@ func runZrTest(t *testing.T, c *Curve) { i = c.NewZrFromInt(3) i.InvModP(c.NewZrFromInt(11)) assert.True(t, i.Equals(c.NewZrFromInt(4))) - assert.Equal(t, c.NewZrFromInt(35).String(), "23") + assert.Equal(t, "23", c.NewZrFromInt(35).String()) - rand.Seed(seed) - - i64 = rand.Int63() + i64 = testRng.Int63() i = c.NewZrFromInt(i64) - i64_, err := i.Int() - assert.NoError(t, err) - assert.Equal(t, i64, i64_) + i64Result, err := i.Int() + require.NoError(t, err) + assert.Equal(t, i64, i64Result) i1 := c.NewZrFromInt(i64) i64 = 0 - i64 @@ -194,7 +207,7 @@ func runZrTest(t *testing.T, c *Curve) { i = i.Plus(c.NewZrFromInt(math.MaxInt64)) i = i.Plus(c.NewZrFromInt(2)) _, err = i.Int() - assert.EqualError(t, err, "out of range") + require.EqualError(t, err, "out of range") // D/H r1 := c.NewRandomZr(rng) @@ -210,7 +223,7 @@ func runZrTest(t *testing.T, c *Curve) { i2 = c.ModNeg(i2, c.GroupOrder) i3 = i1.Plus(i2) i3.Mod(c.GroupOrder) - assert.True(t, i3.Equals(c.NewZrFromInt(0)), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, i3.Equals(c.NewZrFromInt(0)), "failed with curve %T", c.c) // large negative numbers with neg i1 = c.NewRandomZr(rng) @@ -218,17 +231,17 @@ func runZrTest(t *testing.T, c *Curve) { i2.Neg() i3 = i1.Plus(i2) i3.Mod(c.GroupOrder) - assert.True(t, i3.Equals(c.NewZrFromInt(0)), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, i3.Equals(c.NewZrFromInt(0)), "failed with curve %T", c.c) // large negative numbers with minus i1 = c.NewRandomZr(rng) i2 = i1.Copy() i3 = i1.Minus(i2) i3.Mod(c.GroupOrder) - assert.True(t, i3.Equals(c.NewZrFromInt(0)), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, i3.Equals(c.NewZrFromInt(0)), "failed with curve %T", c.c) // Euler's totient - assert.True(t, r1.PowMod(c.GroupOrder.Plus(c.NewZrFromInt(-1))).Equals(c.NewZrFromInt(1)), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, r1.PowMod(c.GroupOrder.Plus(c.NewZrFromInt(-1))).Equals(c.NewZrFromInt(1)), "failed with curve %T", c.c) // byte size assert.Len(t, r1.Bytes(), c.ScalarByteSize) @@ -257,9 +270,10 @@ var expectedModuli = []string{ } func runG1Test(t *testing.T, c *Curve) { + t.Helper() assert.Equal(t, expectedG1Gens[c.ID()], c.GenG1.String()) - assert.Equal(t, expectedModuli[c.ID()], c.GroupOrder.String(), fmt.Sprintf("failed with curve %T", c.c)) + assert.Equal(t, expectedModuli[c.ID()], c.GroupOrder.String(), "failed with curve %T", c.c) g1copy := c.NewG1() g1copy.Clone(c.GenG1) @@ -307,6 +321,7 @@ func runG1Test(t *testing.T, c *Curve) { } func runMultiScalarMul(t *testing.T, c *Curve) { + t.Helper() // choose random elements in G1 rng, err := c.Rand() require.NoError(t, err) @@ -331,6 +346,7 @@ func runMultiScalarMul(t *testing.T, c *Curve) { } func runG2Test(t *testing.T, c *Curve) { + t.Helper() g2copy := c.NewG2() g2copy.Clone(c.GenG2) assert.True(t, c.GenG2.Equals(g2copy)) @@ -342,7 +358,7 @@ func runG2Test(t *testing.T, c *Curve) { assert.True(t, g1.Equals(g3)) assert.True(t, g2.Equals(c.GenG2.Mul(c.NewZrFromInt(23)))) g1.Sub(g2) - assert.True(t, g1.Equals(c.GenG2.Mul(c.NewZrFromInt(35))), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, g1.Equals(c.GenG2.Mul(c.NewZrFromInt(35))), "failed with curve %T", c.c) g4 := c.GenG2.Mul(c.NewZrFromInt(35)) g5 := c.GenG2.Mul(c.NewZrFromInt(23)) @@ -355,7 +371,7 @@ func runG2Test(t *testing.T, c *Curve) { assert.True(t, g5.Equals(c.GenG2.Mul(c.NewZrFromInt(23)))) rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) a := c.NewRandomZr(rng) p := c.GenG2.Mul(a) @@ -372,8 +388,9 @@ func runG2Test(t *testing.T, c *Curve) { } func runPowTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) a := c.NewRandomZr(rng) b := c.NewRandomZr(rng) @@ -404,8 +421,9 @@ func runPowTest(t *testing.T, c *Curve) { } func runPairingTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r0 := c.NewRandomZr(rng) g1r := c.GenG1.Mul(r0) g2r := c.GenG2.Mul(r0) @@ -437,6 +455,7 @@ func runPairingTest(t *testing.T, c *Curve) { } func runGtTest(t *testing.T, c *Curve) { + t.Helper() r := c.NewZrFromInt(1541) g2r := c.GenG2.Mul(r) a := c.Pairing(g2r, c.GenG1) @@ -451,8 +470,9 @@ func runGtTest(t *testing.T, c *Curve) { } func runInvModOrderTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r := c.NewRandomZr(rng) r1 := c.NewZrFromUint64(0) r1.Clone(r) @@ -463,8 +483,9 @@ func runInvModOrderTest(t *testing.T, c *Curve) { } func runRndTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r := c.NewRandomZr(rng) gr := c.GenG1.Mul(r) @@ -474,8 +495,10 @@ func runRndTest(t *testing.T, c *Curve) { } func runHashTest(t *testing.T, c *Curve) { + t.Helper() bytes := make([]byte, 128) - rand.Read(bytes) + _, err := rand.Read(bytes) + require.NoError(t, err) r := c.HashToZr(bytes) gr := c.GenG1.Mul(r) @@ -486,87 +509,89 @@ func runHashTest(t *testing.T, c *Curve) { } func runToFroBytesTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r := c.NewRandomZr(rng) rbytes := r.Bytes() rback := c.NewZrFromBytes(rbytes) - assert.NoError(t, err) assert.True(t, r.Equals(rback)) g1r := c.GenG1.Mul(r) g1rbytes := g1r.Bytes() assert.Len(t, g1rbytes, c.G1ByteSize) g1rback, err := c.NewG1FromBytes(g1rbytes) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, g1r.Equals(g1rback)) - assert.Len(t, g1rback.Bytes(), c.G1ByteSize, fmt.Sprintf("failed with curve %T", c.c)) - assert.Len(t, g1rback.Compressed(), c.CompressedG1ByteSize, fmt.Sprintf("failed with curve %T", c.c)) + assert.Len(t, g1rback.Bytes(), c.G1ByteSize, "failed with curve %T", c.c) + assert.Len(t, g1rback.Compressed(), c.CompressedG1ByteSize, "failed with curve %T", c.c) g2r := c.GenG2.Mul(r) g2rbytes := g2r.Bytes() assert.Len(t, g2rbytes, c.G2ByteSize) g2rback, err := c.NewG2FromBytes(g2rbytes) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, g2r.Equals(g2rback)) - assert.Len(t, g2rback.Bytes(), c.G2ByteSize, fmt.Sprintf("failed with curve %T", c.c)) - assert.Len(t, g2rback.Compressed(), c.CompressedG2ByteSize, fmt.Sprintf("failed with curve %T", c.c)) + assert.Len(t, g2rback.Bytes(), c.G2ByteSize, "failed with curve %T", c.c) + assert.Len(t, g2rback.Compressed(), c.CompressedG2ByteSize, "failed with curve %T", c.c) g2r = c.GenG2.Mul(r) a := c.Pairing(g2r, c.GenG1) abytes := a.Bytes() aback, err := c.NewGtFromBytes(abytes) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, a.Equals(aback)) g1rback, err = c.NewG1FromBytes(nil) assert.Nil(t, g1rback) - assert.Error(t, err) + require.Error(t, err) g2rback, err = c.NewG2FromBytes(nil) assert.Nil(t, g2rback) - assert.Error(t, err) + require.Error(t, err) gtrback, err := c.NewGtFromBytes(nil) assert.Nil(t, gtrback) - assert.Error(t, err) + require.Error(t, err) } func runToFroCompressedTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r := c.NewRandomZr(rng) g1r := c.GenG1.Mul(r) g1rbytes := g1r.Compressed() assert.Len(t, g1rbytes, c.CompressedG1ByteSize) g1rback, err := c.NewG1FromCompressed(g1rbytes) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, g1r.Equals(g1rback)) - assert.Len(t, g1rback.Bytes(), c.G1ByteSize, fmt.Sprintf("failed with curve %T", c.c)) - assert.Len(t, g1rback.Compressed(), c.CompressedG1ByteSize, fmt.Sprintf("failed with curve %T", c.c)) + assert.Len(t, g1rback.Bytes(), c.G1ByteSize, "failed with curve %T", c.c) + assert.Len(t, g1rback.Compressed(), c.CompressedG1ByteSize, "failed with curve %T", c.c) g2r := c.GenG2.Mul(r) g2rbytes := g2r.Compressed() assert.Len(t, g2rbytes, c.CompressedG2ByteSize) g2rback, err := c.NewG2FromCompressed(g2rbytes) - assert.NoError(t, err) + require.NoError(t, err) assert.True(t, g2r.Equals(g2rback)) - assert.Len(t, g2rback.Bytes(), c.G2ByteSize, fmt.Sprintf("failed with curve %T", c.c)) - assert.Len(t, g2rback.Compressed(), c.CompressedG2ByteSize, fmt.Sprintf("failed with curve %T", c.c)) + assert.Len(t, g2rback.Bytes(), c.G2ByteSize, "failed with curve %T", c.c) + assert.Len(t, g2rback.Compressed(), c.CompressedG2ByteSize, "failed with curve %T", c.c) g1rback, err = c.NewG1FromCompressed(nil) assert.Nil(t, g1rback) - assert.Error(t, err) + require.Error(t, err) g2rback, err = c.NewG2FromCompressed(nil) assert.Nil(t, g2rback) - assert.Error(t, err) + require.Error(t, err) } func runModAddSubNegTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r := c.NewRandomZr(rng) minusr := c.ModNeg(r, c.GroupOrder) @@ -582,18 +607,35 @@ func runModAddSubNegTest(t *testing.T, c *Curve) { b2 := c.NewRandomZr(rng) v := c.ModAddMul2(a, b, a2, b2, c.GroupOrder) - atb := c.ModMul(a, b, c.GroupOrder) - a2tb2 := c.ModMul(a2, b2, c.GroupOrder) - v2 := c.ModAdd(atb, a2tb2, c.GroupOrder) + v2 := c.ModAdd( + c.ModMul(a, b, c.GroupOrder), + c.ModMul(a2, b2, c.GroupOrder), + c.GroupOrder, + ) assert.True(t, v.Equals(v2)) v3 := c.ModAddMul([]*Zr{a, a2}, []*Zr{b, b2}, c.GroupOrder) assert.True(t, v.Equals(v3)) + + a3 := c.NewRandomZr(rng) + b3 := c.NewRandomZr(rng) + v = c.ModAddMul3(a, b, a2, b2, a3, b3, c.GroupOrder) + v2 = c.ModAdd( + c.ModAdd( + c.ModMul(a, b, c.GroupOrder), + c.ModMul(a2, b2, c.GroupOrder), + c.GroupOrder, + ), + c.ModMul(a3, b3, c.GroupOrder), + c.GroupOrder, + ) + assert.True(t, v.Equals(v2)) } func runMulTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) r := c.NewRandomZr(rng) rInv := r.Copy() @@ -607,8 +649,9 @@ func runMulTest(t *testing.T, c *Curve) { } func runQuadDHTestPairing(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) x := c.NewRandomZr(rng) y := c.NewRandomZr(rng) z := c.NewRandomZr(rng) @@ -641,8 +684,9 @@ func runQuadDHTestPairing(t *testing.T, c *Curve) { } func runDHTestG1(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) a := c.NewRandomZr(rng) b := c.NewRandomZr(rng) @@ -658,8 +702,9 @@ func runDHTestG1(t *testing.T, c *Curve) { } func runDHTestG2(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) a := c.NewRandomZr(rng) b := c.NewRandomZr(rng) @@ -675,8 +720,9 @@ func runDHTestG2(t *testing.T, c *Curve) { } func runCopyCloneTest(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) a := c.NewRandomZr(rng) aclone := c.NewRandomZr(rng) @@ -701,6 +747,7 @@ func runCopyCloneTest(t *testing.T, c *Curve) { } func testModAdd(t *testing.T, c *Curve) { + t.Helper() i1 := c.NewZrFromInt(math.MaxInt64) i2 := c.NewZrFromInt(math.MaxInt64) g1 := c.GenG1.Mul2(i1, c.GenG1, i2) @@ -708,22 +755,23 @@ func testModAdd(t *testing.T, c *Curve) { i3 := c.ModAdd(i1, i2, c.GroupOrder) g2 := c.GenG1.Mul(i3) - assert.True(t, g1.Equals(g2), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, g1.Equals(g2), "failed with curve %T", c.c) } func testModAdd2(t *testing.T, c *Curve) { + t.Helper() i1 := c.NewZrFromInt(math.MaxInt64) i2 := c.NewZrFromInt(math.MaxInt64) g1 := c.GenG1.Mul2(i1, c.GenG1, i2) - g2 := c.NewG1() - g2 = c.GenG1.Copy() + g2 := c.GenG1.Copy() g2.Mul2InPlace(i1, c.GenG1, i2) - assert.True(t, g1.Equals(g2), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, g1.Equals(g2), "failed with curve %T", c.c) } func testNotZeroAfterAdd(t *testing.T, c *Curve) { + t.Helper() i1 := c.NewZrFromInt(math.MaxInt64) i2 := c.NewZrFromInt(math.MaxInt64) i3 := c.NewZrFromInt(2) @@ -732,7 +780,7 @@ func testNotZeroAfterAdd(t *testing.T, c *Curve) { zero := c.NewZrFromInt(0) - assert.False(t, zero.Equals(i4), fmt.Sprintf("failed with curve %T", c.c)) + assert.False(t, zero.Equals(i4), "failed with curve %T", c.c) } type testJsonStruct struct { @@ -743,8 +791,9 @@ type testJsonStruct struct { } func runJsonMarshaler(t *testing.T, c *Curve) { + t.Helper() rng, err := c.Rand() - assert.NoError(t, err) + require.NoError(t, err) zr := c.NewRandomZr(rng) g1 := c.GenG1.Mul(zr) @@ -759,16 +808,16 @@ func runJsonMarshaler(t *testing.T, c *Curve) { } bytes, err := json.Marshal(testStruct) - assert.NoError(t, err) + require.NoError(t, err) testStruct = &testJsonStruct{} err = json.Unmarshal(bytes, testStruct) - assert.NoError(t, err) + require.NoError(t, err) - assert.True(t, testStruct.Zr.Equals(zr), fmt.Sprintf("failed with curve %T", c.c)) - assert.True(t, testStruct.G1.Equals(g1), fmt.Sprintf("failed with curve %T", c.c)) - assert.True(t, testStruct.G2.Equals(g2), fmt.Sprintf("failed with curve %T", c.c)) - assert.True(t, testStruct.Gt.Equals(gt), fmt.Sprintf("failed with curve %T", c.c)) + assert.True(t, testStruct.Zr.Equals(zr), "failed with curve %T", c.c) + assert.True(t, testStruct.G1.Equals(g1), "failed with curve %T", c.c) + assert.True(t, testStruct.G2.Equals(g2), "failed with curve %T", c.c) + assert.True(t, testStruct.Gt.Equals(gt), "failed with curve %T", c.c) } func TestJSONMarshalerFails(t *testing.T) { @@ -776,28 +825,28 @@ func TestJSONMarshalerFails(t *testing.T) { zr, g1, g2, gt := &Zr{}, &G1{}, &G2{}, &Gt{} err = json.Unmarshal([]byte(`{"element":1}`), zr) - assert.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") + require.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") err = json.Unmarshal([]byte(`{"element":1}`), g1) - assert.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") + require.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") err = json.Unmarshal([]byte(`{"element":1}`), g2) - assert.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") + require.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") err = json.Unmarshal([]byte(`{"element":1}`), gt) - assert.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") + require.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") // err = json.Unmarshal([]byte(`{"element":"YQo="}`), zr) // assert.EqualError(t, err, "json: cannot unmarshal number into Go struct field curveElement.element of type []uint8") err = json.Unmarshal([]byte(`{"element":"YQo="}`), g1) - assert.EqualError(t, err, "failure [runtime error: index out of range [2] with length 2]") + require.EqualError(t, err, "failure [runtime error: index out of range [2] with length 2]") err = json.Unmarshal([]byte(`{"element":"YQo="}`), g2) - assert.EqualError(t, err, "failure [runtime error: index out of range [2] with length 2]") + require.EqualError(t, err, "failure [runtime error: index out of range [2] with length 2]") err = json.Unmarshal([]byte(`{"element":"YQo="}`), gt) - assert.EqualError(t, err, "failure [runtime error: index out of range [2] with length 2]") + require.EqualError(t, err, "failure [runtime error: index out of range [2] with length 2]") } func TestCurves(t *testing.T) { @@ -829,7 +878,7 @@ func TestCurves(t *testing.T) { func Test381Compat(t *testing.T) { rng, err := Curves[BLS12_381].Rand() - assert.NoError(t, err) + require.NoError(t, err) kilic := Curves[BLS12_381] gurvy := Curves[BLS12_381_GURVY] @@ -863,7 +912,7 @@ func Test381Compat(t *testing.T) { func Test381BBSCompat(t *testing.T) { rng, err := Curves[BLS12_381_BBS].Rand() - assert.NoError(t, err) + require.NoError(t, err) kilic := Curves[BLS12_381_BBS] gurvy := Curves[BLS12_381_BBS_GURVY] diff --git a/perf_test.go b/perf_test.go index 1986197..b820f56 100644 --- a/perf_test.go +++ b/perf_test.go @@ -8,7 +8,6 @@ package math import ( "crypto/rand" - "fmt" "io" "math/big" "testing" @@ -28,6 +27,7 @@ func newRandZr(rng io.Reader, m *big.Int) *big.Int { } func blsInit(b *testing.B, curve *Curve) (*G2, *Zr, error) { + b.Helper() rng, err := curve.Rand() if err != nil { return nil, nil, err @@ -40,6 +40,7 @@ func blsInit(b *testing.B, curve *Curve) (*G2, *Zr, error) { } func blsInitGurvy(b *testing.B) (*bls12381.G2Affine, *big.Int) { + b.Helper() rng := rand.Reader _, _, _, g2 := bls12381.Generators() @@ -51,6 +52,7 @@ func blsInitGurvy(b *testing.B) (*bls12381.G2Affine, *big.Int) { } func blsInitKilic(b *testing.B) (*kilic.PointG2, *big.Int) { + b.Helper() rng := rand.Reader _g := kilic.NewG2() @@ -64,6 +66,7 @@ func blsInitKilic(b *testing.B) (*kilic.PointG2, *big.Int) { } func pokPedersenCommittmentInit(b *testing.B, curve *Curve) (io.Reader, *G1, *G1, *Zr, error) { + b.Helper() rng, err := curve.Rand() if err != nil { return nil, nil, nil, nil, err @@ -77,6 +80,7 @@ func pokPedersenCommittmentInit(b *testing.B, curve *Curve) (io.Reader, *G1, *G1 } func pokPedersenCommittmentInitGurvy(b *testing.B) (io.Reader, *bls12381.G1Affine, *bls12381.G1Affine, *big.Int) { + b.Helper() rng := rand.Reader _, _, g1, _ := bls12381.Generators() @@ -89,6 +93,7 @@ func pokPedersenCommittmentInitGurvy(b *testing.B) (io.Reader, *bls12381.G1Affin } func pokPedersenCommittmentInitKilic(b *testing.B) (io.Reader, *kilic.PointG1, *kilic.PointG1, *big.Int) { + b.Helper() rng := rand.Reader _g := kilic.NewG1() @@ -112,8 +117,7 @@ func Benchmark_Sequential_PedersenCommitmentPoKKilic(b *testing.B) { b.ResetTimer() b.Run("curve BLS12_381 (direct)", func(b *testing.B) { - - for i := 0; i < b.N; i++ { + for range b.N { r := newRandZr(rng, mod) c := _g.New() _g.MulScalarBig(c, g, x) @@ -154,9 +158,7 @@ func Benchmark_Sequential_PedersenCommitmentPoKGurvy(b *testing.B) { b.ResetTimer() b.Run("curve BLS12_381_GURVY (direct)", func(b *testing.B) { - - for i := 0; i < b.N; i++ { - + for range b.N { r := newRandZr(rng, fr.Modulus()) c := new(bls12381.G1Affine).ScalarMultiplication(g, x) c.Add(c, new(bls12381.G1Affine).ScalarMultiplication(h, r)) @@ -185,7 +187,6 @@ func Benchmark_Sequential_PedersenCommitmentPoKGurvy(b *testing.B) { } func Benchmark_Sequential_PedersenCommitmentPoK(b *testing.B) { - for _, curve := range Curves { rng, g, h, x, err := pokPedersenCommittmentInit(b, curve) if err != nil { @@ -194,9 +195,8 @@ func Benchmark_Sequential_PedersenCommitmentPoK(b *testing.B) { b.ResetTimer() - b.Run(fmt.Sprintf("curve %s", CurveIDToString(curve.curveID)), func(b *testing.B) { - - for i := 0; i < b.N; i++ { + b.Run("curve "+CurveIDToString(curve.curveID), func(b *testing.B) { + for range b.N { r := curve.NewRandomZr(rng) c := g.Mul(x) c.Add(h.Mul(r)) @@ -226,7 +226,6 @@ func Benchmark_Sequential_PedersenCommitmentPoK(b *testing.B) { } func Benchmark_Sequential_BLS(b *testing.B) { - for _, curve := range Curves { g, x, err := blsInit(b, curve) if err != nil { @@ -239,8 +238,8 @@ func Benchmark_Sequential_BLS(b *testing.B) { var sig *G1 - b.Run(fmt.Sprintf("sign curve %s", CurveIDToString(curve.curveID)), func(b *testing.B) { - for i := 0; i < b.N; i++ { + b.Run("sign curve "+CurveIDToString(curve.curveID), func(b *testing.B) { + for range b.N { h := curve.HashToG1WithDomain([]byte("msg"), []byte("context")) sig = h.Mul(x) } @@ -248,8 +247,8 @@ func Benchmark_Sequential_BLS(b *testing.B) { sig.Neg() - b.Run(fmt.Sprintf("verify curve %s", CurveIDToString(curve.curveID)), func(b *testing.B) { - for i := 0; i < b.N; i++ { + b.Run("verify curve "+CurveIDToString(curve.curveID), func(b *testing.B) { + for range b.N { h := curve.HashToG1WithDomain([]byte("msg"), []byte("context")) p := curve.Pairing2(g, sig, pk, h) @@ -379,7 +378,7 @@ func Benchmark_Parallel_BLS(b *testing.B) { var sig *G1 - b.Run(fmt.Sprintf("sign curve %s", CurveIDToString(curve.curveID)), func(b *testing.B) { + b.Run("sign curve "+CurveIDToString(curve.curveID), func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { h := curve.HashToG1WithDomain([]byte("msg"), []byte("context")) @@ -390,7 +389,7 @@ func Benchmark_Parallel_BLS(b *testing.B) { sig.Neg() - b.Run(fmt.Sprintf("verify curve %s", CurveIDToString(curve.curveID)), func(b *testing.B) { + b.Run("verify curve "+CurveIDToString(curve.curveID), func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { h := curve.HashToG1WithDomain([]byte("msg"), []byte("context")) @@ -462,7 +461,6 @@ func Benchmark_Parallel_IndividualOpsGurvy(b *testing.B) { b.Run("pairing2/gurvy", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - t_gurv, err = bls12381.MillerLoop([]bls12381.G1Affine{*sig_gurv, h_gurv}, []bls12381.G2Affine{*g_gurv, *pk_gurv}) if err != nil { panic(err)