From c42a9d912565adba6a5d99bae710e3b00dcef0e5 Mon Sep 17 00:00:00 2001
From: Arella Matteo
Date: Thu, 20 Jul 2023 20:46:54 +0200
Subject: [PATCH] Initial commit
---
.devcontainer/Dockerfile | 23 +
.devcontainer/docs/Dockerfile | 19 +
.devcontainer/docs/devcontainer.json | 16 +
.devcontainer/openssl-1.0.2/devcontainer.json | 16 +
.devcontainer/openssl-1.1.0/devcontainer.json | 16 +
.devcontainer/openssl-1.1.1/devcontainer.json | 16 +
.devcontainer/openssl-3.0.1/devcontainer.json | 16 +
.dockerignore | 1 +
.editorconfig | 18 +
.github/dependabot.yml | 6 +
.github/workflows/docs.yml | 127 +
.github/workflows/main.yml | 272 +
.gitignore | 1 +
.golangci.yml | 67 +
Dockerfile | 215 +
LICENSE | 21 +
README.md | 36 +
big/bn.go | 742 ++
big/bn_test.go | 126 +
big/context.go | 142 +
big/goopenssl.c | 267 +
big/goopenssl.h | 68 +
big/goopenssl_dev.c | 193 +
big/openssl.go | 183 +
big/openssl_dev.go | 128 +
big/openssl_funcs.h | 181 +
big/openssl_lock_setup.c | 70 +
big/openssl_test.go | 26 +
cmd/pedersen/pedersen.go | 19 +
combine.go | 165 +
combine_test.go | 232 +
docker-bake.hcl | 183 +
docs/.gitignore | 21 +
docs/babel.config.js | 3 +
docs/docs/01-acknowledgements.mdx | 8 +
docs/docs/getting-started/01-installation.mdx | 83 +
docs/docs/getting-started/_category_.json | 7 +
docs/docs/guides/01-group.mdx | 89 +
docs/docs/guides/02-split.mdx | 71 +
docs/docs/guides/03-combine.mdx | 35 +
docs/docs/guides/04-verify.mdx | 99 +
docs/docs/guides/_category_.yml | 5 +
docs/docs/intro.mdx | 27 +
docs/docs/reference/01-go.mdx | 7 +
docs/docs/reference/02-cli.mdx | 168 +
docs/docs/reference/_category_.yml | 5 +
docs/docusaurus.config.js | 141 +
docs/package.json | 50 +
docs/sidebars.js | 19 +
docs/src/components/BinariesTabs.tsx | 82 +
docs/src/components/RepoUrl.tsx | 14 +
docs/src/css/custom.css | 59 +
docs/static/.nojekyll | 0
docs/static/img/docusaurus-social-card.jpg | Bin 0 -> 55746 bytes
docs/static/img/favicon.ico | Bin 0 -> 3626 bytes
docs/static/img/logo.svg | 1 +
docs/tsconfig.json | 7 +
docs/yarn.lock | 7752 +++++++++++++++++
go.mod | 23 +
go.sum | 469 +
group.go | 281 +
group_test.go | 56 +
internal/cmd/combine.go | 129 +
internal/cmd/fmtflags.go | 159 +
internal/cmd/generate.go | 72 +
internal/cmd/generate_test.go | 171 +
internal/cmd/pedersenflags.go | 71 +
internal/cmd/root.go | 97 +
internal/cmd/root_test.go | 20 +
internal/cmd/split.go | 122 +
internal/cmd/verify.go | 200 +
internal/cmd/version.go | 37 +
internal/errors/errors.go | 56 +
internal/errors/errors_test.go | 25 +
internal/io/io.go | 66 +
internal/io/json/json.go | 59 +
internal/io/xml/xml.go | 60 +
internal/io/yaml/yaml.go | 54 +
internal/logger/logger.go | 75 +
internal/schema/schema.go | 28 +
internal/version.go | 9 +
pedersen.go | 198 +
pedersen_test.go | 120 +
polynomial.go | 254 +
scripts/openssl.sh | 178 +
secretpart.go | 54 +
split.go | 272 +
split_test.go | 206 +
verify.go | 263 +
verify_test.go | 250 +
90 files changed, 16498 insertions(+)
create mode 100644 .devcontainer/Dockerfile
create mode 100644 .devcontainer/docs/Dockerfile
create mode 100644 .devcontainer/docs/devcontainer.json
create mode 100644 .devcontainer/openssl-1.0.2/devcontainer.json
create mode 100644 .devcontainer/openssl-1.1.0/devcontainer.json
create mode 100644 .devcontainer/openssl-1.1.1/devcontainer.json
create mode 100644 .devcontainer/openssl-3.0.1/devcontainer.json
create mode 100644 .dockerignore
create mode 100644 .editorconfig
create mode 100644 .github/dependabot.yml
create mode 100644 .github/workflows/docs.yml
create mode 100644 .github/workflows/main.yml
create mode 100644 .gitignore
create mode 100644 .golangci.yml
create mode 100644 Dockerfile
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 big/bn.go
create mode 100644 big/bn_test.go
create mode 100644 big/context.go
create mode 100644 big/goopenssl.c
create mode 100644 big/goopenssl.h
create mode 100644 big/goopenssl_dev.c
create mode 100644 big/openssl.go
create mode 100644 big/openssl_dev.go
create mode 100644 big/openssl_funcs.h
create mode 100644 big/openssl_lock_setup.c
create mode 100644 big/openssl_test.go
create mode 100644 cmd/pedersen/pedersen.go
create mode 100644 combine.go
create mode 100644 combine_test.go
create mode 100644 docker-bake.hcl
create mode 100644 docs/.gitignore
create mode 100644 docs/babel.config.js
create mode 100644 docs/docs/01-acknowledgements.mdx
create mode 100644 docs/docs/getting-started/01-installation.mdx
create mode 100644 docs/docs/getting-started/_category_.json
create mode 100644 docs/docs/guides/01-group.mdx
create mode 100644 docs/docs/guides/02-split.mdx
create mode 100644 docs/docs/guides/03-combine.mdx
create mode 100644 docs/docs/guides/04-verify.mdx
create mode 100644 docs/docs/guides/_category_.yml
create mode 100644 docs/docs/intro.mdx
create mode 100644 docs/docs/reference/01-go.mdx
create mode 100644 docs/docs/reference/02-cli.mdx
create mode 100644 docs/docs/reference/_category_.yml
create mode 100644 docs/docusaurus.config.js
create mode 100644 docs/package.json
create mode 100644 docs/sidebars.js
create mode 100644 docs/src/components/BinariesTabs.tsx
create mode 100644 docs/src/components/RepoUrl.tsx
create mode 100644 docs/src/css/custom.css
create mode 100644 docs/static/.nojekyll
create mode 100644 docs/static/img/docusaurus-social-card.jpg
create mode 100644 docs/static/img/favicon.ico
create mode 100644 docs/static/img/logo.svg
create mode 100644 docs/tsconfig.json
create mode 100644 docs/yarn.lock
create mode 100644 go.mod
create mode 100644 go.sum
create mode 100644 group.go
create mode 100644 group_test.go
create mode 100644 internal/cmd/combine.go
create mode 100644 internal/cmd/fmtflags.go
create mode 100644 internal/cmd/generate.go
create mode 100644 internal/cmd/generate_test.go
create mode 100644 internal/cmd/pedersenflags.go
create mode 100644 internal/cmd/root.go
create mode 100644 internal/cmd/root_test.go
create mode 100644 internal/cmd/split.go
create mode 100644 internal/cmd/verify.go
create mode 100644 internal/cmd/version.go
create mode 100644 internal/errors/errors.go
create mode 100644 internal/errors/errors_test.go
create mode 100644 internal/io/io.go
create mode 100644 internal/io/json/json.go
create mode 100644 internal/io/xml/xml.go
create mode 100644 internal/io/yaml/yaml.go
create mode 100644 internal/logger/logger.go
create mode 100644 internal/schema/schema.go
create mode 100644 internal/version.go
create mode 100644 pedersen.go
create mode 100644 pedersen_test.go
create mode 100644 polynomial.go
create mode 100755 scripts/openssl.sh
create mode 100644 secretpart.go
create mode 100644 split.go
create mode 100644 split_test.go
create mode 100644 verify.go
create mode 100644 verify_test.go
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..7ee8d0b
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,23 @@
+# syntax=docker/dockerfile:1
+
+# Copyright (c) Pedersen authors.
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+
+ARG GO_VARIANT=1.17-alpine
+
+FROM golang:${GO_VARIANT}
+
+ARG GO_OPENSSL_VERSION_OVERRIDE=1.1.1
+
+ENV GO_OPENSSL_VERSION_OVERRIDE=${GO_OPENSSL_VERSION_OVERRIDE}
+
+RUN apk update && \
+ apk del --no-cache openssl && \
+ apk add --no-cache make build-base pkgconfig coreutils perl linux-headers
+
+COPY scripts/openssl.sh /tmp/openssl.sh
+
+RUN /tmp/openssl.sh --dev ${GO_OPENSSL_VERSION_OVERRIDE}
diff --git a/.devcontainer/docs/Dockerfile b/.devcontainer/docs/Dockerfile
new file mode 100644
index 0000000..03d5389
--- /dev/null
+++ b/.devcontainer/docs/Dockerfile
@@ -0,0 +1,19 @@
+# syntax=docker/dockerfile:1
+
+# Copyright (c) Pedersen authors.
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+
+ARG NODE_VERSION=20
+
+FROM node:${NODE_VERSION}-alpine
+
+EXPOSE 3000
+
+WORKDIR /src
+
+RUN apk add --no-cache git
+
+ENV NODE_ENV=dev
diff --git a/.devcontainer/docs/devcontainer.json b/.devcontainer/docs/devcontainer.json
new file mode 100644
index 0000000..d39df2a
--- /dev/null
+++ b/.devcontainer/docs/devcontainer.json
@@ -0,0 +1,16 @@
+{
+ "name": "Pedersen Docs",
+ "build": {
+ "dockerfile": "Dockerfile",
+ "context": "../..",
+ "args": {
+ "NODE_VERSION": "20"
+ }
+ },
+ "postStartCommand": "cd docs && yarn install && yarn start",
+ "customizations": {
+ "vscode": {
+ "extensions": ["ms-vscode.vscode-typescript-next", "leizongmin.node-module-intellisense"]
+ }
+ }
+}
diff --git a/.devcontainer/openssl-1.0.2/devcontainer.json b/.devcontainer/openssl-1.0.2/devcontainer.json
new file mode 100644
index 0000000..442041f
--- /dev/null
+++ b/.devcontainer/openssl-1.0.2/devcontainer.json
@@ -0,0 +1,16 @@
+{
+ "name": "Pedersen 1.0.2",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "context": "../..",
+ "args": {
+ "GO_VARIANT": "1.17-alpine",
+ "GO_OPENSSL_VERSION_OVERRIDE": "1.0.2"
+ }
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["golang.go", "ms-vscode.cpptools-extension-pack"]
+ }
+ }
+}
diff --git a/.devcontainer/openssl-1.1.0/devcontainer.json b/.devcontainer/openssl-1.1.0/devcontainer.json
new file mode 100644
index 0000000..79556ed
--- /dev/null
+++ b/.devcontainer/openssl-1.1.0/devcontainer.json
@@ -0,0 +1,16 @@
+{
+ "name": "Pedersen 1.1.0",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "context": "../..",
+ "args": {
+ "GO_VARIANT": "1.17-alpine",
+ "GO_OPENSSL_VERSION_OVERRIDE": "1.1.0"
+ }
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["golang.go", "ms-vscode.cpptools-extension-pack"]
+ }
+ }
+}
diff --git a/.devcontainer/openssl-1.1.1/devcontainer.json b/.devcontainer/openssl-1.1.1/devcontainer.json
new file mode 100644
index 0000000..4bab2d7
--- /dev/null
+++ b/.devcontainer/openssl-1.1.1/devcontainer.json
@@ -0,0 +1,16 @@
+{
+ "name": "Pedersen 1.1.1",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "context": "../..",
+ "args": {
+ "GO_VARIANT": "1.17-alpine",
+ "GO_OPENSSL_VERSION_OVERRIDE": "1.1.1"
+ }
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["golang.go", "ms-vscode.cpptools-extension-pack"]
+ }
+ }
+}
diff --git a/.devcontainer/openssl-3.0.1/devcontainer.json b/.devcontainer/openssl-3.0.1/devcontainer.json
new file mode 100644
index 0000000..72ae5f1
--- /dev/null
+++ b/.devcontainer/openssl-3.0.1/devcontainer.json
@@ -0,0 +1,16 @@
+{
+ "name": "Pedersen 3.0.1",
+ "build": {
+ "dockerfile": "../Dockerfile",
+ "context": "../..",
+ "args": {
+ "GO_VARIANT": "1.17-alpine",
+ "GO_OPENSSL_VERSION_OVERRIDE": "3.0.1"
+ }
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["golang.go", "ms-vscode.cpptools-extension-pack"]
+ }
+ }
+}
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..e660fd9
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+bin/
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..236d4a9
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# Matches multiple files with brace expansion notation
+# Set default charset
+[*.{go}]
+charset = utf-8
+
+# 4 space indentation
+[*.go]
+indent_style = tab
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..36b24f2
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: gomod
+ directory: /
+ schedule:
+ interval: daily
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..7e6a002
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,127 @@
+---
+name: Docs
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ schedule:
+ - cron: '0 8 */6 * *' # every 6 days to keep cache
+ workflow_dispatch:
+ inputs:
+ release_tag:
+ description: 'Release tag'
+ required: true
+ push:
+ branches:
+ - 'master'
+ pull_request:
+ branches:
+ - 'master'
+
+env:
+ RELEASE_BRANCH: master
+
+jobs:
+ prepare:
+ runs-on: ubuntu-22.04
+ outputs:
+ release_tag: ${{ steps.release_tag.outputs.release_tag }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Get latest tag on release branch
+ id: release_tag
+ run: |
+ set -ex
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.release_tag }}" ]; then
+ echo "release_tag=${{ inputs.release_tag }}" >> $GITHUB_OUTPUT
+ elif [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/${{ env.RELEASE_BRANCH }}" ]; then
+ echo "release_tag=$(git describe --match 'v[0-9]*' --abbrev=0 --tags ${RELEASE_BRANCH})" >> $GITHUB_OUTPUT || \
+ echo "release_tag=" >> $GITHUB_OUTPUT
+ else
+ echo "release_tag=" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Show latest release tag
+ run: |
+ echo ${{ steps.release_tag.outputs.release_tag }}
+
+ build:
+ runs-on: ubuntu-22.04
+ needs: [prepare]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Build docs
+ uses: docker/bake-action@v3
+ with:
+ targets: docs
+ set: |
+ *.args.ORGANIZATION_NAME=${{ github.repository_owner }}
+ *.args.REPO_NAME=${{ github.event.repository.name }}
+ *.args.REPO_URL=${{ github.server_url }}/${{ github.repository }}
+ *.args.DOCS_URL=https://${{ github.repository_owner }}.github.io
+ *.args.DOCS_EDIT_URL=${{ github.server_url }}/${{ github.repository }}/tree/${{ env.RELEASE_BRANCH }}/docs/
+ *.args.VERSION=${{ needs.prepare.outputs.release_tag }}
+ *.cache-from=type=gha,scope=docs
+ *.cache-to=type=gha,scope=docs,mode=max
+ env:
+ ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
+ ALGOLIA_SEARCH_API_KEY: ${{ secrets.ALGOLIA_SEARCH_API_KEY }}
+ ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }}
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: docs
+ path: ./bin/docs/*
+ if-no-files-found: error
+
+ release:
+ needs: [prepare, build]
+ runs-on: ubuntu-22.04
+ if: ${{ needs.prepare.outputs.release_tag != '' }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ ref: gh-pages
+
+ - name: Download artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: docs
+ path: /tmp/pedersen-docs
+
+ - name: List docs files
+ run: |
+ echo "DOCS_FILES<> $GITHUB_ENV
+ find /tmp/pedersen-docs -type f -printf "%P\n" >> $GITHUB_ENV
+ echo "EOF" >> $GITHUB_ENV
+
+ - name: Move docs files
+ run: |
+ rsync -cavr --ignore-times --delete /tmp/pedersen-docs/ .
+
+ - name: Commit changes
+ uses: swinton/commit@v2.x
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ files: ${{ env.DOCS_FILES }}
+ commit-message: Update docs
+ ref: refs/heads/gh-pages
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..43169d9
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,272 @@
+---
+name: Main
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on:
+ schedule:
+ - cron: '0 8 */6 * *' # every 6 days to keep cache
+ workflow_dispatch:
+ push:
+ branches:
+ - '**'
+ tags:
+ - 'v*'
+ pull_request:
+
+jobs:
+ prepare:
+ runs-on: ubuntu-22.04
+ outputs:
+ test_matrix: ${{ steps.platforms.outputs.test_matrix }}
+ build_matrix: ${{ steps.platforms.outputs.build_matrix }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Create matrix
+ id: platforms
+ run: |
+ echo test_matrix=$(docker buildx bake test-cross --print | jq -cr '.target."test-cross".platforms') >> $GITHUB_OUTPUT
+ echo build_matrix=$(docker buildx bake binary-cross --print | jq -cr '.target."binary-cross".platforms') >> $GITHUB_OUTPUT
+
+ - name: Show matrix
+ run: |
+ echo ${{ steps.platforms.outputs.test_matrix }}
+ echo ${{ steps.platforms.outputs.build_matrix }}
+
+ validate:
+ runs-on: ubuntu-22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ target: ['lint', 'license-validate']
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: ${{ matrix.target }}
+ uses: docker/bake-action@v3
+ with:
+ targets: ${{ matrix.target }}
+ set: |
+ *.cache-from=type=gha,scope=test
+ *.cache-to=type=gha,scope=test
+
+ test:
+ runs-on: ubuntu-22.04
+ needs: [prepare, validate]
+ strategy:
+ fail-fast: false
+ matrix:
+ go-version: ['1.20']
+ openssl-version: ['1.0.2', '1.1.0', '1.1.1', '3.0.1']
+ openssl-dev: [false, true]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Test
+ uses: docker/bake-action@v3
+ with:
+ targets: test
+ set: |
+ *.args.GO_VERSION=${{ matrix.go-version }}
+ *.args.OPENSSL_VERSION=${{ matrix.openssl-version }}
+ *.args.GO_BUILDTAGS=${{ matrix.openssl-dev && 'openssldev' || '' }}
+ *.args.GO_TESTFLAGS=-gcflags=all=-d=checkptr -count 10
+ *.cache-from=type=gha,scope=test-${{ matrix.openssl-version }}
+ *.cache-to=type=gha,scope=test-${{ matrix.openssl-version }}
+
+ - name: Test (with race detector)
+ uses: docker/bake-action@v3
+ with:
+ targets: test
+ set: |
+ *.args.GO_VERSION=${{ matrix.go-version }}
+ *.args.OPENSSL_VERSION=${{ matrix.openssl-version }}
+ *.args.GO_BUILDTAGS=${{ matrix.openssl-dev && 'openssldev' || '' }}
+ *.args.GO_TESTFLAGS=-race
+ *.cache-from=type=gha,scope=test-${{ matrix.openssl-version }}
+ *.cache-to=type=gha,scope=test-${{ matrix.openssl-version }}
+
+ test-cross:
+ runs-on: ubuntu-22.04
+ needs: [prepare, validate]
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: ${{ fromJson(needs.prepare.outputs.test_matrix) }}
+ steps:
+ - name: Prepare
+ run: |
+ platform=${{ matrix.platform }}
+ echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
+
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v2
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Test
+ uses: docker/bake-action@v3
+ with:
+ targets: test-cross
+ set: |
+ *.platform=${{ matrix.platform }}
+ *.args.GO_BUILDTAGS=openssldev
+ *.args.GO_TESTFLAGS=-gcflags=all=-d=checkptr -count 10
+ *.cache-from=type=gha,scope=test-${{ env.PLATFORM_PAIR }}
+ *.cache-to=type=gha,scope=test-${{ env.PLATFORM_PAIR }}
+
+ binary:
+ runs-on: ubuntu-22.04
+ needs: [prepare, test, test-cross]
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: ${{ fromJson(needs.prepare.outputs.build_matrix) }}
+ steps:
+ - name: Prepare
+ run: |
+ platform=${{ matrix.platform }}
+ echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
+
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Build static binary
+ uses: docker/bake-action@v3
+ with:
+ targets: release
+ set: |
+ *.platform=${{ matrix.platform }}
+ *.args.GO_BUILDTAGS=openssldev
+ *.args.GO_BUILDFLAGS=-v -x
+ *.args.GO_LINKMODE=static
+ *.args.GO_STRIP=1
+ *.cache-from=type=gha,scope=binary-${{ env.PLATFORM_PAIR }}
+ *.cache-to=type=gha,scope=binary-${{ env.PLATFORM_PAIR }},mode=max
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: pedersen
+ path: ./bin/release/*
+ if-no-files-found: error
+
+ release-image:
+ needs: [binary]
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Docker meta
+ id: meta
+ uses: docker/metadata-action@v4
+ with:
+ images: |
+ name=arella/pedersen
+ flavor: |
+ latest=auto
+ tags: |
+ type=ref,event=tag
+ type=semver,pattern={{version}}
+
+ - uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_WRITE_TOKEN }}
+
+ - name: Build images
+ uses: docker/bake-action@v3
+ with:
+ files: |
+ ./docker-bake.hcl
+ ${{ steps.meta.outputs.bake-file }}
+ targets: image-cross
+ push: ${{ startsWith(github.ref, 'refs/tags/v') }}
+ sbom: true
+ set: |
+ *.args.GO_BUILDTAGS=openssldev
+ *.args.GO_BUILDFLAGS=-v -x
+ *.args.GO_LINKMODE=static
+ *.args.GO_STRIP=1
+ *.cache-from=type=gha,scope=image-${{ env.PLATFORM_PAIR }}
+ *.cache-to=type=gha,scope=image-${{ env.PLATFORM_PAIR }},mode=max
+
+ release-bin:
+ needs: [binary]
+ runs-on: ubuntu-22.04
+ if: ${{ startsWith(github.ref, 'refs/tags/v') }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Download artifacts
+ uses: actions/download-artifact@v3
+ with:
+ name: pedersen
+ path: ./bin/release/*
+
+ - name: Release
+ uses: softprops/action-gh-release@v1
+ with:
+ files: |
+ ./bin/release/**
+ draft: false
+ generate_release_notes: true
+
+ - name: Trigger docs build
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ await github.rest.actions.createWorkflowDispatch({
+ owner: '${{ github.repository_owner }}',
+ repo: '${{ github.event.repository.name }}',
+ workflow_id: 'docs.yml',
+ ref: '${{ github.ref }}',
+ inputs: {
+ "release_tag": "${{ github.ref_name }}"
+ }
+ })
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..6914e88
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,67 @@
+run:
+ concurrency: 2
+ timeout: 10m
+linters:
+ enable-all: false
+ disable-all: true
+ enable:
+ - depguard
+ - errcheck
+ - gocritic
+ - gofmt
+ - goimports
+ - gomodguard
+ - revive
+ - gosimple
+ - govet
+ - ineffassign
+ - lll
+ - misspell
+ - nakedret
+ - nolintlint
+ - staticcheck
+ - typecheck
+ - unconvert
+ - unparam
+ - unused
+linters-settings:
+ revive:
+ rules:
+ - name: package-comments
+ disabled: true
+ depguard:
+ rules:
+ all:
+ deny:
+ - pkg: io/ioutil
+ desc: 'io/ioutil package has been deprecated'
+ gomodguard:
+ blocked:
+ versions:
+ - gotest.tools:
+ version: "< 3.0.0"
+ reason: "deprecated, pre-modules version"
+ gocritic:
+ # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
+ # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
+ enabled-tags:
+ - diagnostic
+ - opinionated
+ - style
+ disabled-checks:
+ - dupImport # https://github.com/go-critic/go-critic/issues/845
+ - paramTypeCombine
+ - unnamedResult
+ - whyNoLint
+ - uncheckedInlineErr
+ - dupSubExpr
+ gocyclo:
+ min-complexity: 16
+ lll:
+ line-length: 200
+issues:
+ # golangci hides some golint warnings (the warning about exported things
+ # withtout documentation for example), this will make it show them anyway.
+ exclude-use-default: false
+ exclude:
+ - should not use dot imports
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..0608fda
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,215 @@
+# syntax=docker/dockerfile:1
+
+# Copyright (c) Pedersen authors.
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+
+ARG GO_VERSION=1.20.5
+ARG XX_VERSION=1.2.1
+ARG GOLANGCI_LINT_VERSION=v1.53.2
+ARG ADDLICENSE_VERSION=v1.0.0
+ARG NODE_VERSION=20
+ARG LICENSE_FILES=".*\(Dockerfile\|\.go\|\.hcl\)"
+
+# xx is a helper for cross-compilation
+FROM --platform=${BUILDPLATFORM} tonistiigi/xx:${XX_VERSION} AS xx
+
+# osxcross contains the MacOSX cross toolchain for xx
+FROM tonistiigi/xx:sdk-extras AS osxcross
+
+FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
+FROM ghcr.io/google/addlicense:${ADDLICENSE_VERSION} AS addlicense
+
+FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine AS base
+COPY --from=xx / /
+RUN apk add --no-cache bash perl make pkgconfig clang lld llvm git file
+WORKDIR /src
+ENV CGO_ENABLED=1
+
+FROM --platform=${BUILDPLATFORM} base AS base-linux
+ARG TARGETPLATFORM
+RUN xx-apk add gcc musl-dev linux-headers
+
+FROM --platform=${BUILDPLATFORM} base-linux AS base-darwin
+
+FROM --platform=${BUILDPLATFORM} base AS base-windows
+RUN apk add --no-cache mingw-w64-gcc
+ARG TARGETPLATFORM
+RUN mv /usr/bin/$(xx-info)-windres /usr/bin/$(xx-info)-windres.orig
+RUN xx-clang --setup-target-triple && \
+ ln -s /usr/bin/$(xx-info)-windres /usr/bin/windres
+
+FROM base-${TARGETOS} AS build-base
+COPY go.* .
+RUN --mount=type=cache,target=/go/pkg/mod \
+ --mount=type=cache,target=/root/.cache/go-build \
+ go mod download
+
+FROM build-base AS build-openssl
+ARG OPENSSL_VERSION=3.0.1
+ARG TARGETPLATFORM
+ENV GO_OPENSSL_VERSION_OVERRIDE=${OPENSSL_VERSION}
+COPY scripts/openssl.sh /bin/openssl.sh
+RUN --mount=type=bind,from=osxcross,src=/xx-sdk,target=/xx-sdk,rw < /etc/ld-musl-$(xx-info march).path
+ fi
+ go test $([ ! -n "$GO_BUILDTAGS" ] || echo "-tags ${GO_BUILDTAGS}") ${GO_TESTFLAGS} -v ./...
+EOT
+
+FROM base AS license-validate
+ARG LICENSE_FILES
+RUN --mount=type=bind,target=. \
+ --mount=from=addlicense,source=/app/addlicense,target=/usr/bin/addlicense \
+ find . -regex "${LICENSE_FILES}" | xargs addlicense -check -c 'Pedersen authors' -l mit -v
+
+FROM --platform=${BUILDPLATFORM} node:${NODE_VERSION}-alpine AS docs-build
+ARG ORGANIZATION_NAME=matteoarella
+ARG REPO_NAME=pedersen
+ARG REPO_URL=https://github.com/matteoarella/pedersen
+ARG DOCS_URL=https://matteoarella.github.io
+ARG DOCS_EDIT_URL=https://github.com/matteoarella/pedersen/tree/master/docs/
+ARG VERSION
+
+ENV ORGANIZATION_NAME=${ORGANIZATION_NAME}
+ENV REPO_NAME=${REPO_NAME}
+ENV REPO_URL=${REPO_URL}
+ENV DOCS_URL=${DOCS_URL}
+ENV DOCS_EDIT_URL=${DOCS_EDIT_URL}
+ENV NODE_ENV=production
+
+WORKDIR /docs
+COPY ./ .
+RUN apk update && apk add --no-cache git
+RUN --mount=type=secret,id=ALGOLIA_APP_ID \
+ --mount=type=secret,id=ALGOLIA_SEARCH_API_KEY \
+ --mount=type=secret,id=ALGOLIA_INDEX_NAME <
+
+
+
+
+
+
+> Secret sharing (also called secret splitting) refers to methods for distributing a secret among a group, in such a way that no individual holds any intelligible information about the secret, but when a sufficient number of individuals combine their 'shares', the secret may be reconstructed.
+> [Wikipedia](https://en.wikipedia.org/wiki/Secret_sharing)
+
+Secret sharing schemes are ideal for storing information that is highly sensitive
+and highly important, like for example encryption keys. Each of these pieces of
+information must be kept highly confidential, as their exposure could be disastrous;
+however, it is also critical that they should not be lost. Traditional methods for
+encryption are ill-suited for simultaneously achieving high levels of confidentiality
+and reliability. This is because when storing the encryption key, one must choose
+between keeping a single copy of the key in one location for maximum secrecy,
+or keeping multiple copies of the key in different locations for greater reliability.
+Increasing reliability of the key by storing multiple copies lowers confidentiality by
+creating additional attack vectors. Secret sharing schemes address this problem,
+and allow arbitrarily high levels of confidentiality and reliability to be achieved.
+
+> In cryptography, a secret sharing scheme is verifiable if auxiliary information is included that allows players to verify their shares as consistent.
+> [Wikipedia](https://en.wikipedia.org/wiki/Verifiable_secret_sharing)
+
+The `pedersen` package implements verifiable secret sharing procedures that are defined by [Pedersen Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing](https://link.springer.com/chapter/10.1007/3-540-46766-1_9) conference paper.
+
+# Getting started
+
+Any information on how to use this project can be found [here](https://matteoarella.github.io/pedersen).
+
+# License
+
+Pedersen is released under MIT license. See [LICENSE](./LICENSE).
diff --git a/big/bn.go b/big/bn.go
new file mode 100644
index 0000000..0682dfc
--- /dev/null
+++ b/big/bn.go
@@ -0,0 +1,742 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+//go:build cgo
+// +build cgo
+
+package big
+
+// #include "goopenssl.h"
+import "C"
+import (
+ "crypto/subtle"
+ "encoding/json"
+ "errors"
+ "math"
+ "runtime"
+ "unsafe"
+)
+
+var (
+ ErrInvalidParse = errors.New("invalid parse")
+)
+
+// An Int represents a signed multi-precision integer.
+type Int struct {
+ bn *C.GO_BIGNUM
+}
+
+func (z *Int) wrapInt(finalize bool) *Int {
+ runtime.SetFinalizer(z, func(bn *Int) {
+ if bn.bn == nil {
+ return
+ }
+
+ if finalize {
+ C.go_openssl_BN_free(bn.bn)
+ }
+
+ bn.bn = nil
+ })
+
+ return z
+}
+
+func (z *Int) init() error {
+ if z.bn == nil {
+ if !isGeq11() {
+ z.bn = C.go_openssl_BN_new()
+ if z.bn == nil {
+ return newOpenSSLError("BN_new")
+ }
+ } else {
+ z.bn = C.go_openssl_BN_secure_new()
+ if z.bn == nil {
+ return newOpenSSLError("BN_secure_new")
+ }
+ }
+
+ z.wrapInt(true)
+ }
+
+ return nil
+}
+
+// Allocates and initialize an Int struct.
+func NewInt() (*Int, error) {
+ i := &Int{}
+
+ err := i.init()
+ if err != nil {
+ return nil, err
+ }
+
+ return i, nil
+}
+
+// Returns a constant Int with value 1.
+func One() *Int {
+ one := &Int{
+ bn: C.go_openssl_BN_value_one(),
+ }
+
+ return one.wrapInt(false)
+}
+
+func (i *Int) SetConstantTime() *Int {
+ err := i.init()
+ if err != nil {
+ return i
+ }
+
+ if isGeq11() {
+ C.go_openssl_BN_set_flags(i.bn, C.GO_BN_FLG_CONSTTIME)
+ } else {
+ C.legacy_1_0_BN_set_flags(i.bn, C.GO_BN_FLG_CONSTTIME)
+ }
+
+ return i
+}
+
+// String returns the decimal representation of i.
+func (i *Int) String() string {
+ return C.GoString(C.go_openssl_BN_bn2dec(i.bn))
+}
+
+// String returns the hexadecimal representation of i.
+func (i *Int) Hex() string {
+ return C.GoString(C.go_openssl_BN_bn2hex(i.bn))
+}
+
+func (i *Int) unmarshalString(data string) error {
+ length := len(data)
+
+ if length < 1 {
+ return nil
+ }
+
+ if length > 2 && data[0] == '0' && (data[1] == 'x' || data[1] == 'X') {
+ return i.SetHexString(data[2:])
+ }
+
+ // try decimal
+ err := i.SetDecString(data)
+ if err == nil {
+ return nil
+ }
+
+ // try exadecimal
+ return i.SetHexString(data)
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (i *Int) MarshalJSON() ([]byte, error) {
+ return json.Marshal("0x" + i.Hex())
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface..
+func (i *Int) UnmarshalJSON(data []byte) error {
+ var x string
+ err := json.Unmarshal(data, &x)
+ if err != nil {
+ return err
+ }
+
+ return i.unmarshalString(x)
+}
+
+// MarshalText implements the encoding.TextMarshaler interface.
+func (i *Int) MarshalText() ([]byte, error) {
+ return []byte("0x" + i.Hex()), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface.
+func (i *Int) UnmarshalText(data []byte) error {
+ return i.unmarshalString(string(data))
+}
+
+// GeneratePrime generates a pseudo-random prime number of at least bit length bits
+// using the IntContext provided in ctx.
+// The returned number is probably prime with a negligible error. The maximum error
+// rate is 2^-128. It's 2^-287 for a 512 bit prime, 2^-435 for a 1024 bit prime, 2^-648
+// for a 2048 bit prime, and lower than 2^-882 for primes larger than 2048 bit.
+// If safe is true, it will be a safe prime (i.e. a prime p so that (p-1)/2 is also prime).
+// ctx is a previously allocated IntContext used for temporary variables.
+func GeneratePrime(ctx *IntContext, bits int, safe bool) (*Int, error) {
+ p, err := NewInt()
+ if err != nil {
+ return nil, err
+ }
+
+ safePrime := C.int(0)
+ if safe {
+ safePrime = C.int(1)
+ }
+
+ if !is30() {
+ r := C.go_openssl_BN_generate_prime_ex(p.bn, C.int(bits), safePrime, nil, nil, nil)
+ if r != 1 {
+ return nil, newOpenSSLError("BN_generate_prime_ex")
+ }
+
+ return p, nil
+ }
+
+ newCtx := ctx
+ if newCtx == nil {
+ newCtx, err = NewIntContext()
+ if err != nil {
+ return nil, err
+ }
+
+ defer newCtx.Destroy()
+ }
+
+ r := C.go_openssl_BN_generate_prime_ex2(p.bn, C.int(bits), safePrime, nil, nil, nil, newCtx.ctx)
+ if r != 1 {
+ return nil, newOpenSSLError("BN_generate_prime_ex2")
+ }
+
+ return p, nil
+}
+
+// ProbablyPrime tests if the number z is prime. The functions tests until one of the tests
+// shows that z is composite, or all the tests passed. If z passes all these tests, it is
+// considered a probable prime.
+// The test performed on z are trial division by a number of small primes and rounds of the
+// of the Miller-Rabin probabilistic primality test.
+// The functions do at least 64 rounds of the Miller-Rabin test giving a maximum false
+// positive rate of 2^-128.
+// If the size of z is more than 2048 bits, they do at least 128 rounds giving a maximum
+// false positive rate of 2^-256.
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) ProbablyPrime(ctx *IntContext) (bool, error) {
+ newCtx := ctx
+ if newCtx == nil {
+ newCtx, err := NewIntContext()
+ if err != nil {
+ return false, err
+ }
+
+ defer newCtx.Destroy()
+ }
+
+ if isLegacy1() {
+ nChecks := C.go_openssl_BN_prime_checks_for_size(C.int(z.BitLen()))
+ ret := C.go_openssl_BN_is_prime_ex(z.bn, nChecks, newCtx.ctx, nil)
+ if ret == -1 {
+ return false, newOpenSSLError("BN_is_prime_ex")
+ }
+
+ return ret == 1, nil
+ } else {
+
+ ret := C.go_openssl_BN_check_prime(z.bn, newCtx.ctx, nil)
+ if ret == -1 {
+ return false, newOpenSSLError("BN_check_prime")
+ }
+
+ return ret == 1, nil
+ }
+}
+
+// Add sets z to the sum x+y and places the result in z.
+func (z *Int) Add(x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_add(z.bn, x.bn, y.bn)
+ if ret != 1 {
+ return newOpenSSLError("BN_add")
+ }
+
+ return nil
+}
+
+// Sub sets z to the difference x-y and places the result in z.
+func (z *Int) Sub(x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_sub(z.bn, x.bn, y.bn)
+ if ret != 1 {
+ return newOpenSSLError("BN_sub")
+ }
+
+ return nil
+}
+
+// Mul multiplies x and y and places the result in z.
+// For multiplication by powers of 2, use [Lsh].
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) Mul(ctx *IntContext, x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mul(z.bn, x.bn, y.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_mul")
+ }
+
+ return nil
+}
+
+// ModMul multiplies x by y and finds the nonnegative remainder respective to modulus m (z=(x*y) mod m).
+// For more efficient algorithms for repeated computations using the same modulus, see [ModMulMontgomery].
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) ModMul(ctx *IntContext, x, y, m *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mod_mul(z.bn, x.bn, y.bn, m.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_mod_mul")
+ }
+
+ return nil
+}
+
+// ModMulMontgomery implement Montgomery multiplication.
+// It computes Mont(x,y):=x*y*R^-1 and places the result in z.
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) ModMulMontgomery(mont *MontgomeryContext, ctx *IntContext, x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mod_mul_montgomery(z.bn, x.bn, y.bn, mont.ctx, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_mod_mul_montgomery")
+ }
+
+ return nil
+}
+
+// Div divides z by y and places the result in z.
+// For division by powers of 2, use [Rsh].
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) Div(ctx *IntContext, x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_div(z.bn, nil, x.bn, y.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_div")
+ }
+
+ return nil
+}
+
+// Exp raises x to the y-th power and places the result in z (z=x^y).
+// This function is faster than repeated applications of [Mul].
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) Exp(ctx *IntContext, x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_exp(z.bn, x.bn, y.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_exp")
+ }
+
+ return nil
+}
+
+// ModExp computes x to the y-th power modulo m (z=x^y % m).
+// This function uses less time and space than [Exp].
+// Do not call this function when m is even and any of the parameters
+// have the constant-time flag set.
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) ModExp(ctx *IntContext, x, y, m *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mod_exp(z.bn, x.bn, y.bn, m.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_mod_exp")
+ }
+
+ return nil
+}
+
+// ModExpMont computes z to the y-th power modulo m (z=x^y % m) using Montgomery multiplication.
+// mont is a Montgomery context and can be nil. In the case mont is nil, it will be initialized
+// within the function, so you can save time on initialization if you provide it in advance.
+// If any of the parameters x, y or m have the constant-time flag set, this function uses fixed
+// windows and the special precomputation memory layout to limit data-dependency to a minimum
+// to protect secret exponents.
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) ModExpMont(mont *MontgomeryContext, ctx *IntContext, x, y, m *Int) error {
+ var montCtx *C.GO_BN_MONT_CTX
+
+ if mont != nil {
+ montCtx = mont.ctx
+ }
+
+ var modulus *C.GO_BIGNUM
+ if m != nil {
+ modulus = m.bn
+ }
+
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mod_exp_mont(z.bn, x.bn, y.bn, modulus, ctx.ctx, montCtx)
+ if ret != 1 {
+ return newOpenSSLError("BN_mod_exp_mont")
+ }
+
+ return nil
+}
+
+// Mod sets z to the modulus x%y for y != 0.
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) Mod(ctx *IntContext, x, y *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mod(z.bn, x.bn, y.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_mod")
+ }
+
+ return nil
+}
+
+// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ.
+// ctx is a previously allocated IntContext used for temporary variables.
+func (z *Int) ModInverse(ctx *IntContext, g, n *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_mod_inverse(z.bn, g.bn, n.bn, ctx.ctx)
+ if ret == nil {
+ return newOpenSSLError("BN_mod_inverse")
+ }
+
+ return nil
+}
+
+// BitLen returns the length of the absolute value of z in bits. The bit length of 0 is 0.
+func (z *Int) BitLen() int {
+ if z.bn == nil {
+ return 0
+ }
+
+ return int(C.go_openssl_BN_num_bits(z.bn))
+}
+
+// BytesLen returns the size of z in bytes.
+func (z *Int) BytesLen() int {
+ if z.bn == nil {
+ return 0
+ }
+
+ return int(C.go_openssl_BN_num_bytes(z.bn))
+}
+
+// SetBytes interprets buf as the bytes of a big-endian unsigned integer, sets z to that value, and returns z.
+func (z *Int) SetBytes(buf []byte) *Int {
+ err := z.init()
+ if err != nil {
+ return z
+ }
+
+ C.go_openssl_BN_bin2bn((*C.uchar)(unsafe.Pointer(&buf[0])), C.int(len(buf)), z.bn)
+ return z
+}
+
+// SetDecString sets z to the value of s interpreted in the decimal base.
+func (z *Int) SetDecString(s string) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ str := C.CString(s)
+ defer C.free(unsafe.Pointer(str))
+
+ ret := C.go_openssl_BN_dec2bn(&z.bn, str)
+ if ret == C.int(0) {
+ return newOpenSSLError("BN_dec2bn")
+ } else if ret < C.int(len(s)) {
+ return ErrInvalidParse
+ }
+
+ return nil
+}
+
+// SetHexString sets z to the value of s interpreted in the hexadecimal base.
+func (z *Int) SetHexString(s string) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ str := C.CString(s)
+ defer C.free(unsafe.Pointer(str))
+
+ ret := C.go_openssl_BN_hex2bn(&z.bn, str)
+ if ret == 0 {
+ return newOpenSSLError("BN_hex2bn")
+ } else if ret < C.int(len(s)) {
+ return ErrInvalidParse
+ }
+
+ return nil
+}
+
+// SetUInt64 sets z to the value of x.
+func (z *Int) SetUInt64(x uint64) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ok := C.go_openssl_BN_set_word(z.bn, C.GO_BN_ULONG(x))
+ if ok != 1 {
+ return newOpenSSLError("BN_set_word")
+ }
+
+ return nil
+}
+
+// Bytes sets buf to the absolute value of z as a big-endian byte slice.
+// If the absolute value of z doesn't fit in buf, FillBytes will panic.
+func (z *Int) FillBytes(buf []byte) error {
+ bytesLen := z.BytesLen()
+ bufLen := len(buf)
+
+ if bufLen < bytesLen {
+ panic("bn.Int: buffer too small to fit value")
+ }
+
+ if isGeq11() {
+ ret := C.go_openssl_BN_bn2binpad(z.bn, (*C.uchar)(unsafe.Pointer(&buf[0])), C.int(bufLen))
+ if ret != C.int(bufLen) {
+ return newOpenSSLError("BN_bn2binpad")
+ }
+ } else {
+ ret := C.go_openssl_BN_bn2bin(z.bn, (*C.uchar)(unsafe.Pointer(&buf[0])))
+ if ret != C.int(bufLen) {
+ return newOpenSSLError("BN_bn2bin")
+ }
+ }
+
+ return nil
+}
+
+// Bytes returns the absolute value of z as a big-endian byte slice.
+// To use a fixed length slice, or a preallocated one, use [FillBytes].
+func (z *Int) Bytes() ([]byte, error) {
+ bytesLen := z.BytesLen()
+ buf := make([]byte, bytesLen)
+
+ if bytesLen <= 0 {
+ return buf, nil
+ }
+
+ if isGeq11() {
+ ret := C.go_openssl_BN_bn2binpad(z.bn, (*C.uchar)(unsafe.Pointer(&buf[0])), C.int(bytesLen))
+ if ret != C.int(bytesLen) {
+ return nil, newOpenSSLError("BN_bn2binpad")
+ }
+ } else {
+ ret := C.go_openssl_BN_bn2bin(z.bn, (*C.uchar)(unsafe.Pointer(&buf[0])))
+ if ret != C.int(bytesLen) {
+ return nil, newOpenSSLError("BN_bn2bin")
+ }
+ }
+
+ return buf, nil
+}
+
+// Lsh shifts x left by n bits and places the result in z (z=x*2^n).
+// Note that n must be nonnegative.
+func (z *Int) Lsh(x *Int, n uint) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_lshift(z.bn, x.bn, C.int(n))
+ if ret != 1 {
+ return newOpenSSLError("BN_lshift")
+ }
+
+ return nil
+}
+
+// Rsh shifts x right by n bits and places the result in z (z=x/2^n).
+// Note that n must be nonnegative.
+func (z *Int) Rsh(x *Int, n uint) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_rshift(z.bn, x.bn, C.int(n))
+ if ret != 1 {
+ return newOpenSSLError("BN_rshift")
+ }
+
+ return nil
+}
+
+// Or sets z = x | y.
+func (z *Int) Or(x, y *Int) error {
+ max, min := x, y
+
+ maxLen, minLen := max.BytesLen(), min.BytesLen()
+
+ if maxLen < minLen {
+ max, min = y, x
+ maxLen, minLen = max.BytesLen(), min.BytesLen()
+ }
+
+ minBytes, err := min.Bytes()
+ if err != nil {
+ return err
+ }
+
+ maxBytes, err := max.Bytes()
+ if err != nil {
+ return err
+ }
+
+ zBytes := make([]byte, maxLen)
+
+ offset := maxLen - minLen
+ for i, j := offset, 0; i < maxLen; i, j = i+1, j+1 {
+ zBytes[i] = minBytes[j] | maxBytes[i]
+ }
+ copy(zBytes[0:offset], maxBytes[0:offset])
+
+ z.SetBytes(zBytes)
+
+ return nil
+}
+
+// And sets z = x & y.
+func (z *Int) And(x, y *Int) error {
+ max, min := x, y
+
+ maxLen, minLen := max.BytesLen(), min.BytesLen()
+
+ if maxLen < minLen {
+ max, min = y, x
+ maxLen, minLen = max.BytesLen(), min.BytesLen()
+ }
+
+ minBytes, err := min.Bytes()
+ if err != nil {
+ return err
+ }
+
+ maxBytes, err := max.Bytes()
+ if err != nil {
+ return err
+ }
+
+ zBytes := make([]byte, maxLen)
+
+ offset := maxLen - minLen
+ for i, j := offset, 0; i < maxLen; i, j = i+1, j+1 {
+ zBytes[i] = minBytes[j] & maxBytes[i]
+ }
+
+ z.SetBytes(zBytes)
+
+ return nil
+}
+
+// Uint64 returns the uint64 representation of z. If z cannot be represented in a uint64, the function
+// returns [math.MaxUint64].
+func (z *Int) Uint64() uint64 {
+ if z.bn == nil {
+ return math.MaxUint64
+ }
+
+ return uint64(C.go_openssl_BN_get_word(z.bn))
+}
+
+// Set sets z to x.
+func (z *Int) Set(x *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_copy(z.bn, x.bn)
+ if ret == nil {
+ return newOpenSSLError("BN_copy")
+ }
+
+ return nil
+}
+
+// RandRange generates a cryptographically strong pseudo-random number z in
+// the range 0 <= z < max.
+func (z *Int) RandRange(max *Int) error {
+ err := z.init()
+ if err != nil {
+ return err
+ }
+
+ ret := C.go_openssl_BN_rand_range(z.bn, max.bn)
+ if ret != 1 {
+ return newOpenSSLError("BN_rand_range")
+ }
+
+ return nil
+}
+
+// Cmp compares z and x and returns:
+//
+// -1 if z < x
+// 0 if z == x
+// +1 if z > x
+func (z *Int) Cmp(x *Int) int {
+ return int(C.go_openssl_BN_cmp(z.bn, x.bn))
+}
+
+// ConstantTimeEq compares z and x and returns true if they are equal, false otherwise.
+// The time taken is a function of the bytes length of the numbers and is independent
+// of the contents.
+func (z *Int) ConstantTimeEq(x *Int) (bool, error) {
+ zBytes, err := z.Bytes()
+ if err != nil {
+ return false, err
+ }
+
+ xBytes, err := x.Bytes()
+ if err != nil {
+ return false, err
+ }
+
+ return subtle.ConstantTimeCompare(zBytes, xBytes) == 1, nil
+}
diff --git a/big/bn_test.go b/big/bn_test.go
new file mode 100644
index 0000000..17c988f
--- /dev/null
+++ b/big/bn_test.go
@@ -0,0 +1,126 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+package big_test
+
+import (
+ "testing"
+
+ "github.com/matteoarella/pedersen/big"
+ "github.com/stretchr/testify/require"
+)
+
+func TestBnValid(t *testing.T) {
+ t.Run("valid bn", func(t *testing.T) {
+ a, err := big.NewInt()
+ require.NoError(t, err)
+ err = a.SetUInt64(1)
+ require.NoError(t, err)
+
+ b, err := big.NewInt()
+ require.NoError(t, err)
+ err = b.SetUInt64(2)
+ require.NoError(t, err)
+
+ sum, err := big.NewInt()
+ require.NoError(t, err)
+ err = sum.SetUInt64(3)
+ require.NoError(t, err)
+
+ s, err := big.NewInt()
+ require.NoError(t, err)
+ err = s.Set(sum)
+ require.NoError(t, err)
+
+ err = b.Add(a, b)
+ require.NoError(t, err)
+
+ require.Equal(t, 0, sum.Cmp(b))
+ require.Equal(t, 0, sum.Cmp(s))
+
+ dec, err := big.NewInt()
+ require.NoError(t, err)
+
+ err = dec.SetDecString("10")
+ require.NoError(t, err)
+
+ expected, err := big.NewInt()
+ require.NoError(t, err)
+ err = expected.SetUInt64(10)
+ require.NoError(t, err)
+
+ require.Equal(t, 0, dec.Cmp(expected))
+
+ mask, err := big.NewInt()
+ require.NoError(t, err)
+ err = mask.SetUInt64(5)
+ require.NoError(t, err)
+
+ ctx, err := big.NewIntContext()
+ require.NoError(t, err)
+
+ err = mask.Mod(ctx, mask, sum)
+ require.NoError(t, err)
+
+ err = expected.SetUInt64(3)
+ require.NoError(t, err)
+
+ err = a.SetUInt64(2)
+ require.NoError(t, err)
+ err = b.SetUInt64(3)
+ require.NoError(t, err)
+ err = s.SetUInt64(5)
+ require.NoError(t, err)
+
+ err = a.ModExp(ctx, a, b, s)
+ require.NoError(t, err)
+
+ require.Equal(t, 0, expected.Cmp(a))
+ })
+}
+
+func TestExpMontValid(t *testing.T) {
+ t.Run("valid exp mont bn", func(t *testing.T) {
+ a, err := big.NewInt()
+ require.NoError(t, err)
+ err = a.SetUInt64(2)
+ require.NoError(t, err)
+
+ b, err := big.NewInt()
+ require.NoError(t, err)
+ err = b.SetUInt64(3)
+ require.NoError(t, err)
+
+ m, err := big.NewInt()
+ require.NoError(t, err)
+ err = m.SetUInt64(5)
+ require.NoError(t, err)
+
+ ctx, err := big.NewIntContext()
+ require.NoError(t, err)
+ defer ctx.Destroy()
+
+ mont, err := big.NewMontgomeryContext()
+ require.NoError(t, err)
+ defer mont.Destroy()
+
+ err = mont.Set(m, ctx)
+ require.NoError(t, err)
+
+ res, err := big.NewInt()
+ require.NoError(t, err)
+
+ err = res.ModExpMont(mont, ctx, a, b, m)
+ require.NoError(t, err)
+
+ expected, err := big.NewInt()
+ require.NoError(t, err)
+ err = expected.SetUInt64(3)
+ require.NoError(t, err)
+
+ require.Equal(t, 0, res.Cmp(expected))
+ })
+}
diff --git a/big/context.go b/big/context.go
new file mode 100644
index 0000000..546a06d
--- /dev/null
+++ b/big/context.go
@@ -0,0 +1,142 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+package big
+
+// #include "goopenssl.h"
+import "C"
+import (
+ "runtime"
+)
+
+// A IntContext is a structure that holds [Int] temporary variables
+// used by library functions.
+// Since dynamic memory allocation to create [Int]s is rather expensive
+// when used in conjunction with repeated subroutine calls,
+// the IntContext structure is used.
+// A given IntContext must only be used by a single thread of execution.
+// No locking is performed, and the internal pool allocator will not properly
+// handle multiple threads of execution.
+type IntContext struct {
+ ctx *C.GO_BN_CTX
+}
+
+type MontgomeryContext struct {
+ ctx *C.GO_BN_MONT_CTX
+}
+
+func finalizeIntContext(bnCtx *IntContext) {
+ if bnCtx.ctx == nil {
+ return
+ }
+
+ C.go_openssl_BN_CTX_free(bnCtx.ctx)
+
+ bnCtx.ctx = nil
+}
+
+func wrapIntContext(ctx *C.GO_BN_CTX) *IntContext {
+ c := &IntContext{
+ ctx: ctx,
+ }
+
+ runtime.SetFinalizer(c, finalizeIntContext)
+
+ return c
+}
+
+// NewIntContext allocates and initializes a IntContext structure
+func NewIntContext() (*IntContext, error) {
+ var ctx *C.GO_BN_CTX
+
+ switch {
+ case is30():
+ ctx = C.go_openssl_BN_CTX_secure_new_ex(nil)
+ if ctx == nil {
+ return nil, newOpenSSLError("BN_CTX_secure_new_ex")
+ }
+ case is11():
+ ctx = C.go_openssl_BN_CTX_secure_new()
+ if ctx == nil {
+ return nil, newOpenSSLError("BN_CTX_secure_new")
+ }
+ default:
+ ctx = C.go_openssl_BN_CTX_new()
+ if ctx == nil {
+ return nil, newOpenSSLError("BN_CTX_new")
+ }
+ }
+
+ return wrapIntContext(ctx), nil
+}
+
+func (c *IntContext) Attach() {
+ C.go_openssl_BN_CTX_start(c.ctx)
+}
+
+func (c *IntContext) Detach() {
+ C.go_openssl_BN_CTX_end(c.ctx)
+}
+
+func (c *IntContext) GetInt() (*Int, error) {
+ bn := C.go_openssl_BN_CTX_get(c.ctx)
+ if bn == nil {
+ return nil, newOpenSSLError("BN_CTX_get")
+ }
+
+ i := &Int{
+ bn: bn,
+ }
+
+ return i.wrapInt(false), nil
+}
+
+// Destroy frees the components of the IntContext and the structure itself.
+func (c *IntContext) Destroy() {
+ finalizeIntContext(c)
+}
+
+func finalizeMontgomeryContext(montCtx *MontgomeryContext) {
+ if montCtx.ctx == nil {
+ return
+ }
+
+ C.go_openssl_BN_MONT_CTX_free(montCtx.ctx)
+
+ montCtx.ctx = nil
+}
+
+func wrapMontgomeryContext(ctx *C.GO_BN_MONT_CTX) *MontgomeryContext {
+ c := &MontgomeryContext{
+ ctx: ctx,
+ }
+
+ runtime.SetFinalizer(c, finalizeMontgomeryContext)
+
+ return c
+}
+
+func NewMontgomeryContext() (*MontgomeryContext, error) {
+ ctx := C.go_openssl_BN_MONT_CTX_new()
+ if ctx == nil {
+ return nil, newOpenSSLError("BN_MONT_CTX_new")
+ }
+
+ return wrapMontgomeryContext(ctx), nil
+}
+
+func (c *MontgomeryContext) Set(m *Int, ctx *IntContext) error {
+ ret := C.go_openssl_BN_MONT_CTX_set(c.ctx, m.bn, ctx.ctx)
+ if ret != 1 {
+ return newOpenSSLError("BN_MONT_CTX_set")
+ }
+
+ return nil
+}
+
+func (c *MontgomeryContext) Destroy() {
+ finalizeMontgomeryContext(c)
+}
diff --git a/big/goopenssl.c b/big/goopenssl.c
new file mode 100644
index 0000000..a385dcf
--- /dev/null
+++ b/big/goopenssl.c
@@ -0,0 +1,267 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+// go:build !openssldev
+// +build !openssldev
+
+#include "goopenssl.h"
+
+#include
+#include
+
+static unsigned long
+version_num(void *handle)
+{
+ unsigned long (*fn)(void);
+ // OPENSSL_version_num is defined in OpenSSL 1.1.0 and 1.1.1.
+ fn = (unsigned long (*)(void))dlsym(handle, "OpenSSL_version_num");
+ if (fn != NULL)
+ return fn();
+
+ // SSLeay is defined in OpenSSL 1.0.2.
+ fn = (unsigned long (*)(void))dlsym(handle, "SSLeay");
+ if (fn != NULL)
+ return fn();
+
+ return 0;
+}
+
+int go_openssl_version_major(void *handle)
+{
+ unsigned int (*fn)(void);
+ // OPENSSL_version_major is supported since OpenSSL 3.
+ fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_major");
+ if (fn != NULL)
+ return (int)fn();
+
+ // If OPENSSL_version_major is not defined, try with OpenSSL 1 functions.
+ unsigned long num = version_num(handle);
+ if (num < 0x10000000L || num >= 0x20000000L)
+ return -1;
+
+ return 1;
+}
+
+int go_openssl_version_minor(void *handle)
+{
+ unsigned int (*fn)(void);
+ // OPENSSL_version_major is supported since OpenSSL 3.
+ fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_minor");
+ if (fn != NULL)
+ return (int)fn();
+
+ // If OPENSSL_version_major is not defined, try with OpenSSL 1 functions.
+ unsigned long num = version_num(handle);
+ // OpenSSL version number follows this schema:
+ // MNNFFPPS: major minor fix patch status.
+ if (num < 0x10000000L || num >= 0x10200000L)
+ {
+ // We only support minor version 0 and 1,
+ // so there is no need to implement an algorithm
+ // that decodes the version number into individual components.
+ return -1;
+ }
+
+ if (num >= 0x10100000L)
+ return 1;
+
+ return 0;
+}
+
+// Approach taken from .Net System.Security.Cryptography.Native
+// https://github.com/dotnet/runtime/blob/f64246ce08fb7a58221b2b7c8e68f69c02522b0d/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.c
+
+#define DEFINEFUNC(ret, func, args, argscall) ret(*_g_##func) args;
+#define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall, res) DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_LEGACY_1(ret, func, args, argscall, res) DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_1_1(ret, func, args, argscall, res) DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_3_0(ret, func, args, argscall, res) DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) DEFINEFUNC(ret, func, args, argscall)
+
+FOR_ALL_OPENSSL_FUNCTIONS
+
+#undef DEFINEFUNC
+#undef DEFINEFUNC_LEGACY_1_0
+#undef DEFINEFUNC_LEGACY_1
+#undef DEFINEFUNC_1_1
+#undef DEFINEFUNC_3_0
+#undef DEFINEFUNC_RENAMED_1_1
+#undef DEFINEFUNC_RENAMED_3_0
+
+// Load all the functions stored in FOR_ALL_OPENSSL_FUNCTIONS
+// and assign them to their corresponding function pointer
+// defined in goopenssl.h.
+void go_openssl_load_functions(void *handle, int major, int minor)
+{
+#define DEFINEFUNC_INTERNAL(name, func) \
+ _g_##name = dlsym(handle, func); \
+ if (_g_##name == NULL) \
+ { \
+ fprintf(stderr, "Cannot get required symbol " #func " from libcrypto version %d.%d\n", major, minor); \
+ abort(); \
+ }
+#define DEFINEFUNC(ret, func, args, argscall) \
+ DEFINEFUNC_INTERNAL(func, #func)
+#define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall, res) \
+ if (major == 1 && minor == 0) \
+ { \
+ DEFINEFUNC_INTERNAL(func, #func) \
+ }
+#define DEFINEFUNC_LEGACY_1(ret, func, args, argscall, res) \
+ if (major == 1) \
+ { \
+ DEFINEFUNC_INTERNAL(func, #func) \
+ }
+#define DEFINEFUNC_1_1(ret, func, args, argscall, res) \
+ if (major == 3 || (major == 1 && minor == 1)) \
+ { \
+ DEFINEFUNC_INTERNAL(func, #func) \
+ }
+#define DEFINEFUNC_3_0(ret, func, args, argscall, res) \
+ if (major == 3) \
+ { \
+ DEFINEFUNC_INTERNAL(func, #func) \
+ }
+#define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
+ if (major == 1 && minor == 0) \
+ { \
+ DEFINEFUNC_INTERNAL(func, #oldfunc) \
+ } \
+ else \
+ { \
+ DEFINEFUNC_INTERNAL(func, #func) \
+ }
+#define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
+ if (major == 1) \
+ { \
+ DEFINEFUNC_INTERNAL(func, #oldfunc) \
+ } \
+ else \
+ { \
+ DEFINEFUNC_INTERNAL(func, #func) \
+ }
+
+ FOR_ALL_OPENSSL_FUNCTIONS
+
+#undef DEFINEFUNC
+#undef DEFINEFUNC_LEGACY_1_0
+#undef DEFINEFUNC_LEGACY_1
+#undef DEFINEFUNC_1_1
+#undef DEFINEFUNC_3_0
+#undef DEFINEFUNC_RENAMED_1_1
+#undef DEFINEFUNC_RENAMED_3_0
+}
+
+// LEGACY
+struct bignum_st
+{
+ GO_BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit
+ * chunks. */
+ int top; /* Index of last used d +1. */
+ /* The next are internal book keeping for bn_expand. */
+ int dmax; /* Size of the d array. */
+ int neg; /* one if the number is negative */
+ int flags;
+};
+
+#define LEGACY_1_0_BN_set_flags(b, n) ((b)->flags |= (n))
+#define LEGACY_1_0_BN_get_flags(b, n) ((b)->flags & (n))
+
+void legacy_1_0_BN_set_flags(GO_BIGNUM *arg0, int arg1)
+{
+ LEGACY_1_0_BN_set_flags(arg0, arg1);
+}
+
+int legacy_1_0_BN_get_flags(const GO_BIGNUM *arg0, int arg1)
+{
+ return LEGACY_1_0_BN_get_flags(arg0, arg1);
+}
+
+int go_openssl_BN_num_bytes(const GO_BIGNUM *a)
+{
+ return (go_openssl_BN_num_bits(a) + 7) / 8;
+}
+
+int go_openssl_BN_mod(GO_BIGNUM *rem, const GO_BIGNUM *a, const GO_BIGNUM *m, GO_BN_CTX *ctx)
+{
+ return go_openssl_BN_div(NULL, rem, a, m, ctx);
+}
+
+/*
+ * BN_prime_checks_for_size() returns the number of Miller-Rabin iterations
+ * that will be done for checking that a random number is probably prime. The
+ * error rate for accepting a composite number as prime depends on the size of
+ * the prime |b|. The error rates used are for calculating an RSA key with 2 primes,
+ * and so the level is what you would expect for a key of double the size of the
+ * prime.
+ *
+ * This table is generated using the algorithm of FIPS PUB 186-4
+ * Digital Signature Standard (DSS), section F.1, page 117.
+ * (https://dx.doi.org/10.6028/NIST.FIPS.186-4)
+ *
+ * The following magma script was used to generate the output:
+ * securitybits:=125;
+ * k:=1024;
+ * for t:=1 to 65 do
+ * for M:=3 to Floor(2*Sqrt(k-1)-1) do
+ * S:=0;
+ * // Sum over m
+ * for m:=3 to M do
+ * s:=0;
+ * // Sum over j
+ * for j:=2 to m do
+ * s+:=(RealField(32)!2)^-(j+(k-1)/j);
+ * end for;
+ * S+:=2^(m-(m-1)*t)*s;
+ * end for;
+ * A:=2^(k-2-M*t);
+ * B:=8*(Pi(RealField(32))^2-6)/3*2^(k-2)*S;
+ * pkt:=2.00743*Log(2)*k*2^-k*(A+B);
+ * seclevel:=Floor(-Log(2,pkt));
+ * if seclevel ge securitybits then
+ * printf "k: %5o, security: %o bits (t: %o, M: %o)\n",k,seclevel,t,M;
+ * break;
+ * end if;
+ * end for;
+ * if seclevel ge securitybits then break; end if;
+ * end for;
+ *
+ * It can be run online at:
+ * http://magma.maths.usyd.edu.au/calc
+ *
+ * And will output:
+ * k: 1024, security: 129 bits (t: 6, M: 23)
+ *
+ * k is the number of bits of the prime, securitybits is the level we want to
+ * reach.
+ *
+ * prime length | RSA key size | # MR tests | security level
+ * -------------+--------------|------------+---------------
+ * (b) >= 6394 | >= 12788 | 3 | 256 bit
+ * (b) >= 3747 | >= 7494 | 3 | 192 bit
+ * (b) >= 1345 | >= 2690 | 4 | 128 bit
+ * (b) >= 1080 | >= 2160 | 5 | 128 bit
+ * (b) >= 852 | >= 1704 | 5 | 112 bit
+ * (b) >= 476 | >= 952 | 5 | 80 bit
+ * (b) >= 400 | >= 800 | 6 | 80 bit
+ * (b) >= 347 | >= 694 | 7 | 80 bit
+ * (b) >= 308 | >= 616 | 8 | 80 bit
+ * (b) >= 55 | >= 110 | 27 | 64 bit
+ * (b) >= 6 | >= 12 | 34 | 64 bit
+ */
+#define BN_prime_checks_for_size(b) ((b) >= 3747 ? 3 : (b) >= 1345 ? 4 \
+ : (b) >= 476 ? 5 \
+ : (b) >= 400 ? 6 \
+ : (b) >= 347 ? 7 \
+ : (b) >= 308 ? 8 \
+ : (b) >= 55 ? 27 \
+ : /* b >= 6 */ 34)
+
+int go_openssl_BN_prime_checks_for_size(int size)
+{
+ return BN_prime_checks_for_size(size);
+}
diff --git a/big/goopenssl.h b/big/goopenssl.h
new file mode 100644
index 0000000..41b3655
--- /dev/null
+++ b/big/goopenssl.h
@@ -0,0 +1,68 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+#ifndef GOOPENSSL_H
+#define GOOPENSSL_H
+
+// This header file describes the OpenSSL ABI as built for use in Go.
+
+#include "openssl_funcs.h"
+
+int go_openssl_thread_setup(void);
+
+// Define pointers to all the used OpenSSL functions.
+// Calling C function pointers from Go is currently not supported.
+// It is possible to circumvent this by using a C function wrapper.
+// https://pkg.go.dev/cmd/cgo
+#ifndef GO_OPENSSL_DEV
+int go_openssl_version_major(void *handle);
+int go_openssl_version_minor(void *handle);
+void go_openssl_load_functions(void *handle, int major, int minor);
+
+#define DEFINEFUNC(ret, func, args, argscall) \
+ extern ret(*_g_##func) args; \
+ static inline ret go_openssl_##func args \
+ { \
+ return _g_##func argscall; \
+ }
+#else /* GO_OPENSSL_DEV */
+int go_openssl_version_major(void);
+int go_openssl_version_minor(void);
+
+#define DEFINEFUNC(ret, func, args, argscall) \
+ extern ret go_openssl_##func args;
+#endif /* GO_OPENSSL_DEV */
+
+#define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_LEGACY_1(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_1_1(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_3_0(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
+ DEFINEFUNC(ret, func, args, argscall)
+#define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
+ DEFINEFUNC(ret, func, args, argscall)
+
+FOR_ALL_OPENSSL_FUNCTIONS
+
+#undef DEFINEFUNC
+#undef DEFINEFUNC_LEGACY_1_0
+#undef DEFINEFUNC_LEGACY_1
+#undef DEFINEFUNC_1_1
+#undef DEFINEFUNC_3_0
+#undef DEFINEFUNC_RENAMED_1_1
+#undef DEFINEFUNC_RENAMED_3_0
+
+void legacy_1_0_BN_set_flags(GO_BIGNUM *arg0, int arg1);
+
+int go_openssl_BN_num_bytes(const GO_BIGNUM *a);
+int go_openssl_BN_mod(GO_BIGNUM *rem, const GO_BIGNUM *a, const GO_BIGNUM *m, GO_BN_CTX *ctx);
+int go_openssl_BN_prime_checks_for_size(int size);
+
+#endif /* GOOPENSSL_H */
diff --git a/big/goopenssl_dev.c b/big/goopenssl_dev.c
new file mode 100644
index 0000000..1f89a0e
--- /dev/null
+++ b/big/goopenssl_dev.c
@@ -0,0 +1,193 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+// go:build openssldev
+// +build openssldev
+
+#include "goopenssl.h"
+
+#include
+
+int go_openssl_version_major(void)
+{
+ return OPENSSL_VERSION_MAJOR;
+}
+
+int go_openssl_version_minor(void)
+{
+ return OPENSSL_VERSION_MINOR;
+}
+
+#define DEFINEFUNC(ret, func, args, argscall) \
+ inline ret go_openssl_##func args \
+ { \
+ return func argscall; \
+ }
+
+#define DEFINEFUNC_RENAMED(ret, func, renamed, args, argscall) \
+ inline ret go_openssl_##func args \
+ { \
+ return renamed argscall; \
+ }
+
+#define DEFINEFUNC_DUMMY(ret, func, args, argscall, res) \
+ inline ret go_openssl_##func args \
+ { \
+ fprintf(stderr, "Cannot get required symbol " #func " from libcrypto\n"); \
+ abort(); \
+ return res; \
+ }
+
+#if OPENSSL_VERSION_MAJOR == 1 && OPENSSL_VERSION_MINOR == 0
+#define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#else
+#define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall, res) \
+ DEFINEFUNC_DUMMY(ret, func, args, argscall, res)
+#endif
+
+#if OPENSSL_VERSION_MAJOR == 1
+#define DEFINEFUNC_LEGACY_1(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#else
+#define DEFINEFUNC_LEGACY_1(ret, func, args, argscall, res) \
+ DEFINEFUNC_DUMMY(ret, func, args, argscall, res)
+#endif
+
+#if (OPENSSL_VERSION_MAJOR == 3 || (OPENSSL_VERSION_MAJOR == 1 && OPENSSL_VERSION_MINOR == 1))
+#define DEFINEFUNC_1_1(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#else
+#define DEFINEFUNC_1_1(ret, func, args, argscall, res) \
+ DEFINEFUNC_DUMMY(ret, func, args, argscall, res)
+#endif
+
+#if OPENSSL_VERSION_MAJOR == 3
+#define DEFINEFUNC_3_0(ret, func, args, argscall, res) \
+ DEFINEFUNC(ret, func, args, argscall)
+#else
+#define DEFINEFUNC_3_0(ret, func, args, argscall, res) \
+ DEFINEFUNC_DUMMY(ret, func, args, argscall, res)
+#endif
+
+#if OPENSSL_VERSION_MAJOR == 1 && OPENSSL_VERSION_MINOR == 0
+#define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
+ DEFINEFUNC_RENAMED(ret, func, oldfunc, args, argscall)
+#else
+#define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
+ DEFINEFUNC(ret, func, args, argscall)
+#endif
+
+#if OPENSSL_VERSION_MAJOR == 1
+#define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
+ DEFINEFUNC_RENAMED(ret, func, oldfunc, args, argscall)
+#else
+#define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
+ DEFINEFUNC(ret, func, args, argscall)
+#endif
+
+FOR_ALL_OPENSSL_FUNCTIONS
+
+#undef DEFINEFUNC
+#undef DEFINEFUNC_RENAMED
+#undef DEFINEFUNC_DUMMY
+#undef DEFINEFUNC_LEGACY_1_0
+#undef DEFINEFUNC_LEGACY_1
+#undef DEFINEFUNC_1_1
+#undef DEFINEFUNC_3_0
+#undef DEFINEFUNC_RENAMED_1_1
+#undef DEFINEFUNC_RENAMED_3_0
+
+// LEGACY
+void legacy_1_0_BN_set_flags(GO_BIGNUM *arg0, int arg1)
+{
+ BN_set_flags(arg0, arg1);
+}
+
+int go_openssl_BN_num_bytes(const GO_BIGNUM *a)
+{
+ return (go_openssl_BN_num_bits(a) + 7) / 8;
+}
+
+int go_openssl_BN_mod(GO_BIGNUM *rem, const GO_BIGNUM *a, const GO_BIGNUM *m, GO_BN_CTX *ctx)
+{
+ return go_openssl_BN_div(NULL, rem, a, m, ctx);
+}
+
+/*
+ * BN_prime_checks_for_size() returns the number of Miller-Rabin iterations
+ * that will be done for checking that a random number is probably prime. The
+ * error rate for accepting a composite number as prime depends on the size of
+ * the prime |b|. The error rates used are for calculating an RSA key with 2 primes,
+ * and so the level is what you would expect for a key of double the size of the
+ * prime.
+ *
+ * This table is generated using the algorithm of FIPS PUB 186-4
+ * Digital Signature Standard (DSS), section F.1, page 117.
+ * (https://dx.doi.org/10.6028/NIST.FIPS.186-4)
+ *
+ * The following magma script was used to generate the output:
+ * securitybits:=125;
+ * k:=1024;
+ * for t:=1 to 65 do
+ * for M:=3 to Floor(2*Sqrt(k-1)-1) do
+ * S:=0;
+ * // Sum over m
+ * for m:=3 to M do
+ * s:=0;
+ * // Sum over j
+ * for j:=2 to m do
+ * s+:=(RealField(32)!2)^-(j+(k-1)/j);
+ * end for;
+ * S+:=2^(m-(m-1)*t)*s;
+ * end for;
+ * A:=2^(k-2-M*t);
+ * B:=8*(Pi(RealField(32))^2-6)/3*2^(k-2)*S;
+ * pkt:=2.00743*Log(2)*k*2^-k*(A+B);
+ * seclevel:=Floor(-Log(2,pkt));
+ * if seclevel ge securitybits then
+ * printf "k: %5o, security: %o bits (t: %o, M: %o)\n",k,seclevel,t,M;
+ * break;
+ * end if;
+ * end for;
+ * if seclevel ge securitybits then break; end if;
+ * end for;
+ *
+ * It can be run online at:
+ * http://magma.maths.usyd.edu.au/calc
+ *
+ * And will output:
+ * k: 1024, security: 129 bits (t: 6, M: 23)
+ *
+ * k is the number of bits of the prime, securitybits is the level we want to
+ * reach.
+ *
+ * prime length | RSA key size | # MR tests | security level
+ * -------------+--------------|------------+---------------
+ * (b) >= 6394 | >= 12788 | 3 | 256 bit
+ * (b) >= 3747 | >= 7494 | 3 | 192 bit
+ * (b) >= 1345 | >= 2690 | 4 | 128 bit
+ * (b) >= 1080 | >= 2160 | 5 | 128 bit
+ * (b) >= 852 | >= 1704 | 5 | 112 bit
+ * (b) >= 476 | >= 952 | 5 | 80 bit
+ * (b) >= 400 | >= 800 | 6 | 80 bit
+ * (b) >= 347 | >= 694 | 7 | 80 bit
+ * (b) >= 308 | >= 616 | 8 | 80 bit
+ * (b) >= 55 | >= 110 | 27 | 64 bit
+ * (b) >= 6 | >= 12 | 34 | 64 bit
+ */
+#define BN_prime_checks_for_size(b) ((b) >= 3747 ? 3 : (b) >= 1345 ? 4 \
+ : (b) >= 476 ? 5 \
+ : (b) >= 400 ? 6 \
+ : (b) >= 347 ? 7 \
+ : (b) >= 308 ? 8 \
+ : (b) >= 55 ? 27 \
+ : /* b >= 6 */ 34)
+
+int go_openssl_BN_prime_checks_for_size(int size)
+{
+ return BN_prime_checks_for_size(size);
+}
diff --git a/big/openssl.go b/big/openssl.go
new file mode 100644
index 0000000..c43c69a
--- /dev/null
+++ b/big/openssl.go
@@ -0,0 +1,183 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+//go:build !openssldev
+// +build !openssldev
+
+package big
+
+// #include "goopenssl.h"
+// #include
+// #cgo LDFLAGS: -ldl
+import "C"
+import (
+ "errors"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ initOnce sync.Once
+ // errInit is set when first calling Init().
+ errInit error
+ // vMajor and vMinor hold the major/minor OpenSSL version.
+ // It is only populated if Init has been called.
+ vMajor, vMinor int
+)
+
+// knownVersions is a list of supported and well-known libcrypto.so suffixes in decreasing version order.
+//
+// FreeBSD library version numbering does not directly align to the version of OpenSSL.
+// Its preferred search order is 11 -> 111.
+//
+// Some distributions use 1.0.0 and others (such as Debian) 1.0.2 to refer to the same OpenSSL 1.0.2 version.
+//
+// Fedora derived distros use different naming for the version 1.0.x.
+var knownVersions = [...]string{"3", "1.1", "11", "111", "1.0.2", "1.0.0", "10"}
+
+func errUnsuportedVersion() error {
+ return errors.New("openssl: OpenSSL version: " + strconv.Itoa(vMajor) + "." + strconv.Itoa(vMinor) + " is not supported")
+}
+
+// Init loads and initializes OpenSSL.
+// It must be called before any other OpenSSL call.
+//
+// Only the first call to Init is effective,
+// subsequent calls will return the same error result as the one from the first call.
+//
+// If GO_OPENSSL_VERSION_OVERRIDE environment variable is not empty, its value will be appended to the OpenSSL shared library name
+// as a version suffix when calling dlopen. For example, "GO_OPENSSL_VERSION_OVERRIDE=1.1.1k-fips"
+// makes Init look for the shared library libcrypto.so.1.1.1k-fips.
+// If GO_OPENSSL_VERSION_OVERRIDE environment variable is empty, Init will try to load the OpenSSL shared library
+// using a list if supported and well-known version suffixes, going from higher to lower versions.
+func Init() error {
+ initOnce.Do(func() {
+ version, _ := syscall.Getenv("GO_OPENSSL_VERSION_OVERRIDE")
+ handle, err := loadLibrary(version)
+ if err != nil {
+ errInit = err
+ return
+ }
+
+ vMajor = int(C.go_openssl_version_major(handle))
+ vMinor = int(C.go_openssl_version_minor(handle))
+ if vMajor == -1 || vMinor == -1 {
+ errInit = errors.New("openssl: can't retrieve OpenSSL version")
+ return
+ }
+ var supported bool
+ if vMajor == 1 {
+ supported = vMinor == 0 || vMinor == 1
+ } else if vMajor == 3 {
+ // OpenSSL team guarantees API and ABI compatibility within the same major version since OpenSSL 3.
+ supported = true
+ }
+ if !supported {
+ errInit = errUnsuportedVersion()
+ return
+ }
+
+ C.go_openssl_load_functions(handle, C.int(vMajor), C.int(vMinor))
+ C.go_openssl_OPENSSL_init()
+ if vMajor == 1 && vMinor == 0 {
+ if C.go_openssl_thread_setup() != 1 {
+ errInit = newOpenSSLError("openssl: thread setup")
+ return
+ }
+ C.go_openssl_ERR_load_crypto_strings()
+ } else {
+ flags := C.uint64_t(C.GO_OPENSSL_INIT_LOAD_CONFIG | C.GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS)
+ if C.go_openssl_OPENSSL_init_crypto(flags, nil) != 1 {
+ errInit = newOpenSSLError("openssl: init crypto")
+ return
+ }
+ }
+ })
+
+ return errInit
+}
+
+func init() { //nolint:gochecknoinits
+ err := Init()
+ if err != nil {
+ panic(err)
+ }
+}
+
+func dlopen(version string) unsafe.Pointer {
+ ext := ".so"
+ if runtime.GOOS == "darwin" {
+ ext = ".dylib"
+ }
+
+ cv := C.CString("libcrypto" + ext + "." + version)
+ defer C.free(unsafe.Pointer(cv))
+ return C.dlopen(cv, C.RTLD_LAZY|C.RTLD_LOCAL)
+}
+
+func loadLibrary(version string) (unsafe.Pointer, error) {
+ if version != "" {
+ // If version is specified try to load it or error out.
+ handle := dlopen(version)
+ if handle == nil {
+ errstr := C.GoString(C.dlerror())
+ return nil, errors.New("openssl: can't load libcrypto.so." + version + ": " + errstr)
+ }
+ return handle, nil
+ }
+ for _, v := range knownVersions {
+ handle := dlopen(v)
+ if handle != nil {
+ return handle, nil
+ }
+ }
+ return nil, errors.New("openssl: can't load libcrypto.so using any known version suffix")
+}
+
+// VersionText returns the version text of the OpenSSL currently loaded.
+func VersionText() string {
+ return C.GoString(C.go_openssl_OpenSSL_version(0))
+}
+
+func isLegacy1() bool {
+ return vMajor == 1
+}
+
+func is11() bool {
+ return (vMajor == 1 && vMinor == 1)
+}
+
+func isGeq11() bool {
+ return is30() || is11()
+}
+
+func is30() bool {
+ return vMajor == 3
+}
+
+func newOpenSSLError(msg string) error {
+ var b strings.Builder
+ var e C.ulong
+
+ b.WriteString(msg)
+ b.WriteString("\nopenssl error(s):\n")
+
+ for {
+ e = C.go_openssl_ERR_get_error()
+ if e == 0 {
+ break
+ }
+ var buf [256]byte
+ C.go_openssl_ERR_error_string_n(e, (*C.char)(unsafe.Pointer(&buf[0])), 256)
+ b.Write(buf[:])
+ b.WriteByte('\n')
+ }
+ return errors.New(b.String())
+}
diff --git a/big/openssl_dev.go b/big/openssl_dev.go
new file mode 100644
index 0000000..cc35917
--- /dev/null
+++ b/big/openssl_dev.go
@@ -0,0 +1,128 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+//go:build openssldev
+// +build openssldev
+
+package big
+
+// #cgo CFLAGS: -DGO_OPENSSL_DEV
+// #cgo pkg-config: libcrypto
+// #include "goopenssl.h"
+import "C"
+import (
+ "errors"
+ "strconv"
+ "strings"
+ "sync"
+ "unsafe"
+)
+
+var (
+ initOnce sync.Once
+ // errInit is set when first calling Init().
+ errInit error
+ // vMajor and vMinor hold the major/minor OpenSSL version.
+ // It is only populated if Init has been called.
+ vMajor, vMinor int
+)
+
+func errUnsuportedVersion() error {
+ return errors.New("openssl: OpenSSL version: " + strconv.Itoa(vMajor) + "." + strconv.Itoa(vMinor) + " is not supported")
+}
+
+// Init loads and initializes OpenSSL.
+// It must be called before any other OpenSSL call.
+//
+// Only the first call to Init is effective,
+// subsequent calls will return the same error result as the one from the first call.
+func Init() error {
+ initOnce.Do(func() {
+ vMajor = int(C.go_openssl_version_major())
+ vMinor = int(C.go_openssl_version_minor())
+
+ if vMajor == -1 || vMinor == -1 {
+ errInit = errors.New("openssl: can't retrieve OpenSSL version")
+ return
+ }
+ var supported bool
+ if vMajor == 1 {
+ supported = vMinor == 0 || vMinor == 1
+ } else if vMajor == 3 {
+ // OpenSSL team guarantees API and ABI compatibility within the same major version since OpenSSL 3.
+ supported = true
+ }
+ if !supported {
+ errInit = errUnsuportedVersion()
+ return
+ }
+
+ C.go_openssl_OPENSSL_init()
+ if vMajor == 1 && vMinor == 0 {
+ if C.go_openssl_thread_setup() != 1 {
+ errInit = newOpenSSLError("openssl: thread setup")
+ return
+ }
+ C.go_openssl_ERR_load_crypto_strings()
+ } else {
+ flags := C.uint64_t(C.GO_OPENSSL_INIT_LOAD_CONFIG | C.GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS)
+ if C.go_openssl_OPENSSL_init_crypto(flags, nil) != 1 {
+ errInit = newOpenSSLError("openssl: init crypto")
+ return
+ }
+ }
+ })
+
+ return errInit
+}
+
+func init() {
+ err := Init()
+ if err != nil {
+ panic(err)
+ }
+}
+
+// VersionText returns the version text of the OpenSSL currently loaded.
+func VersionText() string {
+ return C.GoString(C.go_openssl_OpenSSL_version(0))
+}
+
+func isLegacy1() bool {
+ return vMajor == 1
+}
+
+func is11() bool {
+ return (vMajor == 1 && vMinor == 1)
+}
+
+func isGeq11() bool {
+ return is30() || is11()
+}
+
+func is30() bool {
+ return vMajor == 3
+}
+
+func newOpenSSLError(msg string) error {
+ var b strings.Builder
+ var e C.ulong
+
+ b.WriteString(msg)
+ b.WriteString("\nopenssl error(s):\n")
+
+ for {
+ e = C.go_openssl_ERR_get_error()
+ if e == 0 {
+ break
+ }
+ var buf [256]byte
+ C.go_openssl_ERR_error_string_n(e, (*C.char)(unsafe.Pointer(&buf[0])), 256)
+ b.Write(buf[:])
+ b.WriteByte('\n')
+ }
+ return errors.New(b.String())
+}
diff --git a/big/openssl_funcs.h b/big/openssl_funcs.h
new file mode 100644
index 0000000..a6bf806
--- /dev/null
+++ b/big/openssl_funcs.h
@@ -0,0 +1,181 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+#ifndef OPENSSL_FUNCS_H
+#define OPENSSL_FUNCS_H
+
+#include // size_t
+#include // uint64_t
+
+#ifdef GO_OPENSSL_DEV
+#include
+#include
+#include
+#include
+
+#if !defined(OPENSSL_VERSION_MAJOR)
+#define OPENSSL_VERSION_MAJOR (OPENSSL_VERSION_NUMBER >> 28)
+#endif
+
+#if !defined(OPENSSL_VERSION_MINOR)
+#define OPENSSL_VERSION_MINOR ((OPENSSL_VERSION_NUMBER >> 20) & 0x000000FFL)
+#endif
+
+#if !(OPENSSL_VERSION_MAJOR == 1 && OPENSSL_VERSION_MINOR == 0)
+enum
+{
+ GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS = OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
+ GO_OPENSSL_INIT_LOAD_CONFIG = OPENSSL_INIT_LOAD_CONFIG,
+};
+#else
+// dummy values for legacy library compilation
+enum
+{
+ GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0,
+ GO_OPENSSL_INIT_LOAD_CONFIG = 1,
+};
+#endif
+
+typedef BN_ULONG GO_BN_ULONG;
+
+enum
+{
+ /*
+ * avoid leaking exponent information through timing,
+ * BN_mod_exp_mont() will call BN_mod_exp_mont_consttime,
+ * BN_div() will call BN_div_no_branch,
+ * BN_mod_inverse() will call bn_mod_inverse_no_branch.
+ */
+ GO_BN_FLG_CONSTTIME = BN_FLG_CONSTTIME,
+};
+
+#else /* !GO_OPENSSL_DEV */
+
+enum
+{
+ GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000002L,
+ GO_OPENSSL_INIT_LOAD_CONFIG = 0x00000040L,
+};
+
+#ifdef __UINT64_TYPE__
+typedef __UINT64_TYPE__ GO_BN_ULONG;
+#else
+typedef __UINT32_TYPE__ GO_BN_ULONG;
+#endif
+
+enum
+{
+ /*
+ * avoid leaking exponent information through timing,
+ * BN_mod_exp_mont() will call BN_mod_exp_mont_consttime,
+ * BN_div() will call BN_div_no_branch,
+ * BN_mod_inverse() will call bn_mod_inverse_no_branch.
+ */
+ GO_BN_FLG_CONSTTIME = 0x04,
+};
+
+#endif /* GO_OPENSSL_DEV */
+
+typedef struct ossl_init_settings_st GO_OPENSSL_INIT_SETTINGS;
+typedef struct ossl_lib_ctx_st GO_OSSL_LIB_CTX;
+typedef struct bignum_st GO_BIGNUM;
+typedef struct bignum_ctx GO_BN_CTX;
+typedef struct bn_mont_ctx_st GO_BN_MONT_CTX;
+typedef struct bn_gencb_st GO_BN_GENCB;
+
+// List of all functions from the libcrypto that are used in this package.
+// Forgetting to add a function here results in build failure with message reporting the function
+// that needs to be added.
+//
+// The purpose of FOR_ALL_OPENSSL_FUNCTIONS is to define all libcrypto functions
+// without depending on the openssl headers so it is easier to use this package
+// with an openssl version different that the one used at build time.
+//
+// The following macros may not be defined at this point,
+// they are not resolved here but just accumulated in FOR_ALL_OPENSSL_FUNCTIONS.
+//
+// DEFINEFUNC defines and loads openssl functions that can be directly called from Go as their signatures match
+// the OpenSSL API and do not require special logic.
+// The process will be aborted if the function can't be loaded.
+//
+// DEFINEFUNC_LEGACY_1_0 acts like DEFINEFUNC but only aborts the process if the function can't be loaded
+// when using 1.0.x. This indicates the function is required when using 1.0.x, but is unused when using later versions.
+// It also might not exist in later versions.
+//
+// DEFINEFUNC_LEGACY_1 acts like DEFINEFUNC but only aborts the process if the function can't be loaded
+// when using 1.x. This indicates the function is required when using 1.x, but is unused when using later versions.
+// It also might not exist in later versions.
+//
+// DEFINEFUNC_1_1 acts like DEFINEFUNC but only aborts the process if function can't be loaded
+// when using 1.1.0 or higher.
+//
+// DEFINEFUNC_3_0 acts like DEFINEFUNC but only aborts the process if function can't be loaded
+// when using 3.0.0 or higher.
+//
+// DEFINEFUNC_RENAMED_1_1 acts like DEFINEFUNC but tries to load the function using the new name when using >= 1.1.x
+// and the old name when using 1.0.2. In both cases the function will have the new name.
+//
+// DEFINEFUNC_RENAMED_3_0 acts like DEFINEFUNC but tries to load the function using the new name when using >= 3.x
+// and the old name when using 1.x. In both cases the function will have the new name.
+
+#define FOR_ALL_OPENSSL_FUNCTIONS \
+ DEFINEFUNC(unsigned long, ERR_get_error, (void), ()) \
+ DEFINEFUNC(void, ERR_error_string_n, (unsigned long e, char *buf, size_t len), (e, buf, len)) \
+ DEFINEFUNC_RENAMED_1_1(const char *, OpenSSL_version, SSLeay_version, (int type), (type)) \
+ DEFINEFUNC(void, OPENSSL_init, (void), ()) \
+ DEFINEFUNC_LEGACY_1_0(void, ERR_load_crypto_strings, (void), (), ) \
+ DEFINEFUNC_LEGACY_1_0(int, CRYPTO_num_locks, (void), (), -1) \
+ DEFINEFUNC_LEGACY_1_0(void, CRYPTO_set_id_callback, (unsigned long (*id_function)(void)), (id_function), ) \
+ DEFINEFUNC_LEGACY_1_0(void, CRYPTO_set_locking_callback, (void (*locking_function)(int mode, int n, const char *file, int line)), (locking_function), ) \
+ DEFINEFUNC_1_1(int, OPENSSL_init_crypto, (uint64_t ops, const GO_OPENSSL_INIT_SETTINGS *settings), (ops, settings), -1) \
+ DEFINEFUNC(GO_BIGNUM *, BN_new, (void), ()) \
+ DEFINEFUNC_1_1(GO_BIGNUM *, BN_secure_new, (void), (), NULL) \
+ DEFINEFUNC(void, BN_free, (GO_BIGNUM * arg0), (arg0)) \
+ DEFINEFUNC(void, BN_clear_free, (GO_BIGNUM * arg0), (arg0)) \
+ DEFINEFUNC(const GO_BIGNUM *, BN_value_one, (void), ()) \
+ DEFINEFUNC(char *, BN_bn2dec, (const GO_BIGNUM *arg0), (arg0)) \
+ DEFINEFUNC(char *, BN_bn2hex, (const GO_BIGNUM *arg0), (arg0)) \
+ DEFINEFUNC(int, BN_generate_prime_ex, (GO_BIGNUM * ret, int bits, int safe, const GO_BIGNUM *add, const GO_BIGNUM *rem, GO_BN_GENCB *cb), (ret, bits, safe, add, rem, cb)) \
+ DEFINEFUNC_3_0(int, BN_generate_prime_ex2, (GO_BIGNUM * arg0, int arg1, int arg2, const GO_BIGNUM *arg3, const GO_BIGNUM *arg4, GO_BN_GENCB *arg5, GO_BN_CTX *arg6), (arg0, arg1, arg2, arg3, arg4, arg5, arg6), -1) \
+ DEFINEFUNC_LEGACY_1(int, BN_is_prime_ex, (const GO_BIGNUM *arg0, int arg1, GO_BN_CTX *arg2, GO_BN_GENCB *arg3), (arg0, arg1, arg2, arg3), -1) \
+ DEFINEFUNC_3_0(int, BN_check_prime, (const GO_BIGNUM *arg0, GO_BN_CTX *arg1, GO_BN_GENCB *arg2), (arg0, arg1, arg2), -1) \
+ DEFINEFUNC(int, BN_add, (GO_BIGNUM * r, const GO_BIGNUM *a, const GO_BIGNUM *b), (r, a, b)) \
+ DEFINEFUNC(int, BN_sub, (GO_BIGNUM * r, const GO_BIGNUM *a, const GO_BIGNUM *b), (r, a, b)) \
+ DEFINEFUNC(int, BN_mul, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1, const GO_BIGNUM *arg2, GO_BN_CTX *arg3), (arg0, arg1, arg2, arg3)) \
+ DEFINEFUNC(int, BN_mod_mul, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1, const GO_BIGNUM *arg2, const GO_BIGNUM *arg3, GO_BN_CTX *arg4), (arg0, arg1, arg2, arg3, arg4)) \
+ DEFINEFUNC(int, BN_mod_mul_montgomery, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1, const GO_BIGNUM *arg2, GO_BN_MONT_CTX *arg3, GO_BN_CTX *arg4), (arg0, arg1, arg2, arg3, arg4)) \
+ DEFINEFUNC(int, BN_div, (GO_BIGNUM * dv, GO_BIGNUM * rem, const GO_BIGNUM *m, const GO_BIGNUM *d, GO_BN_CTX *ctx), (dv, rem, m, d, ctx)) \
+ DEFINEFUNC(int, BN_exp, (GO_BIGNUM * r, GO_BIGNUM * a, GO_BIGNUM * p, GO_BN_CTX * ctx), (r, a, p, ctx)) \
+ DEFINEFUNC(int, BN_mod_exp, (GO_BIGNUM * r, const GO_BIGNUM *a, const GO_BIGNUM *p, const GO_BIGNUM *m, GO_BN_CTX *ctx), (r, a, p, m, ctx)) \
+ DEFINEFUNC(int, BN_mod_exp_mont, (GO_BIGNUM * r, const GO_BIGNUM *a, const GO_BIGNUM *p, const GO_BIGNUM *m, GO_BN_CTX *ctx, GO_BN_MONT_CTX *m_ctx), (r, a, p, m, ctx, m_ctx)) \
+ DEFINEFUNC(GO_BIGNUM *, BN_mod_inverse, (GO_BIGNUM * ret, const GO_BIGNUM *a, const GO_BIGNUM *n, GO_BN_CTX *ctx), (ret, a, n, ctx)) \
+ DEFINEFUNC(int, BN_num_bits, (const GO_BIGNUM *arg0), (arg0)) \
+ DEFINEFUNC(GO_BIGNUM *, BN_bin2bn, (const unsigned char *arg0, int arg1, GO_BIGNUM *arg2), (arg0, arg1, arg2)) \
+ DEFINEFUNC(int, BN_dec2bn, (GO_BIGNUM * *arg0, const char *arg1), (arg0, arg1)) \
+ DEFINEFUNC(int, BN_hex2bn, (GO_BIGNUM * *arg0, const char *arg1), (arg0, arg1)) \
+ DEFINEFUNC(int, BN_set_word, (GO_BIGNUM * arg0, GO_BN_ULONG arg1), (arg0, arg1)) \
+ DEFINEFUNC(int, BN_bn2bin, (const GO_BIGNUM *arg0, unsigned char *arg1), (arg0, arg1)) \
+ DEFINEFUNC_1_1(int, BN_bn2binpad, (const GO_BIGNUM *arg0, unsigned char *arg1, int arg2), (arg0, arg1, arg2), -1) \
+ DEFINEFUNC(int, BN_lshift, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1, int arg2), (arg0, arg1, arg2)) \
+ DEFINEFUNC(int, BN_rshift, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1, int arg2), (arg0, arg1, arg2)) \
+ DEFINEFUNC(GO_BN_ULONG, BN_get_word, (const GO_BIGNUM *arg0), (arg0)) \
+ DEFINEFUNC(GO_BIGNUM *, BN_copy, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1), (arg0, arg1)) \
+ DEFINEFUNC(int, BN_rand_range, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1), (arg0, arg1)) \
+ DEFINEFUNC(int, BN_cmp, (GO_BIGNUM * arg0, const GO_BIGNUM *arg1), (arg0, arg1)) \
+ DEFINEFUNC(GO_BN_CTX *, BN_CTX_new, (void), ()) \
+ DEFINEFUNC_3_0(GO_BN_CTX *, BN_CTX_new_ex, (GO_OSSL_LIB_CTX * arg0), (arg0), NULL) \
+ DEFINEFUNC_1_1(GO_BN_CTX *, BN_CTX_secure_new, (void), (), NULL) \
+ DEFINEFUNC_3_0(GO_BN_CTX *, BN_CTX_secure_new_ex, (GO_OSSL_LIB_CTX * arg0), (arg0), NULL) \
+ DEFINEFUNC(void, BN_CTX_free, (GO_BN_CTX * arg0), (arg0)) \
+ DEFINEFUNC(void, BN_CTX_start, (GO_BN_CTX * arg0), (arg0)) \
+ DEFINEFUNC(void, BN_CTX_end, (GO_BN_CTX * arg0), (arg0)) \
+ DEFINEFUNC(GO_BIGNUM *, BN_CTX_get, (GO_BN_CTX * arg0), (arg0)) \
+ DEFINEFUNC_1_1(void, BN_set_flags, (GO_BIGNUM * arg0, int arg1), (arg0, arg1), ) \
+ DEFINEFUNC(GO_BN_MONT_CTX *, BN_MONT_CTX_new, (void), ()) \
+ DEFINEFUNC(void, BN_MONT_CTX_free, (GO_BN_MONT_CTX * arg0), (arg0)) \
+ DEFINEFUNC(int, BN_MONT_CTX_set, (GO_BN_MONT_CTX * arg0, const GO_BIGNUM *arg1, GO_BN_CTX *arg2), (arg0, arg1, arg2))
+
+#endif /* OPENSSL_FUNCS_H */
diff --git a/big/openssl_lock_setup.c b/big/openssl_lock_setup.c
new file mode 100644
index 0000000..9f0d6c5
--- /dev/null
+++ b/big/openssl_lock_setup.c
@@ -0,0 +1,70 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+#include "goopenssl.h"
+
+#include
+#include
+
+#if defined(_WIN32) || defined(_WIN64)
+
+#include
+
+#define MUTEX_TYPE HANDLE
+#define MUTEX_SETUP(x) (x = CreateMutex(NULL, FALSE, NULL))
+#define MUTEX_CLEANUP(x) CloseHandle(x)
+#define MUTEX_LOCK(x) WaitForSingleObject(x, INFINITE)
+#define MUTEX_UNLOCK(x) ReleaseMutex(&(x))
+#define THREAD_ID GetCurrentThreadId()
+
+#else /* !(defined(_WIN32) || defined(_WIN64)) */
+
+#include
+
+#define _GNU_SOURCE
+#include
+
+#define MUTEX_TYPE pthread_mutex_t
+#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
+#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
+#define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
+#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
+#define THREAD_ID pthread_self()
+#endif
+
+#ifndef GO_OPENSSL_DEV
+#define CRYPTO_LOCK 1
+#endif
+
+/* This array will store all of the mutexes available to OpenSSL. */
+static MUTEX_TYPE *mutex_buf = NULL;
+
+static void locking_function(int mode, int n, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ MUTEX_LOCK(mutex_buf[n]);
+ else
+ MUTEX_UNLOCK(mutex_buf[n]);
+}
+
+static unsigned long id_function(void)
+{
+ return ((unsigned long)THREAD_ID);
+}
+
+int go_openssl_thread_setup(void)
+{
+ int i;
+
+ mutex_buf = malloc(go_openssl_CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
+ if (!mutex_buf)
+ return 0;
+ for (i = 0; i < go_openssl_CRYPTO_num_locks(); i++)
+ MUTEX_SETUP(mutex_buf[i]);
+ go_openssl_CRYPTO_set_id_callback(id_function);
+ go_openssl_CRYPTO_set_locking_callback(locking_function);
+ return 1;
+}
diff --git a/big/openssl_test.go b/big/openssl_test.go
new file mode 100644
index 0000000..ddbcd4b
--- /dev/null
+++ b/big/openssl_test.go
@@ -0,0 +1,26 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+package big_test
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/matteoarella/pedersen/big"
+)
+
+func TestMain(m *testing.M) {
+ if err := big.Init(); err != nil {
+ // An error here could mean that this Linux distro does not have a supported OpenSSL version
+ // or that there is a bug in the Init code.
+ panic(err)
+ }
+
+ fmt.Println("OpenSSL version:", big.VersionText())
+ os.Exit(m.Run())
+}
diff --git a/cmd/pedersen/pedersen.go b/cmd/pedersen/pedersen.go
new file mode 100644
index 0000000..6db8b8b
--- /dev/null
+++ b/cmd/pedersen/pedersen.go
@@ -0,0 +1,19 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+package main
+
+import (
+ "os"
+
+ "github.com/matteoarella/pedersen/internal/cmd"
+)
+
+func main() {
+ if err := cmd.Execute(); err != nil {
+ os.Exit(1)
+ }
+}
diff --git a/combine.go b/combine.go
new file mode 100644
index 0000000..6192583
--- /dev/null
+++ b/combine.go
@@ -0,0 +1,165 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+package pedersen
+
+import (
+ "github.com/matteoarella/pedersen/big"
+ "golang.org/x/sync/errgroup"
+)
+
+type combineValue struct {
+ index int
+ value *big.Int
+}
+
+func (p *Pedersen) combine(
+ ctx *big.IntContext,
+ index int,
+ abscissae []*big.Int,
+ parts []SecretPart,
+) (combineValue, error) {
+ var (
+ xSamples []*big.Int
+ ySamples []*big.Int
+ )
+
+ for idx, p := range parts {
+ if (SecretPart{}) == p {
+ continue
+ }
+
+ ySamples = append(ySamples, p.SShare)
+ xSamples = append(xSamples, abscissae[idx])
+ }
+
+ ctx.Attach()
+ defer ctx.Detach()
+
+ zero, err := ctx.GetInt()
+ if err != nil {
+ return combineValue{}, err
+ }
+
+ if err := zero.SetUInt64(0); err != nil {
+ return combineValue{}, err
+ }
+
+ secret, err := interpolatePolynomial(ctx, xSamples, ySamples, zero, p.group.Q)
+ if err != nil {
+ return combineValue{}, err
+ }
+
+ return combineValue{
+ index: index,
+ value: secret,
+ }, nil
+}
+
+func bigIntUnpadding(ctx *big.IntContext, n *big.Int) ([]byte, error) {
+ ctx.Attach()
+ defer ctx.Detach()
+
+ // extract leading zeros info from trailing bytes
+ mask, err := ctx.GetInt()
+ if err != nil {
+ return nil, err
+ }
+ if err := mask.SetUInt64(0x00000000FFFFFFFF); err != nil {
+ return nil, err
+ }
+
+ if err := mask.And(n, mask); err != nil {
+ return nil, err
+ }
+
+ zeros := mask.Uint64()
+
+ // cut trailer
+ if err := n.Rsh(n, zerosInfoSizeBytes*8); err != nil {
+ return nil, err
+ }
+
+ res := make([]byte, zeros)
+
+ for i := 0; i < int(zeros); i++ {
+ res[i] = 0
+ }
+
+ nBytes, err := n.Bytes()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(res, nBytes...), nil // nozero
+}
+
+// Combine combines the secret shares into the original secret.
+func (p *Pedersen) Combine(shares *Shares) ([]byte, error) {
+ err := p.validateShares(shares)
+ if err != nil {
+ return nil, err
+ }
+
+ splittedLen := len(shares.Parts[0])
+ values := make([]*big.Int, splittedLen)
+ concLimit := p.adjustConcLimit(splittedLen)
+ chunksIndex := p.balanceIndices(splittedLen, concLimit)
+ group := errgroup.Group{}
+ group.SetLimit(concLimit)
+
+ for _, chunk := range chunksIndex {
+ chunk := chunk
+
+ group.Go(func() error {
+ ctx, err := big.NewIntContext()
+ if err != nil {
+ return err
+ }
+ defer ctx.Destroy()
+
+ parts := make([]SecretPart, p.parts)
+
+ for idx := chunk.start; idx < chunk.end; idx++ {
+ for shareIdx := 0; shareIdx < p.parts; shareIdx++ {
+ parts[shareIdx] = shares.Parts[shareIdx][idx]
+ }
+
+ value, err := p.combine(ctx, idx, shares.Abscissae, parts)
+ if err != nil {
+ return err
+ }
+
+ values[value.index] = value.value
+ }
+
+ return nil
+ })
+ }
+
+ err = group.Wait()
+ if err != nil {
+ return nil, err
+ }
+
+ var res []byte
+ ctx, err := big.NewIntContext()
+ if err != nil {
+ return nil, err
+ }
+ defer ctx.Destroy()
+
+ for i := 0; i < splittedLen; i++ {
+ chunk, err := bigIntUnpadding(ctx, values[i])
+ if err != nil {
+ return nil, err
+ }
+
+ res = append(res, chunk...)
+ }
+
+ return res, nil
+}
diff --git a/combine_test.go b/combine_test.go
new file mode 100644
index 0000000..51df896
--- /dev/null
+++ b/combine_test.go
@@ -0,0 +1,232 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+package pedersen_test
+
+import (
+ "crypto/rand"
+ mrand "math/rand"
+ "testing"
+ "time"
+
+ "github.com/matteoarella/pedersen"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+type pedersenCombineTestCases struct {
+ pedersenTestCases
+ shares *pedersen.Shares
+ secret []byte
+}
+
+func TestPedersenCombineInvalid(t *testing.T) {
+ group := getTestSchnorrGroup(t)
+
+ testCases := []pedersenCombineTestCases{
+ {
+ pedersenTestCases: pedersenTestCases{
+ description: "nil shares",
+ parameters: pedersenParameters{
+ parts: 5,
+ threshold: 3,
+ },
+ options: []pedersen.Option{
+ pedersen.CyclicGroup(group),
+ },
+ },
+ shares: nil,
+ },
+ {
+ pedersenTestCases: pedersenTestCases{
+ description: "wrong shares parts len",
+ parameters: pedersenParameters{
+ parts: 5,
+ threshold: 3,
+ },
+ options: []pedersen.Option{
+ pedersen.CyclicGroup(group),
+ },
+ },
+ shares: &pedersen.Shares{
+ Parts: [][]pedersen.SecretPart{
+ {
+ pedersen.SecretPart{},
+ pedersen.SecretPart{},
+ },
+ },
+ },
+ },
+ }
+
+ for _, scenario := range testCases {
+ t.Run(scenario.description, func(t *testing.T) {
+ p, err := pedersen.NewPedersen(scenario.parameters.parts, scenario.parameters.threshold, scenario.options...)
+ require.NoError(t, err)
+ require.NotNil(t, p)
+
+ _, err = p.Combine(scenario.shares)
+ require.Error(t, err)
+ })
+ }
+}
+
+func getRandomIndexSubset(n, size int) []int {
+ mrand.Seed(time.Now().Unix())
+ return mrand.Perm(n)[0:size]
+}
+
+func getSharesSubset(s *pedersen.Shares, threshold uint) *pedersen.Shares {
+ n := len(s.Parts)
+ shares := &pedersen.Shares{
+ Abscissae: s.Abscissae,
+ Parts: make([][]pedersen.SecretPart, n),
+ Commitments: s.Commitments,
+ }
+
+ chunksCount := len(s.Parts[0])
+
+ for idx := 0; idx < n; idx++ {
+ shares.Parts[idx] = make([]pedersen.SecretPart, chunksCount)
+ }
+
+ for chunkIndex := 0; chunkIndex < chunksCount; chunkIndex++ {
+ indexes := getRandomIndexSubset(n, int(threshold))
+
+ for _, idx := range indexes {
+ shares.Parts[idx][chunkIndex] = s.Parts[idx][chunkIndex]
+ }
+ }
+
+ return shares
+}
+
+func TestPedersenCombineValid(t *testing.T) {
+ group := getTestSchnorrGroup(t)
+
+ randomSecret := make([]byte, 128)
+ _, err := rand.Read(randomSecret)
+ require.NoError(t, err)
+
+ testCases := []pedersenCombineTestCases{
+ {
+ pedersenTestCases: pedersenTestCases{
+ description: "valid combine random secret",
+ parameters: pedersenParameters{
+ parts: 10,
+ threshold: 5,
+ },
+ options: []pedersen.Option{
+ pedersen.CyclicGroup(group),
+ },
+ },
+ secret: randomSecret,
+ },
+ {
+ pedersenTestCases: pedersenTestCases{
+ description: "valid combine secret with heading zeros",
+ parameters: pedersenParameters{
+ parts: 10,
+ threshold: 5,
+ },
+ options: []pedersen.Option{
+ pedersen.CyclicGroup(group),
+ },
+ },
+ secret: []byte{0, 0, 0, 1, 2, 3, 4, 5},
+ },
+ {
+ pedersenTestCases: pedersenTestCases{
+ description: "valid combine secret with heading zero chunks",
+ parameters: pedersenParameters{
+ parts: 10,
+ threshold: 5,
+ },
+ options: []pedersen.Option{
+ pedersen.CyclicGroup(group),
+ },
+ },
+ secret: []byte{0x0, 0x0, 0x2d, 0x33, 0x0, 0x0, 0xe7, 0x0, 0x0, 0x1c, 0x82, 0xa4, 0x4c, 0xcb, 0x11, 0x88},
+ },
+ }
+
+ for _, scenario := range testCases {
+ t.Run(scenario.description, func(t *testing.T) {
+ p, err := pedersen.NewPedersen(scenario.parameters.parts, scenario.parameters.threshold, scenario.options...)
+ require.NoError(t, err)
+ require.NotNil(t, p)
+
+ shares, err := p.Split(scenario.secret, nil)
+ require.NoError(t, err)
+ require.NotNil(t, shares)
+
+ // combine full shares
+ secret, err := p.Combine(shares)
+ require.NoError(t, err)
+ require.NotNil(t, secret)
+ assert.Equal(t, scenario.secret, secret)
+
+ // combine subset of shares
+ subset := getSharesSubset(shares, uint(p.GetThreshold()))
+
+ secret, err = p.Combine(subset)
+ require.NoError(t, err)
+ require.NotNil(t, secret)
+ assert.Equal(t, scenario.secret, secret)
+ })
+ }
+}
+
+func benchmarkCombineCase(b *testing.B, groupSize, parts, threshold int) {
+ b.Helper()
+
+ group, err := pedersen.NewSchnorrGroup(groupSize)
+ require.NoError(b, err)
+
+ randomSecret := make([]byte, 256)
+ _, err = rand.Read(randomSecret)
+ require.NoError(b, err)
+
+ p, err := pedersen.NewPedersen(parts, threshold, pedersen.CyclicGroup(group))
+ require.NoError(b, err)
+ require.NotNil(b, p)
+
+ shares, err := p.Split(randomSecret, nil)
+ require.NoError(b, err)
+ require.NotNil(b, shares)
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ _, err = p.Combine(shares)
+ require.NoError(b, err)
+ }
+}
+
+func BenchmarkPedersenCombine_1024_5_3(b *testing.B) {
+ benchmarkCombineCase(b, 1024, 5, 3)
+}
+
+func BenchmarkPedersenCombine_1024_7_4(b *testing.B) {
+ benchmarkCombineCase(b, 1024, 7, 4)
+}
+
+func BenchmarkPedersenCombine_1024_10_5(b *testing.B) {
+ benchmarkCombineCase(b, 1024, 10, 5)
+}
+
+func BenchmarkPedersenCombine_2048_5_3(b *testing.B) {
+ benchmarkCombineCase(b, 2048, 5, 3)
+}
+
+func BenchmarkPedersenCombine_2048_7_4(b *testing.B) {
+ benchmarkCombineCase(b, 2048, 7, 4)
+}
+
+func BenchmarkPedersenCombine_2048_10_5(b *testing.B) {
+ benchmarkCombineCase(b, 2048, 10, 5)
+}
diff --git a/docker-bake.hcl b/docker-bake.hcl
new file mode 100644
index 0000000..1fb6ae2
--- /dev/null
+++ b/docker-bake.hcl
@@ -0,0 +1,183 @@
+// Copyright (c) Pedersen authors.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+variable "GO_VERSION" {
+ # default ARG value set in Dockerfile
+ default = null
+}
+
+variable "OPENSSL_VERSION" {
+ default = null
+}
+
+variable "GO_BUILDTAGS" {
+ default = null
+}
+
+variable "GO_BUILDFLAGS" {
+ default = null
+}
+
+variable "GO_LINKMODE" {
+ default = null
+}
+
+variable "GO_STRIP" {
+ default = null
+}
+
+variable "GO_TESTFLAGS" {
+ default = null
+}
+
+# Defines the output folder to override the default behavior.
+# See Makefile for details, this is generally only useful for
+# the packaging scripts and care should be taken to not break
+# them.
+variable "DESTDIR" {
+ default = ""
+}
+
+variable "REPO_NAME" {
+ default = null
+}
+
+variable "REPO_URL" {
+ default = null
+}
+
+variable "ORGANIZATION_NAME" {
+ default = null
+}
+
+variable "DOCS_URL" {
+ default = null
+}
+
+variable "DOCS_EDIT_URL" {
+ default = null
+}
+
+variable "VERSION" {
+ default = null
+}
+
+function "outdir" {
+ params = [defaultdir]
+ result = DESTDIR != "" ? DESTDIR : "${defaultdir}"
+}
+
+target "_common" {
+ args = {
+ GO_VERSION = GO_VERSION
+ OPENSSL_VERSION = OPENSSL_VERSION
+ GO_BUILDTAGS = GO_BUILDTAGS
+ GO_BUILDFLAGS = GO_BUILDFLAGS
+ BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1
+ }
+}
+
+group "default" {
+ targets = ["binary"]
+}
+
+group "validate" {
+ targets = ["lint", "license-validate"]
+}
+
+target "lint" {
+ inherits = ["_common"]
+ target = "lint"
+ output = ["type=cacheonly"]
+}
+
+target "license-validate" {
+ target = "license-validate"
+ output = ["type=cacheonly"]
+}
+
+target "test" {
+ inherits = ["_common"]
+ target = "test"
+ args = {
+ GO_TESTFLAGS = GO_TESTFLAGS
+ }
+ output = ["type=cacheonly"]
+ platforms = ["local"]
+}
+
+target "test-cross" {
+ inherits = ["test"]
+ platforms = [
+ "linux/amd64",
+ "linux/arm/v6",
+ "linux/arm/v7",
+ "linux/arm64",
+ "linux/ppc64le",
+ "linux/riscv64",
+ "linux/s390x",
+ ]
+}
+
+target "binary" {
+ inherits = ["_common"]
+ target = "binary"
+ args = {
+ OPENSSL_VERSION = OPENSSL_VERSION
+ GO_LINKMODE = GO_LINKMODE
+ GO_STRIP = GO_STRIP
+ }
+ output = [outdir("./bin/build")]
+ platforms = ["local"]
+}
+
+target "binary-cross" {
+ inherits = ["binary"]
+ platforms = [
+ "darwin/amd64",
+ "darwin/arm64",
+ "linux/amd64",
+ "linux/arm/v6",
+ "linux/arm/v7",
+ "linux/arm64",
+ "linux/ppc64le",
+ "linux/riscv64",
+ "linux/s390x",
+ "windows/amd64",
+ ]
+}
+
+target "release" {
+ inherits = ["binary-cross"]
+ target = "release"
+ output = [outdir("./bin/release")]
+}
+
+target "docs" {
+ target = "docs-release"
+ args = {
+ ORGANIZATION_NAME = ORGANIZATION_NAME
+ REPO_NAME = REPO_NAME
+ REPO_URL = REPO_URL
+ DOCS_URL = DOCS_URL
+ DOCS_EDIT_URL = DOCS_EDIT_URL
+ VERSION = VERSION
+ }
+ secret = [
+ "type=env,id=ALGOLIA_APP_ID",
+ "type=env,id=ALGOLIA_SEARCH_API_KEY",
+ "type=env,id=ALGOLIA_INDEX_NAME",
+ ]
+ output = [outdir("./bin/docs")]
+}
+
+target "docker-metadata-action" {}
+
+target "image-cross" {
+ inherits = ["docker-metadata-action", "release"]
+ target = "image"
+ output = ["type=image"]
+}
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..b083ed4
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,21 @@
+# Dependencies
+/node_modules
+
+# Production
+/build
+
+# Generated files
+.docusaurus
+.cache-loader
+
+# Misc
+.DS_Store
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/docs/babel.config.js b/docs/babel.config.js
new file mode 100644
index 0000000..e00595d
--- /dev/null
+++ b/docs/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
+};
diff --git a/docs/docs/01-acknowledgements.mdx b/docs/docs/01-acknowledgements.mdx
new file mode 100644
index 0000000..5413244
--- /dev/null
+++ b/docs/docs/01-acknowledgements.mdx
@@ -0,0 +1,8 @@
+---
+sidebar_position: 5
+---
+
+# Acknowledgements
+
+The work done for dynamic loading the OpenSSL library is
+inspired from the [go-crypto-openssl](https://github.com/microsoft/go-crypto-openssl) open-source project.
diff --git a/docs/docs/getting-started/01-installation.mdx b/docs/docs/getting-started/01-installation.mdx
new file mode 100644
index 0000000..5db396c
--- /dev/null
+++ b/docs/docs/getting-started/01-installation.mdx
@@ -0,0 +1,83 @@
+---
+sidebar_position: 1
+---
+
+# Installation
+
+This project provides both a Go module and a CLI tool.
+
+## Go module
+
+In order to use the `pedersen` package just do
+
+```go
+go get github.com/matteoarella/pedersen
+```
+
+### Requirements
+
+- [Go](https://go.dev/doc/install) version 1.17.x or above.
+- CGO enabled.
+
+### Dependencies
+
+The `math/big` package does not support constant-time operations so its usage for cryptographic operations is unsafe
+since it may leak some sensitive information via timing-based side channel attacks.
+For this reason the pedersen module provides its own implementation of `big.Int` operations using OpenSSL library
+bindings under the hood (via cgo calls).
+
+As a consequence the OpenSSL library must be available in the system.
+Several versions of the OpenSSL library are supported and tested, namely 1.0.2, 1.1.0, 1.1.1 and 3.0.2.
+
+The OpenSSL library can be provided in two ways, without or with the OpenSSL headers.
+
+#### OpenSSL without headers (default)
+
+By default the OpenSSL library is automatically loaded at runtime using [dlopen](https://man7.org/linux/man-pages/man3/dlopen.3.html)
+in such a way that only the OpenSSL runtime library is needed.
+Therefore, dlopen's shared library search conventions applies here.
+
+The `libcrypto` shared library file name varies among different platforms, so a best effort is done to find and load the right file:
+
+- The base name is always `libcrypto.so`.
+- Well-known version strings are appended to the base name, until the file is found, in the following order:
+`3` -> `1.1` -> `11` -> `111` -> `1.0.2` -> `1.0.0`.
+
+This algorithm can be overridden by setting the environment variable `GO_OPENSSL_VERSION_OVERRIDE`
+to the desired version string.
+For example, `GO_OPENSSL_VERSION_OVERRIDE="1.1.1k-fips"` makes the runtime look for the shared library `libcrypto.so.1.1.1k-fips`
+before running the checks for well-known versions.
+
+#### OpenSSL with headers
+
+OpenSSL headers can be used to provide all the symbols that are needed.
+If the Go tag `openssldev` is provided, the module tries to load the OpenSSL library by using the `pkg-config` tool.
+
+This case is mandatory in case of using this module for building a statically linked binary.
+
+## CLI tool
+
+import RepoUrl from '@site/src/components/RepoUrl';
+
+Download from the the correct binary for your target platform and install it.
+
+import {BinariesTabs, Platform, Variant} from '@site/src/components/BinariesTabs';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/docs/getting-started/_category_.json b/docs/docs/getting-started/_category_.json
new file mode 100644
index 0000000..534b5dc
--- /dev/null
+++ b/docs/docs/getting-started/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "Getting started",
+ "position": 2,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/docs/guides/01-group.mdx b/docs/docs/guides/01-group.mdx
new file mode 100644
index 0000000..c101bdc
--- /dev/null
+++ b/docs/docs/guides/01-group.mdx
@@ -0,0 +1,89 @@
+---
+title: 'Configure a cyclic group'
+sidebar_position: 1
+---
+
+# Configure a cyclic group
+
+In order to perform any Pedersen operations (like splitting a secret, combining a secret or verifying a secret part or every
+secret parts), a cyclic group $G_q$ must be generated.
+In particular given two large primes $p$ and $q$ such that $q$ divides $p-1$, $G_q$ is the unique subgroup of $\mathbb{Z}^*_q$
+of order $q$. Let $g$ and $h$ be two generators of $G_q$.
+
+The cyclic group $G_q$ can be instantiated in two ways.
+
+## Generate a new group
+
+In this case a fresh cyclic group is generated at random by using the function `pedersen.NewSchnorrGroup()`:
+
+```go showLineNumbers
+import (
+ "github.com/matteoarella/pedersen"
+)
+
+// highlight-next-line
+groupSize := 1024
+
+// highlight-start
+group, err := pedersen.NewSchnorrGroup(groupSize)
+if err != nil {
+ panic(err)
+}
+// highlight-end
+```
+
+## Use a previously generated group
+
+For reconstructing a secret or validating the secret parts the same group that has been adopted for splitting the secret
+must be used.
+In this case you cannot use the `pedersen.NewSchnorrGroup()` function otherwise a fresh group is generated.
+
+The `pedersen.Group` object must be instantiated by specifying the $p$, $q$, $g$ and $h$ parameters as follows:
+
+```go showLineNumbers
+import (
+ "github.com/matteoarella/pedersen"
+ "github.com/matteoarella/pedersen/big"
+)
+
+// highlight-start
+p, err := big.NewInt()
+// check err
+q, err := big.NewInt()
+// check err
+g, err := big.NewInt()
+// check err
+h, err := big.NewInt()
+// check err
+
+err = p.SetDecString("17634709279010524619")
+// check err
+err = q.SetDecString("8817354639505262309")
+// check err
+err = g.SetDecString("8414335786771157015")
+// check err
+err = h.SetDecString("15078279289296123424")
+// check err
+
+group := pedersen.Group{
+ P: p, // prime p
+ Q: q, // prime q
+ G: g, // first generator g
+ H: h, // second generator h
+}
+// highlight-end
+```
+
+## Use a group
+
+The `group` object created with one of the two methods depicted above can be used for instantiating a `pedersen.Pedersen` object as follows:
+
+```go
+schemeParts := 5
+schemeThreshold := 3
+
+p, err := pedersen.NewPedersen(schemeParts, schemeThreshold, pedersen.CyclicGroup(group))
+if err != nil {
+ panic(err)
+}
+```
diff --git a/docs/docs/guides/02-split.mdx b/docs/docs/guides/02-split.mdx
new file mode 100644
index 0000000..c2323ae
--- /dev/null
+++ b/docs/docs/guides/02-split.mdx
@@ -0,0 +1,71 @@
+---
+title: 'Split a secret'
+sidebar_position: 2
+---
+
+# Split a secret
+
+Before you can split any secret, you have to configure a cyclic group to be used for Pedersen operations as is
+shown in [Configure a cyclic group](group).
+
+This example shows how to split a secret with a $(t, n)$-threshold scheme of $(t, n) = ($ `schemeThreshold` , `schemeParts` $)$.
+
+```go showLineNumbers
+import (
+ "github.com/matteoarella/pedersen"
+)
+
+group := /* cyclic group */
+schemeParts := 5
+schemeThreshold := 3
+// highlight-next-line
+secret := /* secret to split */
+
+p, err := pedersen.NewPedersen(schemeParts, schemeThreshold, pedersen.CyclicGroup(group))
+if err != nil {
+ panic(err)
+}
+
+// highlight-start
+shares, err = p.Split(secret, nil)
+if err != nil {
+ panic(err)
+}
+// highlight-end
+
+/* ...
+ send shares parts to the corresponding shareholder and broadcast the commitments
+ ...
+*/
+```
+
+## Secret shares structure
+
+When a secret is splitted with `pedersen.Split` a `pedersen.Shares` object is returned.
+This object contains every information that the *dealer* has to transfer to the *shareholders*.
+
+If the secret that has to be split is not representable in the cyclic group (this is the case
+if the secret is bigger than the order of the cyclic group),
+the secret is split into chunks, and each chunk is split into secret parts according
+to Pedersen verifiable secret sharing.
+
+### Abscissae vector
+
+`Shares.Abscissae` is the abscissae vector used for computing the ordinate values of
+the secret parts.
+There is one abscissa for each *shareholder*, so if `shareholderIdx` represents
+the index of one *shareholder*, `Abscissae[shareholderIdx]` is the abscissa
+related to that *shareholder*.
+
+### Secret parts
+
+`Shares.Parts` is the matrix of secret parts.
+
+The first index of `Shares.Parts` represents the shareholder index, while the second index
+represents the chunk index (`Parts[shareholderIdx][chunkIdx]`).
+
+### Secret commitments
+
+`Shares.Commitments` is the matrix of commitments.
+The first index of `Shares.Commitments` represents the chunk index so `Commitments[chunkIdx]`
+is the vector of commitments related to the chunk with index `chunkIdx`.
diff --git a/docs/docs/guides/03-combine.mdx b/docs/docs/guides/03-combine.mdx
new file mode 100644
index 0000000..89b61de
--- /dev/null
+++ b/docs/docs/guides/03-combine.mdx
@@ -0,0 +1,35 @@
+---
+title: 'Combine a secret'
+sidebar_position: 3
+---
+
+# Combine a secret
+
+Before you can combine any secret shares, you have to configure a cyclic group to be used for Pedersen operations as is
+shown in [Configure a cyclic group](group).
+
+This example shows how to combine a secret with a $(t, n)$-threshold scheme of $(t, n) = ($ `schemeThreshold` , `schemeParts` $)$
+
+```go
+import (
+ "github.com/matteoarella/pedersen"
+)
+
+schemeParts := 5
+schemeThreshold := 3
+group := /* cyclic group */
+// highlight-next-line
+shares := /* retrieve shares parts from the shareholders */
+
+p, err := pedersen.NewPedersen(schemeParts, schemeThreshold, pedersen.CyclicGroup(group))
+if err != nil {
+ panic(err)
+}
+
+// highlight-start
+secret, err = p.Combine(shares)
+if err != nil {
+ panic(err)
+}
+// highlight-end
+```
diff --git a/docs/docs/guides/04-verify.mdx b/docs/docs/guides/04-verify.mdx
new file mode 100644
index 0000000..d6f89b8
--- /dev/null
+++ b/docs/docs/guides/04-verify.mdx
@@ -0,0 +1,99 @@
+---
+title: 'Verify secret shares'
+sidebar_position: 4
+---
+
+# Verify secret shares
+
+With Pedersen verifiable secret sharing the sharing and the reconstruction of any secret can be verified.
+
+- in the *sharing phase* each *Shareholder* can verify that the secret shares that he has
+received are valid
+
+- in the *reconstruction phase* the *Dealer* can verify that the secret shares received from the *shareholders*
+are valid.
+
+Before you can verify any secret shares, you have to configure a cyclic group to be used for Pedersen operations as is
+shown in [Configure a cyclic group](group).
+
+In the following examples a $(t, n)$-threshold scheme of $(t, n) = ($ `schemeThreshold` , `schemeParts` $)$ is assumed.
+
+## Shareholder verification
+
+When a *shareholder* privately receives a secret part and the corresponding broadcast secret commitments he can verify the part
+in the following way.
+
+```go
+import (
+ "github.com/matteoarella/pedersen"
+)
+
+schemeParts := 5
+schemeThreshold := 3
+group := /* cyclic group */
+x := /* abscissa related to the shareholder part */
+share := /* secret share received from the dealer */
+commitments := /* commitments broadcast from the dealer */
+
+p, err := pedersen.NewPedersen(schemeParts, schemeThreshold, pedersen.CyclicGroup(group))
+if err != nil {
+ panic(err)
+}
+
+err = p.Verify(x, share, commitments)
+if err != nil {
+ panic(err)
+}
+```
+
+## Dealer verification
+
+When a *dealer* receives a secret part from a *shareholder* he can verify the part
+in the same way as shown in [Shareholder verification](verify#shareholder-verification).
+
+```go
+import (
+ "github.com/matteoarella/pedersen"
+)
+
+schemeParts := 5
+schemeThreshold := 3
+group := /* cyclic group */
+x := /* abscissa related to the shareholder part */
+share := /* secret share received from the dealer */
+commitments := /* commitments broadcast from the dealer */
+
+p, err := pedersen.NewPedersen(schemeParts, schemeThreshold, pedersen.CyclicGroup(group))
+if err != nil {
+ panic(err)
+}
+
+err = p.Verify(x, share, commitments)
+if err != nil {
+ panic(err)
+}
+```
+
+Additionally, the *dealer* can also collect every secret shares and then verify all of them in a
+single step as is shown here:
+
+```go
+import (
+ "github.com/matteoarella/pedersen"
+)
+
+schemeParts := 5
+schemeThreshold := 3
+group := /* cyclic group */
+shares := /* secret shares assembled from every secret parts */
+
+p, err := pedersen.NewPedersen(schemeParts, schemeThreshold, pedersen.CyclicGroup(group))
+if err != nil {
+ panic(err)
+}
+
+err = p.VerifyShares(shares)
+if err != nil {
+ panic(err)
+}
+```
diff --git a/docs/docs/guides/_category_.yml b/docs/docs/guides/_category_.yml
new file mode 100644
index 0000000..bb4c940
--- /dev/null
+++ b/docs/docs/guides/_category_.yml
@@ -0,0 +1,5 @@
+---
+label: 'Guides'
+position: 4
+link:
+ type: generated-index
diff --git a/docs/docs/intro.mdx b/docs/docs/intro.mdx
new file mode 100644
index 0000000..ce84101
--- /dev/null
+++ b/docs/docs/intro.mdx
@@ -0,0 +1,27 @@
+---
+slug: /
+sidebar_position: 1
+---
+
+# Overview
+
+> Secret sharing (also called secret splitting) refers to methods for distributing a secret among a group, in such a way that no individual holds any intelligible information about the secret, but when a sufficient number of individuals combine their 'shares', the secret may be reconstructed.
+> [Wikipedia](https://en.wikipedia.org/wiki/Secret_sharing)
+
+Secret sharing schemes are ideal for storing information that is highly sensitive
+and highly important, like for example encryption keys. Each of these pieces of
+information must be kept highly confidential, as their exposure could be disastrous;
+however, it is also critical that they should not be lost. Traditional methods for
+encryption are ill-suited for simultaneously achieving high levels of confidentiality
+and reliability. This is because when storing the encryption key, one must choose
+between keeping a single copy of the key in one location for maximum secrecy,
+or keeping multiple copies of the key in different locations for greater reliability.
+Increasing reliability of the key by storing multiple copies lowers confidentiality by
+creating additional attack vectors. Secret sharing schemes address this problem,
+and allow arbitrarily high levels of confidentiality and reliability to be achieved.
+
+> In cryptography, a secret sharing scheme is verifiable if auxiliary information is included that allows players to verify their shares as consistent.
+> [Wikipedia](https://en.wikipedia.org/wiki/Verifiable_secret_sharing)
+
+The `pedersen` package implements verifiable secret sharing procedures that are defined by [Pedersen Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing](https://link.springer.com/chapter/10.1007/3-540-46766-1_9) conference paper.
+
diff --git a/docs/docs/reference/01-go.mdx b/docs/docs/reference/01-go.mdx
new file mode 100644
index 0000000..dbfd948
--- /dev/null
+++ b/docs/docs/reference/01-go.mdx
@@ -0,0 +1,7 @@
+---
+sidebar_position: 1
+---
+
+# Go API
+
+Go API package documentation can be found [here](https://pkg.go.dev/github.com/matteoarella/pedersen).
diff --git a/docs/docs/reference/02-cli.mdx b/docs/docs/reference/02-cli.mdx
new file mode 100644
index 0000000..82c5200
--- /dev/null
+++ b/docs/docs/reference/02-cli.mdx
@@ -0,0 +1,168 @@
+---
+sidebar_position: 2
+---
+
+# CLI
+
+Pedersen provides a CLI binary for managing the sharing and the reconstruction of a secret.
+
+```
+$ pedersen --help
+Usage:
+ [command]
+
+Available Commands:
+ combine Combine Pedersen shares
+ completion Generate the autocompletion script for the specified shell
+ generate Generate Pedersen parameters
+ help Help about any command
+ split Split secret into Pedersen shares
+ verify Verify Pedersen shares or parts
+ version Show the Pedersen version information
+
+Flags:
+ -h, --help help for this command
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+
+Use " [command] --help" for more information about a command.
+```
+
+## Generate
+
+```
+$ pedersen generate --help
+Generate Pedersen parameters
+
+Usage:
+ generate [flags]
+
+Flags:
+ -b, --bits int prime bits size (default 128)
+ --format FileFmt file format. allowed: ""yaml\", \"json\", \"xml""
+ -h, --help help for generate
+ -o, --out string output file
+ --perm uint32 output file permissions (default 256)
+
+Global Flags:
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+```
+
+## Split
+
+```
+$ pedersen split --help
+Split secret into Pedersen shares
+
+Usage:
+ split [flags]
+
+Flags:
+ --commitments string commitments file
+ --format FileFmt file format. allowed: ""yaml\", \"json\", \"xml""
+ -g, --group string group file
+ -h, --help help for split
+ -i, --in string input file
+ -p, --parts int shares parts (default 5)
+ --perm uint32 output file permissions (default 256)
+ --shares string secret shares files regex expression
+ -t, --threshold int shares threshold (default 3)
+
+Global Flags:
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+```
+
+## Combine
+
+```
+$ pedersen combine --help
+Combine Pedersen shares
+
+Usage:
+ combine [flags]
+
+Flags:
+ --commitments string commitments file
+ --format FileFmt file format. allowed: ""xml\", \"yaml\", \"json""
+ -g, --group string group file
+ -h, --help help for combine
+ -o, --out string output file
+ -p, --parts int shares parts (default 5)
+ --perm uint32 output file permissions (default 256)
+ --shares string secret shares files regex expression
+ -t, --threshold int shares threshold (default 3)
+ -v, --verify verify shares before combine (default true)
+
+Global Flags:
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+```
+
+## Verify
+
+```
+$ pedersen verify --help
+Verify Pedersen shares or parts
+
+Usage:
+ verify [command]
+
+Available Commands:
+ part Verify Pedersen part
+ shares Verify Pedersen shares
+
+Flags:
+ -h, --help help for verify
+
+Global Flags:
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+
+Use " verify [command] --help" for more information about a command.
+```
+
+### Verify secret part
+
+```
+$ pedersen verify part --help
+Verify Pedersen part
+
+Usage:
+ verify part [flags]
+
+Flags:
+ --commitments string commitments file
+ -g, --group string group file
+ -h, --help help for part
+ -p, --parts int shares parts (default 5)
+ --share string secret shares file
+ -t, --threshold int shares threshold (default 3)
+
+Global Flags:
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+```
+
+### Verify secret shares
+
+```
+$ pedersen verify shares --help
+Verify Pedersen shares
+
+Usage:
+ verify shares [flags]
+
+Flags:
+ --commitments string commitments file
+ -g, --group string group file
+ -h, --help help for shares
+ -p, --parts int shares parts (default 5)
+ --shares string secret shares files regex expression
+ -t, --threshold int shares threshold (default 3)
+
+Global Flags:
+ --logfile string logging file
+ --loglevel string logging level (default "INFO")
+```
diff --git a/docs/docs/reference/_category_.yml b/docs/docs/reference/_category_.yml
new file mode 100644
index 0000000..16bcda0
--- /dev/null
+++ b/docs/docs/reference/_category_.yml
@@ -0,0 +1,5 @@
+---
+label: 'Reference'
+position: 3
+link:
+ type: generated-index
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
new file mode 100644
index 0000000..0875e2d
--- /dev/null
+++ b/docs/docusaurus.config.js
@@ -0,0 +1,141 @@
+// @ts-check
+// Note: type annotations allow type checking and IDEs autocompletion
+
+const lightCodeTheme = require('prism-react-renderer/themes/github');
+const darkCodeTheme = require('prism-react-renderer/themes/dracula');
+const math = require('remark-math');
+const katex = require('rehype-katex');
+require('dotenv').config();
+
+/** @type {import('@docusaurus/types').Config} */
+const config = {
+ title: 'Pedersen verifiable secret sharing',
+ tagline: 'PVSS',
+ favicon: 'img/favicon.ico',
+
+ // Set the production url of your site here
+ url: process.env.DOCS_URL,
+ // Set the // pathname under which your site is served
+ // For GitHub pages deployment, it is often '//'
+ baseUrl: process.env.NODE_ENV === 'dev' ? '/' : process.env.REPO_NAME,
+
+ // GitHub pages deployment config.
+ // If you aren't using GitHub pages, you don't need these.
+ organizationName: process.env.ORGANIZATION_NAME, // Usually your GitHub org/user name.
+ projectName: process.env.REPO_NAME, // Usually your repo name.
+
+ onBrokenLinks: 'throw',
+ onBrokenMarkdownLinks: 'warn',
+
+ // Even if you don't use internalization, you can use this field to set useful
+ // metadata like html lang. For example, if your site is Chinese, you may want
+ // to replace "en" with "zh-Hans".
+ i18n: {
+ defaultLocale: 'en',
+ locales: ['en'],
+ },
+
+ stylesheets: [
+ {
+ href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
+ type: 'text/css',
+ integrity:
+ 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM',
+ crossorigin: 'anonymous',
+ },
+ ],
+
+ presets: [
+ [
+ 'classic',
+ /** @type {import('@docusaurus/preset-classic').Options} */
+ ({
+ docs: {
+ routeBasePath: '/', // Serve the docs at the site's root
+ sidebarPath: require.resolve('./sidebars.js'),
+ // Please change this to your repo.
+ // Remove this to remove the "edit this page" links.
+ editUrl: process.env.DOCS_EDIT_URL,
+ remarkPlugins: [math],
+ rehypePlugins: [katex],
+ },
+ blog: false, // disable the blog plugin
+ theme: {
+ customCss: require.resolve('./src/css/custom.css'),
+ },
+ }),
+ ],
+ ],
+
+ themeConfig:
+ /** @type {import('@docusaurus/preset-classic').ThemeConfig} */
+ ({
+ image: 'img/docusaurus-social-card.jpg',
+ highlight: {
+ // default, Highlight.js theme to use for syntax highlighting in code blocks.
+ theme: 'solarized-dark',
+ },
+ navbar: {
+ title: 'Pedersen verifiable secret sharing',
+ logo: {
+ alt: 'Pedersen Logo',
+ src: 'img/logo.svg',
+ },
+ items: [
+ {
+ href: process.env.REPO_URL,
+ label: 'GitHub',
+ position: 'right',
+ },
+ ],
+ },
+ footer: {
+ style: 'dark',
+ copyright: `Copyright © ${new Date().getFullYear()} Pedersen authors. All rights reserved.`,
+ },
+ prism: {
+ theme: lightCodeTheme,
+ darkTheme: darkCodeTheme,
+ additionalLanguages: ['powershell'],
+ },
+ metadata: [{name: 'keywords', content: 'pedersen, secret sharing, verifiable secret sharing, pedersen verifiable secret sharing'}],
+ docs: {
+ sidebar: {
+ hideable: true,
+ autoCollapseCategories: true,
+ },
+ },
+ algolia: {
+ appId: process.env.ALGOLIA_APP_ID,
+
+ apiKey: process.env.ALGOLIA_SEARCH_API_KEY,
+
+ indexName: process.env.ALGOLIA_INDEX_NAME,
+
+ contextualSearch: true,
+
+ // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them.
+ // externalUrlRegex: 'external\\.com|domain\\.com',
+
+ // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs
+ /*replaceSearchResultPathname: {
+ from: '/docs/', // or as RegExp: /\/docs\//
+ to: '/',
+ },*/
+
+ // Optional: Algolia search parameters
+ searchParameters: {},
+
+ // Optional: path for search page that enabled by default (`false` to disable it)
+ searchPagePath: 'search',
+
+ //... other Algolia params
+ },
+ }),
+ customFields: {
+ version: process.env.VERSION,
+ repoUrl: process.env.REPO_URL,
+ }
+};
+
+module.exports = config;
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 0000000..816b4e3
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "docs",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "docusaurus": "docusaurus",
+ "start": "docusaurus start",
+ "build": "docusaurus build",
+ "swizzle": "docusaurus swizzle",
+ "deploy": "docusaurus deploy",
+ "clear": "docusaurus clear",
+ "serve": "docusaurus serve",
+ "write-translations": "docusaurus write-translations",
+ "write-heading-ids": "docusaurus write-heading-ids",
+ "typecheck": "tsc"
+ },
+ "dependencies": {
+ "@docusaurus/core": "2.4.1",
+ "@docusaurus/preset-classic": "2.4.1",
+ "@mdx-js/react": "^1.6.22",
+ "clsx": "^1.2.1",
+ "dotenv": "^16.3.1",
+ "hast-util-is-element": "1.1.0",
+ "prism-react-renderer": "^1.3.5",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "rehype-katex": "5",
+ "remark-math": "3"
+ },
+ "devDependencies": {
+ "@docusaurus/module-type-aliases": "2.4.1",
+ "@tsconfig/docusaurus": "^1.0.5",
+ "typescript": "^4.7.4"
+ },
+ "browserslist": {
+ "production": [
+ ">0.5%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "engines": {
+ "node": ">=16.14"
+ }
+}
diff --git a/docs/sidebars.js b/docs/sidebars.js
new file mode 100644
index 0000000..3fdd0c6
--- /dev/null
+++ b/docs/sidebars.js
@@ -0,0 +1,19 @@
+/**
+ * Creating a sidebar enables you to:
+ - create an ordered group of docs
+ - render a sidebar for each doc of that group
+ - provide next/previous navigation
+
+ The sidebars can be generated from the filesystem, or explicitly defined here.
+
+ Create as many sidebars as you want.
+ */
+
+// @ts-check
+
+/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
+const sidebars = {
+ docsSidebar: [{type: 'autogenerated', dirName: '.'}],
+};
+
+module.exports = sidebars;
diff --git a/docs/src/components/BinariesTabs.tsx b/docs/src/components/BinariesTabs.tsx
new file mode 100644
index 0000000..0ad7b17
--- /dev/null
+++ b/docs/src/components/BinariesTabs.tsx
@@ -0,0 +1,82 @@
+import React, {PropsWithChildren} from 'react';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import CodeBlock from '@theme/CodeBlock';
+import CodeBlockLine from '@theme/CodeBlock/Line';
+
+export interface VariantProps {
+ readonly value: string;
+ readonly label: string;
+ readonly binary: string;
+ readonly powershell: boolean;
+ readonly default?: boolean;
+}
+
+export function Variant(props: VariantProps) {}
+
+function RenderVariant(props: VariantProps): JSX.Element {
+ const {siteConfig} = useDocusaurusContext();
+ if (props.powershell) {
+ return (
+
+
+ {`$VERSION = "${siteConfig.customFields.version}"; \`
+$BINARY = "${props.binary}"; \`
+New-Item -ItemType Directory -Force -Path $env:ProgramFiles\\Pedersen\\bin; \`
+Invoke-WebRequest -OutFile $env:ProgramFiles\\Pedersen\\bin\\pedersen https://github.com/matteoarella/pedersen/releases/download/$VERSION/$BINARY`}
+
+
+ Then add $env:ProgramFiles\Pedersen\bin to your
+ $env:Path system variable.
+
+ );
+ }
+ return (
+
+
+ {`VERSION=${siteConfig.customFields.version} \\
+BINARY=${props.binary} \\
+curl -o /bin/pedersen https://github.com/matteoarella/pedersen/releases/download/$VERSION/$BINARY && \\
+chmod +x /bin/pedersen`}
+
+
+ );
+}
+
+export interface PlatformProps {
+ readonly value: string;
+ readonly label: string;
+ readonly default?: boolean;
+}
+
+export function Platform(props: PropsWithChildren) {}
+
+function RenderPlatform(platform: PropsWithChildren): JSX.Element {
+ return (
+
+ {
+ React.Children.map(platform.children, (variant, _) => {
+ const item = variant as React.ReactElement;
+ const props: VariantProps = {
+ powershell: platform.value === 'windows',
+ ...item.props,
+ }
+ return RenderVariant(props)
+ })
+ }
+
+ )
+}
+
+export function BinariesTabs({children}: {
+ children: React.ReactElement>
+}): JSX.Element {
+ return ({
+ React.Children.map(children, (platform, _) => {
+ return RenderPlatform(platform.props)
+ })
+ })
+};
diff --git a/docs/src/components/RepoUrl.tsx b/docs/src/components/RepoUrl.tsx
new file mode 100644
index 0000000..adda9de
--- /dev/null
+++ b/docs/src/components/RepoUrl.tsx
@@ -0,0 +1,14 @@
+import React, {PropsWithChildren} from 'react';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+
+export interface RepoUrlProps {
+ readonly name: string;
+}
+
+export default function RepoUrl(props: RepoUrlProps): JSX.Element {
+ const {siteConfig} = useDocusaurusContext();
+ const url = siteConfig.customFields!.repoUrl as string || ""
+ return (
+ {props.name}
+ );
+}
diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css
new file mode 100644
index 0000000..55c9296
--- /dev/null
+++ b/docs/src/css/custom.css
@@ -0,0 +1,59 @@
+/**
+ * Any CSS included here will be global. The classic template
+ * bundles Infima by default. Infima is a CSS framework designed to
+ * work well for content-centric websites.
+ */
+
+/* You can override the default Infima variables here. */
+:root {
+ --ifm-color-primary: #2e8555;
+ --ifm-color-primary-dark: #29784c;
+ --ifm-color-primary-darker: #277148;
+ --ifm-color-primary-darkest: #205d3b;
+ --ifm-color-primary-light: #33925d;
+ --ifm-color-primary-lighter: #359962;
+ --ifm-color-primary-lightest: #3cad6e;
+ --ifm-code-font-size: 95%;
+ --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
+}
+/*:root {
+ --ifm-color-primary: #073642;
+ --ifm-color-primary-dark: #06313b;
+ --ifm-color-primary-darker: #062e38;
+ --ifm-color-primary-darkest: #05262e;
+ --ifm-color-primary-light: #083b49;
+ --ifm-color-primary-lighter: #083e4c;
+ --ifm-color-primary-lightest: #094656;
+ --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
+}*/
+/*:root {
+ --ifm-color-primary: #216373;
+ --ifm-color-primary-dark: #1e5968;
+ --ifm-color-primary-darker: #1c5462;
+ --ifm-color-primary-darkest: #174551;
+ --ifm-color-primary-light: #246d7f;
+ --ifm-color-primary-lighter: #267284;
+ --ifm-color-primary-lightest: #2b8196;
+}*/
+
+/* For readability concerns, you should choose a lighter palette in dark mode. */
+[data-theme='dark'] {
+ --ifm-color-primary: #25c2a0;
+ --ifm-color-primary-dark: #21af90;
+ --ifm-color-primary-darker: #1fa588;
+ --ifm-color-primary-darkest: #1a8870;
+ --ifm-color-primary-light: #29d5b0;
+ --ifm-color-primary-lighter: #32d8b4;
+ --ifm-color-primary-lightest: #4fddbf;
+ --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
+}
+/*[data-theme='dark'] {
+ --ifm-color-primary: #839496;
+ --ifm-color-primary-dark: #748789;
+ --ifm-color-primary-darker: #6e7f81;
+ --ifm-color-primary-darkest: #617072;
+ --ifm-color-primary-light: #92a1a3;
+ --ifm-color-primary-lighter: #9aa8a9;
+ --ifm-color-primary-lightest: #b1bbbd;
+ --ifm-background-color: #002b36;
+}*/
diff --git a/docs/static/.nojekyll b/docs/static/.nojekyll
new file mode 100644
index 0000000..e69de29
diff --git a/docs/static/img/docusaurus-social-card.jpg b/docs/static/img/docusaurus-social-card.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..ffcb448210e1a456cb3588ae8b396a597501f187
GIT binary patch
literal 55746
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 |