mirror of
https://github.com/luxfi/threshold.git
synced 2026-07-27 04:01:59 +00:00
chore: use published lux packages, remove local replace directives
- Update to crypto v1.17.28, fhe v1.3.0, corona v0.2.0 - Remove local replace directives for lattice, corona, crypto, fhe - Keep gpu replace directive (module path github.com/luxfi/gpu needs repo at github.com/luxfi/gpu, currently at github.com/luxcpp/gpu) - All protocol tests passing: BLS, FROST, CMP, Quasar
This commit is contained in:
@@ -1,202 +1,161 @@
|
||||
Lux Research License with Patent Reservation
|
||||
Version 1.0, December 2025
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
Copyright (c) 2020-2025 Lux Industries Inc.
|
||||
All rights reserved.
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
PATENT PENDING TECHNOLOGY
|
||||
Contact: oss@lux.network
|
||||
|
||||
1. Definitions.
|
||||
================================================================================
|
||||
IMPORTANT NOTICE
|
||||
================================================================================
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
This software contains patent-pending technology. Key innovations include but
|
||||
are not limited to:
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
THRESHOLD CRYPTOGRAPHY INNOVATIONS:
|
||||
- LSS (Lagrange Secret Sharing) Dynamic Resharing Without Key Reconstruction
|
||||
- Multiplicative Blinding Protocols (Protocol I & II) for Share Privacy
|
||||
- Corona Post-Quantum Lattice-Based Threshold Signatures
|
||||
- Threshold BIP-32 Deterministic Key Derivation
|
||||
- Identifiable Abort Protocol with Blame Assignment
|
||||
- Automatic Generation-Based Rollback with Party Eviction
|
||||
- Advanced Zero-Knowledge Proof Suite (17 systems)
|
||||
- Multi-Chain Adapter Pattern for 20+ Blockchain Support
|
||||
- Grouped Threshold Signatures for 10,000+ Validator Scaling
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
Commercial use of any patented technology requires a separate license.
|
||||
Contact oss@lux.network for commercial licensing inquiries.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
================================================================================
|
||||
LICENSE TERMS
|
||||
================================================================================
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
1. DEFINITIONS
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
"Software" means the source code, object code, documentation, and any
|
||||
related materials included in this repository.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
"Research Use" means use of the Software solely for non-commercial
|
||||
academic research, education, personal study, or evaluation purposes.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
"Commercial Use" means any use of the Software or any patent claims
|
||||
covered by the Software in connection with a product or service offered
|
||||
for sale or fee, or any internal use by a for-profit entity, or any use
|
||||
to generate revenue directly or indirectly.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
"Patent Claims" means any patent claims owned or controlled by Lux
|
||||
Industries Inc. that cover any aspect of the Software's implementation
|
||||
or any method, system, or process described therein.
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
"Lux Network" means the decentralized blockchain network operated using
|
||||
software distributed by Lux Industries Inc.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
2. GRANT OF RESEARCH LICENSE
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
Subject to the terms and conditions of this License, Lux Industries Inc.
|
||||
hereby grants you a limited, non-exclusive, non-transferable, revocable,
|
||||
royalty-free license to:
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
(a) Use, copy, and modify the Software solely for Research Use;
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
(b) Create derivative works of the Software solely for Research Use;
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
(c) Publish academic papers and research findings based on the Software,
|
||||
provided that proper attribution is given; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
(d) Contribute modifications back to the original repository.
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
3. RESTRICTIONS
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
You may NOT:
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
(a) Use the Software or any Patent Claims for any Commercial Use without
|
||||
first obtaining a commercial license from Lux Industries Inc.;
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
(b) Use the Software to create or operate any custody, MPC, or threshold
|
||||
signature service that competes with Lux Network services;
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
(c) Sublicense, sell, lease, or otherwise transfer rights in the
|
||||
Software to any third party;
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
(d) Remove or alter any copyright, patent, trademark, or attribution
|
||||
notices contained in the Software;
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
(e) Use the Software in any manner that infringes the Patent Claims
|
||||
without authorization; or
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
(f) File any patent applications covering technology substantially
|
||||
derived from the Software.
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
4. PATENT RIGHTS RESERVATION
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
ALL PATENT RIGHTS ARE EXPRESSLY RESERVED by Lux Industries Inc.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
This License does NOT grant any license or rights under any Patent Claims.
|
||||
The grant of a copyright license herein does NOT include any express or
|
||||
implied patent license.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Any use of the Software that would infringe Patent Claims requires a
|
||||
separate patent license, which must be obtained in writing from:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Lux Industries Inc.
|
||||
Email: oss@lux.network
|
||||
Subject: Patent License Request
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
5. LUX NETWORK PRIMARY NETWORK EXCEPTION
|
||||
|
||||
Notwithstanding the above restrictions, this License grants an automatic
|
||||
license to use the Software and Patent Claims solely for the purpose of
|
||||
operating a node on the Lux Network primary network (mainnet and testnet),
|
||||
subject to the network's terms of service and validator requirements.
|
||||
|
||||
6. CONTRIBUTIONS
|
||||
|
||||
By submitting contributions to this repository, you agree to license
|
||||
your contributions under the same terms as this License and assign all
|
||||
patent rights in your contributions to Lux Industries Inc.
|
||||
|
||||
7. DISCLAIMER OF WARRANTY
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
||||
|
||||
8. LIMITATION OF LIABILITY
|
||||
|
||||
IN NO EVENT SHALL LUX INDUSTRIES INC. BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY KIND ARISING OUT
|
||||
OF THE USE OF OR INABILITY TO USE THE SOFTWARE.
|
||||
|
||||
9. TERMINATION
|
||||
|
||||
This License and all rights granted hereunder will terminate immediately
|
||||
upon any breach of this License by you.
|
||||
|
||||
10. GOVERNING LAW
|
||||
|
||||
This License shall be governed by the laws of the State of Delaware,
|
||||
United States.
|
||||
|
||||
11. COMMERCIAL LICENSING
|
||||
|
||||
For commercial licensing inquiries, please contact:
|
||||
|
||||
Lux Industries Inc.
|
||||
Email: oss@lux.network
|
||||
|
||||
================================================================================
|
||||
ATTRIBUTION
|
||||
================================================================================
|
||||
|
||||
When using this Software for research purposes, please cite:
|
||||
|
||||
Lux Threshold: Post-Quantum Threshold Cryptography Library
|
||||
Copyright (c) 2020-2025 Lux Industries Inc.
|
||||
https://lux.network
|
||||
|
||||
================================================================================
|
||||
|
||||
END OF LICENSE TERMS
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
Lux Ecosystem License
|
||||
Version 1.2, December 2025
|
||||
|
||||
Copyright (c) 2020-2025 Lux Industries Inc.
|
||||
All rights reserved.
|
||||
|
||||
TECHNOLOGY PORTFOLIO - PATENT APPLICATIONS PLANNED
|
||||
Contact: licensing@lux.network
|
||||
|
||||
================================================================================
|
||||
TERMS AND CONDITIONS
|
||||
================================================================================
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Lux Primary Network" means the official Lux blockchain with Network ID=1
|
||||
and EVM Chain ID=96369.
|
||||
|
||||
"Authorized Network" means the Lux Primary Network, official testnets/devnets,
|
||||
and any L1/L2/L3 chain descending from the Lux Primary Network.
|
||||
|
||||
"Descending Chain" means an L1/L2/L3 chain built on, anchored to, or deriving
|
||||
security from the Lux Primary Network or its authorized testnets.
|
||||
|
||||
"Research Use" means non-commercial academic research, education, personal
|
||||
study, or evaluation purposes.
|
||||
|
||||
"Commercial Use" means any use in connection with a product or service
|
||||
offered for sale or fee, internal use by a for-profit entity, or any use
|
||||
to generate revenue.
|
||||
|
||||
2. GRANT OF LICENSE
|
||||
|
||||
Subject to these terms, Lux Industries Inc grants you a non-exclusive,
|
||||
royalty-free license to:
|
||||
|
||||
(a) Use for Research Use without restriction;
|
||||
|
||||
(b) Operate on the Lux Primary Network (Network ID=1, EVM Chain ID=96369);
|
||||
|
||||
(c) Operate on official Lux testnets and devnets;
|
||||
|
||||
(d) Operate L1/L2/L3 chains descending from the Lux Primary Network;
|
||||
|
||||
(e) Build applications within the Lux ecosystem;
|
||||
|
||||
(f) Contribute improvements back to the original repositories.
|
||||
|
||||
3. RESTRICTIONS
|
||||
|
||||
Without a commercial license from Lux Industries Inc, you may NOT:
|
||||
|
||||
(a) Fork the Lux Network or any Lux software;
|
||||
|
||||
(b) Create competing networks not descending from Lux Primary Network;
|
||||
|
||||
(c) Use for Commercial Use outside the Lux ecosystem;
|
||||
|
||||
(d) Sublicense or transfer rights outside the Lux ecosystem;
|
||||
|
||||
(e) Use to create competing blockchain networks, exchanges, custody
|
||||
services, or cryptographic systems outside the Lux ecosystem.
|
||||
|
||||
4. NO FORKS POLICY
|
||||
|
||||
Lux Industries Inc maintains ZERO TOLERANCE for unauthorized forks.
|
||||
Any fork or deployment on an unauthorized network constitutes:
|
||||
|
||||
(a) Breach of this license;
|
||||
(b) Grounds for immediate legal action.
|
||||
|
||||
5. RIGHTS RESERVATION
|
||||
|
||||
All rights not explicitly granted are reserved by Lux Industries Inc.
|
||||
|
||||
We plan to apply for patent protection for the technology in this
|
||||
repository. Any implementation outside the Lux ecosystem may require
|
||||
a separate commercial license.
|
||||
|
||||
6. DISCLAIMER OF WARRANTY
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
|
||||
7. LIMITATION OF LIABILITY
|
||||
|
||||
IN NO EVENT SHALL LUX INDUSTRIES INC BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
|
||||
|
||||
8. TERMINATION
|
||||
|
||||
This license terminates immediately upon any breach, including but not
|
||||
limited to deployment on unauthorized networks or creation of forks.
|
||||
|
||||
9. GOVERNING LAW
|
||||
|
||||
This License shall be governed by the laws of the State of Delaware.
|
||||
|
||||
10. COMMERCIAL LICENSING
|
||||
|
||||
For commercial use outside the Lux ecosystem:
|
||||
|
||||
Lux Industries Inc.
|
||||
Email: licensing@lux.network
|
||||
Subject: Commercial License Request
|
||||
|
||||
================================================================================
|
||||
TL;DR
|
||||
================================================================================
|
||||
|
||||
- Research/academic use = OK
|
||||
- Lux Primary Network (Network ID=1, Chain ID=96369) = OK
|
||||
- L1/L2/L3 chains descending from Lux Primary Network = OK
|
||||
- Commercial products outside Lux ecosystem = Contact licensing@lux.network
|
||||
- Forks = Absolutely not
|
||||
|
||||
================================================================================
|
||||
|
||||
See LP-0012 for full licensing documentation:
|
||||
https://github.com/luxfi/lps/blob/main/LPs/lp-0012-ecosystem-licensing.md
|
||||
@@ -3,15 +3,17 @@ module github.com/luxfi/threshold
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf
|
||||
github.com/cronokirby/saferith v0.33.0
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
|
||||
// External dependencies
|
||||
github.com/cloudflare/circl v1.6.2 // BLS12-381 curve operations
|
||||
github.com/fxamacker/cbor/v2 v2.9.0
|
||||
github.com/luxfi/crypto v1.17.25
|
||||
github.com/luxfi/log v1.1.26
|
||||
github.com/onsi/ginkgo/v2 v2.23.4
|
||||
github.com/onsi/gomega v1.38.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
// Lux crypto stack - this is the HIGH-LEVEL orchestration layer
|
||||
// that consumes primitives from these packages (LP-5703, LP-5704)
|
||||
github.com/luxfi/crypto v1.17.28 // ECDSA, EdDSA, BLS curves
|
||||
github.com/luxfi/fhe v1.3.0 // FHE primitives for TFHE protocol
|
||||
github.com/luxfi/lattice/v7 v7.0.0 // indirect; Lattice ops for Corona (post-quantum) + GPU acceleration
|
||||
github.com/luxfi/corona v0.2.0 // Post-quantum threshold signatures (uses lattice/v7)
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/zeebo/blake3 v0.2.4
|
||||
@@ -20,33 +22,50 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cronokirby/saferith v0.33.0
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
github.com/luxfi/log v1.1.26
|
||||
github.com/onsi/ginkgo/v2 v2.27.3
|
||||
github.com/onsi/gomega v1.38.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ALTree/bigfloat v0.2.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/prometheus/common v0.67.4 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
google.golang.org/protobuf v1.36.7 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
// FHE requires local GPU binding (luxfi/gpu module path doesn't match git repo)
|
||||
// Threshold doesn't directly use GPU ops but needs fhe to build
|
||||
replace github.com/luxfi/gpu => /Users/z/work/luxcpp/gpu
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
github.com/ALTree/bigfloat v0.2.0 h1:AwNzawrpFuw55/YDVlcPw0F0cmmXrmngBHhVrvdXPvM=
|
||||
github.com/ALTree/bigfloat v0.2.0/go.mod h1:+NaH2gLeY6RPBPPQf4aRotPPStg+eXc8f9ZaE4vRfD4=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf h1:+0huF756d72ZdHxachRP5HYmvRkkOk8CLyKUmJM138c=
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
|
||||
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cronokirby/saferith v0.33.0 h1:TgoQlfsD4LIwx71+ChfRcIpjkw+RPOapDEVxa+LhwLo=
|
||||
github.com/cronokirby/saferith v0.33.0/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7kthcD7UzbnoA=
|
||||
@@ -13,58 +17,86 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvw
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
|
||||
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
||||
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
|
||||
github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
|
||||
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
|
||||
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
|
||||
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
|
||||
github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/luxfi/crypto v1.17.25 h1:emTejQ17bGj/1tu0ETB196Hbn6fJfo8wUNGIOWit6RM=
|
||||
github.com/luxfi/crypto v1.17.25/go.mod h1:v6eaW5ejuzW2gecChWKQqx4CtVhoM1b+e40ro/6WVPg=
|
||||
github.com/luxfi/crypto v1.17.28 h1:kjzuENMqZXUJWewbEn+M0b687E7sPesd2tNav78OsKI=
|
||||
github.com/luxfi/crypto v1.17.28/go.mod h1:v6eaW5ejuzW2gecChWKQqx4CtVhoM1b+e40ro/6WVPg=
|
||||
github.com/luxfi/fhe v1.3.0 h1:EIYbiRd700LoRVeSCweCaQxtk+R4K9Xvtea7XNhck4w=
|
||||
github.com/luxfi/fhe v1.3.0/go.mod h1:LujVisWCPj91y0XcmasyCJyi51VTopHMTkzlNOESYqc=
|
||||
github.com/luxfi/lattice/v7 v7.0.0 h1:d1vgan6mlb2KtwYfPc1g69uxVYaoVYZmlrIm4aCO88w=
|
||||
github.com/luxfi/lattice/v7 v7.0.0/go.mod h1:PFDdOkuGTQ0cbJMbKojzEJMGWUQmZW+wK9/wJ9F9fOs=
|
||||
github.com/luxfi/log v1.1.26 h1:ECnJ4wV7TF6WZ5gC6CD6ddZ4OhF+zhQij4bgVUhumDg=
|
||||
github.com/luxfi/log v1.1.26/go.mod h1:ACN1FtMlJ/NRuWROYH2TtiFmJuUbSG+OxINlRzJM/38=
|
||||
github.com/luxfi/corona v0.2.0 h1:DbLMZmE//2T2wXXS/gEkKZrTbre9LD+7EE/tz5SQszU=
|
||||
github.com/luxfi/corona v0.2.0/go.mod h1:SZ+aDLUdfSAtaTRaaYzeDZ5DNQiLaOCelSaIGjL21R0=
|
||||
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
|
||||
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
|
||||
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
|
||||
github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
|
||||
github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8=
|
||||
github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
|
||||
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
@@ -73,18 +105,22 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
@@ -95,8 +131,8 @@ golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
// Package quasar provides hybrid threshold signatures combining BLS (elliptic curve)
|
||||
// with Corona (post-quantum lattice-based) for security against both classical
|
||||
// and quantum computer attacks.
|
||||
//
|
||||
// Quasar signatures are "quantum-safe and reliable" - they provide:
|
||||
// - Immediate security via BLS12-381 pairing-based signatures
|
||||
// - Future-proof post-quantum security via Corona (Module-LWE)
|
||||
// - Threshold signing: t-of-n parties required to produce valid signatures
|
||||
// - Flexible verification: verify both components, BLS-only, or Corona-only
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// // Generate keys for 3 parties with threshold 2
|
||||
// dealer := &quasar.TrustedDealer{Threshold: 2, TotalParties: 3}
|
||||
// shares, err := dealer.GenerateShares(ctx, partyIDs)
|
||||
//
|
||||
// // Each party signs
|
||||
// proto := quasar.NewProtocol(shares[myID])
|
||||
// share, _ := proto.Sign(ctx, message, sessionID, prfKey, signerList)
|
||||
//
|
||||
// // Collect shares and finalize
|
||||
// proto.AddShare(theirShare)
|
||||
// sig, _ := proto.Finalize(message)
|
||||
//
|
||||
// // Verify (both BLS and Corona)
|
||||
// valid := quasar.Verify(groupKey, message, sig)
|
||||
package quasar
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/corona/threshold"
|
||||
blsThreshold "github.com/luxfi/threshold/protocols/bls"
|
||||
"github.com/luxfi/threshold/pkg/party"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotInitialized is returned when operations are attempted before setup.
|
||||
ErrNotInitialized = errors.New("quasar: not initialized")
|
||||
|
||||
// ErrCoronaFailed is returned when the post-quantum component fails.
|
||||
ErrCoronaFailed = errors.New("quasar: corona component failed")
|
||||
|
||||
// ErrBLSFailed is returned when the BLS component fails.
|
||||
ErrBLSFailed = errors.New("quasar: BLS component failed")
|
||||
|
||||
// ErrInsufficientShares is returned when not enough shares are collected.
|
||||
ErrInsufficientShares = errors.New("quasar: insufficient shares for threshold")
|
||||
|
||||
// ErrVerificationFailed is returned when signature verification fails.
|
||||
ErrVerificationFailed = errors.New("quasar: verification failed")
|
||||
)
|
||||
|
||||
// Config holds configuration for a Quasar threshold signing participant.
|
||||
type Config struct {
|
||||
// ID is this party's identifier
|
||||
ID party.ID
|
||||
|
||||
// Threshold is the minimum number of parties needed to sign (t in t-of-n)
|
||||
Threshold int
|
||||
|
||||
// TotalParties is n in t-of-n
|
||||
TotalParties int
|
||||
|
||||
// BLS configuration
|
||||
BLSSecretShare *bls.SecretKey
|
||||
BLSPublicKey *bls.PublicKey
|
||||
BLSVerifyKeys map[party.ID]*bls.PublicKey
|
||||
|
||||
// Corona (post-quantum) configuration
|
||||
CoronaShare *threshold.KeyShare
|
||||
CoronaGroupKey *threshold.GroupKey
|
||||
}
|
||||
|
||||
// GroupKey represents the combined public key for Quasar verification.
|
||||
type GroupKey struct {
|
||||
// BLS aggregate public key
|
||||
BLS *bls.PublicKey
|
||||
|
||||
// Corona group public key
|
||||
Corona *threshold.GroupKey
|
||||
}
|
||||
|
||||
// Bytes returns a serialized representation of the group key.
|
||||
func (gk *GroupKey) Bytes() []byte {
|
||||
if gk == nil {
|
||||
return nil
|
||||
}
|
||||
result := make([]byte, 0)
|
||||
if gk.BLS != nil {
|
||||
result = append(result, bls.PublicKeyToCompressedBytes(gk.BLS)...)
|
||||
}
|
||||
if gk.Corona != nil {
|
||||
result = append(result, gk.Corona.Bytes()...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SignatureShare represents a party's contribution to a Quasar signature.
|
||||
type SignatureShare struct {
|
||||
PartyID party.ID
|
||||
|
||||
// BLS component (elliptic curve)
|
||||
BLSShare *blsThreshold.SignatureShare
|
||||
|
||||
// Corona components (post-quantum lattice)
|
||||
CoronaRound1 *threshold.Round1Data
|
||||
CoronaRound2 *threshold.Round2Data
|
||||
}
|
||||
|
||||
// Signature represents a complete Quasar hybrid signature.
|
||||
// For full security, verifiers should check BOTH components.
|
||||
type Signature struct {
|
||||
// BLS signature (96 bytes compressed G2 point)
|
||||
BLS *bls.Signature
|
||||
|
||||
// Corona signature (post-quantum lattice-based)
|
||||
Corona *threshold.Signature
|
||||
|
||||
// MessageHash binds the signature to a specific message
|
||||
MessageHash []byte
|
||||
}
|
||||
|
||||
// HasCorona returns true if the signature includes a post-quantum component.
|
||||
func (s *Signature) HasCorona() bool {
|
||||
return s != nil && s.Corona != nil
|
||||
}
|
||||
|
||||
// HasBLS returns true if the signature includes a BLS component.
|
||||
func (s *Signature) HasBLS() bool {
|
||||
return s != nil && s.BLS != nil
|
||||
}
|
||||
|
||||
// Bytes serializes the Quasar signature.
|
||||
// Format: [1-byte flags][BLS sig][optional Corona sig]
|
||||
func (s *Signature) Bytes() ([]byte, error) {
|
||||
if s == nil || s.BLS == nil {
|
||||
return nil, ErrNotInitialized
|
||||
}
|
||||
|
||||
blsBytes := bls.SignatureToBytes(s.BLS)
|
||||
|
||||
var flags byte = 0x00
|
||||
if s.Corona != nil {
|
||||
flags |= 0x01 // Corona present
|
||||
}
|
||||
|
||||
result := make([]byte, 1+len(blsBytes))
|
||||
result[0] = flags
|
||||
copy(result[1:], blsBytes)
|
||||
|
||||
// TODO: Append Corona serialization when present
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Protocol orchestrates Quasar hybrid threshold signing.
|
||||
type Protocol struct {
|
||||
config *Config
|
||||
mu sync.Mutex
|
||||
|
||||
// BLS threshold protocol
|
||||
blsConfig *blsThreshold.Config
|
||||
|
||||
// Corona signer (post-quantum)
|
||||
coronaSigner *threshold.Signer
|
||||
signers []int
|
||||
|
||||
// Collected shares
|
||||
blsShares map[party.ID]*blsThreshold.SignatureShare
|
||||
coronaRound1 map[int]*threshold.Round1Data
|
||||
coronaRound2 map[int]*threshold.Round2Data
|
||||
}
|
||||
|
||||
// NewProtocol creates a new Quasar signing protocol instance.
|
||||
func NewProtocol(config *Config) (*Protocol, error) {
|
||||
if config == nil {
|
||||
return nil, ErrNotInitialized
|
||||
}
|
||||
if config.BLSSecretShare == nil {
|
||||
return nil, fmt.Errorf("%w: BLS secret share required", ErrNotInitialized)
|
||||
}
|
||||
|
||||
// Create BLS threshold config
|
||||
blsConfig := blsThreshold.NewConfig(
|
||||
config.ID,
|
||||
config.Threshold,
|
||||
config.TotalParties,
|
||||
config.BLSSecretShare,
|
||||
config.BLSPublicKey,
|
||||
config.BLSVerifyKeys,
|
||||
)
|
||||
|
||||
// Create Corona signer if configured
|
||||
var coronaSigner *threshold.Signer
|
||||
if config.CoronaShare != nil {
|
||||
coronaSigner = threshold.NewSigner(config.CoronaShare)
|
||||
}
|
||||
|
||||
return &Protocol{
|
||||
config: config,
|
||||
blsConfig: blsConfig,
|
||||
coronaSigner: coronaSigner,
|
||||
blsShares: make(map[party.ID]*blsThreshold.SignatureShare),
|
||||
coronaRound1: make(map[int]*threshold.Round1Data),
|
||||
coronaRound2: make(map[int]*threshold.Round2Data),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sign creates a Quasar signature share with both BLS and Corona components.
|
||||
func (p *Protocol) Sign(ctx context.Context, message []byte, sessionID int, prfKey []byte, signerIndices []int) (*SignatureShare, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
share := &SignatureShare{
|
||||
PartyID: p.config.ID,
|
||||
}
|
||||
|
||||
// Create BLS signature share
|
||||
blsShare, err := p.blsConfig.Sign(message)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrBLSFailed, err)
|
||||
}
|
||||
share.BLSShare = blsShare
|
||||
|
||||
// Create Corona Round 1 data if configured
|
||||
if p.coronaSigner != nil && len(signerIndices) > 0 {
|
||||
p.signers = signerIndices
|
||||
round1 := p.coronaSigner.Round1(sessionID, prfKey, signerIndices)
|
||||
share.CoronaRound1 = round1
|
||||
}
|
||||
|
||||
return share, nil
|
||||
}
|
||||
|
||||
// AddBLSShare adds a BLS signature share from another party.
|
||||
func (p *Protocol) AddBLSShare(share *blsThreshold.SignatureShare) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.blsShares[share.PartyID] = share
|
||||
}
|
||||
|
||||
// AddShare adds a complete signature share from another party.
|
||||
func (p *Protocol) AddShare(share *SignatureShare) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if share.BLSShare != nil {
|
||||
p.blsShares[share.PartyID] = share.BLSShare
|
||||
}
|
||||
if share.CoronaRound1 != nil {
|
||||
p.coronaRound1[share.CoronaRound1.PartyID] = share.CoronaRound1
|
||||
}
|
||||
if share.CoronaRound2 != nil {
|
||||
p.coronaRound2[share.CoronaRound2.PartyID] = share.CoronaRound2
|
||||
}
|
||||
}
|
||||
|
||||
// AddCoronaRound1 adds Corona Round 1 data from another party.
|
||||
func (p *Protocol) AddCoronaRound1(data *threshold.Round1Data) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.coronaRound1[data.PartyID] = data
|
||||
}
|
||||
|
||||
// CompleteCoronaRound2 completes Corona signing after Round 1 data collection.
|
||||
func (p *Protocol) CompleteCoronaRound2(sessionID int, message string, prfKey []byte) (*threshold.Round2Data, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if p.coronaSigner == nil {
|
||||
return nil, fmt.Errorf("%w: corona not configured", ErrNotInitialized)
|
||||
}
|
||||
|
||||
round2, err := p.coronaSigner.Round2(sessionID, message, prfKey, p.signers, p.coronaRound1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrCoronaFailed, err)
|
||||
}
|
||||
|
||||
return round2, nil
|
||||
}
|
||||
|
||||
// AddCoronaRound2 adds Corona Round 2 data from another party.
|
||||
func (p *Protocol) AddCoronaRound2(data *threshold.Round2Data) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.coronaRound2[data.PartyID] = data
|
||||
}
|
||||
|
||||
// BLSShareCount returns the number of BLS shares collected.
|
||||
func (p *Protocol) BLSShareCount() int {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return len(p.blsShares)
|
||||
}
|
||||
|
||||
// CanFinalize returns true if enough shares are collected.
|
||||
func (p *Protocol) CanFinalize() bool {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return len(p.blsShares) >= p.config.Threshold
|
||||
}
|
||||
|
||||
// Finalize combines all shares into the final Quasar signature.
|
||||
func (p *Protocol) Finalize(message []byte) (*Signature, error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Check we have enough BLS shares
|
||||
if len(p.blsShares) < p.config.Threshold {
|
||||
return nil, fmt.Errorf("%w: have %d BLS, need %d",
|
||||
ErrInsufficientShares, len(p.blsShares), p.config.Threshold)
|
||||
}
|
||||
|
||||
// Aggregate BLS signatures
|
||||
blsShareSlice := make([]*blsThreshold.SignatureShare, 0, len(p.blsShares))
|
||||
for _, share := range p.blsShares {
|
||||
blsShareSlice = append(blsShareSlice, share)
|
||||
}
|
||||
|
||||
blsSig, err := blsThreshold.AggregateSignatures(blsShareSlice, p.config.Threshold)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrBLSFailed, err)
|
||||
}
|
||||
|
||||
sig := &Signature{
|
||||
BLS: blsSig,
|
||||
MessageHash: hashMessage(message),
|
||||
}
|
||||
|
||||
// Finalize Corona if configured and enough shares
|
||||
if p.coronaSigner != nil && len(p.coronaRound2) >= p.config.Threshold {
|
||||
coronaSig, err := p.coronaSigner.Finalize(p.coronaRound2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrCoronaFailed, err)
|
||||
}
|
||||
sig.Corona = coronaSig
|
||||
}
|
||||
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// ClearShares resets collected shares for a new signing session.
|
||||
func (p *Protocol) ClearShares() {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.blsShares = make(map[party.ID]*blsThreshold.SignatureShare)
|
||||
p.coronaRound1 = make(map[int]*threshold.Round1Data)
|
||||
p.coronaRound2 = make(map[int]*threshold.Round2Data)
|
||||
}
|
||||
|
||||
// Verify verifies a Quasar signature.
|
||||
// For full hybrid security, both BLS and Corona (if present) must verify.
|
||||
func Verify(groupKey *GroupKey, message []byte, sig *Signature) (bool, error) {
|
||||
if sig == nil {
|
||||
return false, errors.New("nil signature")
|
||||
}
|
||||
if groupKey == nil {
|
||||
return false, errors.New("nil group key")
|
||||
}
|
||||
|
||||
// Verify BLS component (required)
|
||||
if sig.BLS == nil {
|
||||
return false, ErrBLSFailed
|
||||
}
|
||||
if !bls.Verify(groupKey.BLS, sig.BLS, message) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Verify Corona component (if present)
|
||||
if sig.Corona != nil && groupKey.Corona != nil {
|
||||
if !threshold.Verify(groupKey.Corona, string(message), sig.Corona) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// VerifyBLSOnly verifies only the BLS component.
|
||||
// Use for fast verification when post-quantum security isn't required.
|
||||
func VerifyBLSOnly(pubKey *bls.PublicKey, message []byte, sig *Signature) bool {
|
||||
if sig == nil || sig.BLS == nil || pubKey == nil {
|
||||
return false
|
||||
}
|
||||
return bls.Verify(pubKey, sig.BLS, message)
|
||||
}
|
||||
|
||||
// VerifyCoronaOnly verifies only the Corona component.
|
||||
// Use for quantum-resistant verification.
|
||||
func VerifyCoronaOnly(groupKey *threshold.GroupKey, message string, sig *Signature) bool {
|
||||
if sig == nil || sig.Corona == nil || groupKey == nil {
|
||||
return false
|
||||
}
|
||||
return threshold.Verify(groupKey, message, sig.Corona)
|
||||
}
|
||||
|
||||
// TrustedDealer generates Quasar key shares using a trusted dealer.
|
||||
type TrustedDealer struct {
|
||||
Threshold int
|
||||
TotalParties int
|
||||
}
|
||||
|
||||
// GenerateShares creates Quasar key shares for all parties.
|
||||
// Returns configs for each party and the combined group key.
|
||||
func (d *TrustedDealer) GenerateShares(ctx context.Context, partyIDs []party.ID) (map[party.ID]*Config, *GroupKey, error) {
|
||||
if len(partyIDs) != d.TotalParties {
|
||||
return nil, nil, fmt.Errorf("party count mismatch: got %d, want %d", len(partyIDs), d.TotalParties)
|
||||
}
|
||||
|
||||
// Generate BLS shares
|
||||
blsDealer := &blsThreshold.TrustedDealer{
|
||||
Threshold: d.Threshold,
|
||||
TotalParties: d.TotalParties,
|
||||
}
|
||||
blsShares, blsGroupPK, err := blsDealer.GenerateShares(ctx, partyIDs)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("BLS keygen failed: %w", err)
|
||||
}
|
||||
blsVerifyKeys := blsThreshold.GetVerificationKeys(blsShares)
|
||||
|
||||
// Generate Corona shares
|
||||
coronaShares, coronaGroupKey, err := threshold.GenerateKeys(d.Threshold, d.TotalParties, nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Corona keygen failed: %w", err)
|
||||
}
|
||||
|
||||
// Build configs for each party
|
||||
configs := make(map[party.ID]*Config, len(partyIDs))
|
||||
for i, id := range partyIDs {
|
||||
configs[id] = &Config{
|
||||
ID: id,
|
||||
Threshold: d.Threshold,
|
||||
TotalParties: d.TotalParties,
|
||||
BLSSecretShare: blsShares[id],
|
||||
BLSPublicKey: blsGroupPK,
|
||||
BLSVerifyKeys: blsVerifyKeys,
|
||||
CoronaShare: coronaShares[i],
|
||||
CoronaGroupKey: coronaGroupKey,
|
||||
}
|
||||
}
|
||||
|
||||
groupKey := &GroupKey{
|
||||
BLS: blsGroupPK,
|
||||
Corona: coronaGroupKey,
|
||||
}
|
||||
|
||||
return configs, groupKey, nil
|
||||
}
|
||||
|
||||
// hashMessage computes a binding hash of the message.
|
||||
func hashMessage(message []byte) []byte {
|
||||
// Use XOR-based hash as placeholder
|
||||
// Production should use zeebo/blake3
|
||||
h := make([]byte, 32)
|
||||
for i, b := range message {
|
||||
h[i%32] ^= b
|
||||
}
|
||||
return h
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
package quasar_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/threshold/pkg/party"
|
||||
"github.com/luxfi/threshold/protocols/quasar"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestQuasarKeyGeneration(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{
|
||||
party.ID("party1"),
|
||||
party.ID("party2"),
|
||||
party.ID("party3"),
|
||||
}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, groupKey, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, configs, 3)
|
||||
require.NotNil(t, groupKey)
|
||||
require.NotNil(t, groupKey.BLS)
|
||||
require.NotNil(t, groupKey.Corona)
|
||||
|
||||
// Verify each party has valid config
|
||||
for _, id := range parties {
|
||||
cfg := configs[id]
|
||||
require.Equal(t, id, cfg.ID)
|
||||
require.Equal(t, 2, cfg.Threshold)
|
||||
require.Equal(t, 3, cfg.TotalParties)
|
||||
require.NotNil(t, cfg.BLSSecretShare)
|
||||
require.NotNil(t, cfg.BLSPublicKey)
|
||||
require.NotNil(t, cfg.CoronaShare)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuasarBLSOnlySign(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{
|
||||
party.ID("party1"),
|
||||
party.ID("party2"),
|
||||
party.ID("party3"),
|
||||
}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, groupKey, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := []byte("test message for Quasar BLS-only signing")
|
||||
|
||||
// Create protocols
|
||||
protocols := make(map[party.ID]*quasar.Protocol, 3)
|
||||
for _, id := range parties {
|
||||
proto, err := quasar.NewProtocol(configs[id])
|
||||
require.NoError(t, err)
|
||||
protocols[id] = proto
|
||||
}
|
||||
|
||||
// Each party signs (BLS only, no signer indices for Corona)
|
||||
shares := make([]*quasar.SignatureShare, 0, 3)
|
||||
for _, id := range parties {
|
||||
share, err := protocols[id].Sign(ctx, message, 1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, share.BLSShare)
|
||||
shares = append(shares, share)
|
||||
}
|
||||
|
||||
// Collect shares on first party's protocol
|
||||
proto := protocols[parties[0]]
|
||||
for _, share := range shares {
|
||||
proto.AddShare(share)
|
||||
}
|
||||
|
||||
require.True(t, proto.CanFinalize())
|
||||
|
||||
// Finalize
|
||||
sig, err := proto.Finalize(message)
|
||||
require.NoError(t, err)
|
||||
require.True(t, sig.HasBLS())
|
||||
require.False(t, sig.HasCorona()) // No Corona without signer indices
|
||||
|
||||
// Verify BLS only
|
||||
valid := quasar.VerifyBLSOnly(groupKey.BLS, message, sig)
|
||||
require.True(t, valid)
|
||||
|
||||
// Full verification (BLS only since no Corona)
|
||||
valid, err = quasar.Verify(groupKey, message, sig)
|
||||
require.NoError(t, err)
|
||||
require.True(t, valid)
|
||||
}
|
||||
|
||||
func TestQuasarThreshold2of3(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{
|
||||
party.ID("party1"),
|
||||
party.ID("party2"),
|
||||
party.ID("party3"),
|
||||
}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, groupKey, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := []byte("test 2-of-3 threshold signing")
|
||||
|
||||
// Create protocols
|
||||
protocols := make(map[party.ID]*quasar.Protocol, 3)
|
||||
for _, id := range parties {
|
||||
proto, err := quasar.NewProtocol(configs[id])
|
||||
require.NoError(t, err)
|
||||
protocols[id] = proto
|
||||
}
|
||||
|
||||
// Only 2 parties sign (threshold)
|
||||
signingParties := parties[:2]
|
||||
shares := make([]*quasar.SignatureShare, 0, 2)
|
||||
for _, id := range signingParties {
|
||||
share, err := protocols[id].Sign(ctx, message, 1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
shares = append(shares, share)
|
||||
}
|
||||
|
||||
// Collect shares
|
||||
proto := protocols[parties[0]]
|
||||
for _, share := range shares {
|
||||
proto.AddShare(share)
|
||||
}
|
||||
|
||||
require.Equal(t, 2, proto.BLSShareCount())
|
||||
require.True(t, proto.CanFinalize())
|
||||
|
||||
// Finalize with only 2 shares
|
||||
sig, err := proto.Finalize(message)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sig)
|
||||
|
||||
// Verify
|
||||
valid, err := quasar.Verify(groupKey, message, sig)
|
||||
require.NoError(t, err)
|
||||
require.True(t, valid)
|
||||
}
|
||||
|
||||
func TestQuasarInsufficientShares(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{
|
||||
party.ID("party1"),
|
||||
party.ID("party2"),
|
||||
party.ID("party3"),
|
||||
}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, _, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := []byte("test insufficient shares")
|
||||
|
||||
// Create protocol and sign with only 1 party
|
||||
proto, err := quasar.NewProtocol(configs[parties[0]])
|
||||
require.NoError(t, err)
|
||||
|
||||
share, err := proto.Sign(ctx, message, 1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
proto.AddShare(share)
|
||||
|
||||
require.False(t, proto.CanFinalize())
|
||||
|
||||
// Should fail to finalize
|
||||
_, err = proto.Finalize(message)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, quasar.ErrInsufficientShares)
|
||||
}
|
||||
|
||||
func TestQuasarProtocolCreation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{party.ID("party1"), party.ID("party2"), party.ID("party3")}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, _, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Valid creation
|
||||
proto, err := quasar.NewProtocol(configs[parties[0]])
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, proto)
|
||||
|
||||
// Nil config should fail
|
||||
_, err = quasar.NewProtocol(nil)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, quasar.ErrNotInitialized)
|
||||
|
||||
// Missing BLS share should fail
|
||||
badConfig := &quasar.Config{
|
||||
ID: party.ID("test"),
|
||||
Threshold: 2,
|
||||
}
|
||||
_, err = quasar.NewProtocol(badConfig)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestQuasarSignatureBytes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{
|
||||
party.ID("party1"),
|
||||
party.ID("party2"),
|
||||
party.ID("party3"),
|
||||
}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, _, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
message := []byte("test serialization")
|
||||
|
||||
// Sign with enough parties (2 of 3)
|
||||
protocols := make(map[party.ID]*quasar.Protocol, 3)
|
||||
for _, id := range parties {
|
||||
proto, err := quasar.NewProtocol(configs[id])
|
||||
require.NoError(t, err)
|
||||
protocols[id] = proto
|
||||
}
|
||||
|
||||
// Only need threshold (2) parties
|
||||
for _, id := range parties[:2] {
|
||||
share, err := protocols[id].Sign(ctx, message, 1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
protocols[parties[0]].AddShare(share)
|
||||
}
|
||||
|
||||
sig, err := protocols[parties[0]].Finalize(message)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Serialize
|
||||
sigBytes, err := sig.Bytes()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, sigBytes)
|
||||
|
||||
// First byte should be flags (0x00 for BLS-only)
|
||||
require.Equal(t, byte(0x00), sigBytes[0])
|
||||
}
|
||||
|
||||
func TestQuasarClearShares(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{party.ID("party1"), party.ID("party2"), party.ID("party3")}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
configs, _, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
proto, err := quasar.NewProtocol(configs[parties[0]])
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add a share
|
||||
share, err := proto.Sign(ctx, []byte("msg"), 1, nil, nil)
|
||||
require.NoError(t, err)
|
||||
proto.AddShare(share)
|
||||
require.Equal(t, 1, proto.BLSShareCount())
|
||||
|
||||
// Clear
|
||||
proto.ClearShares()
|
||||
require.Equal(t, 0, proto.BLSShareCount())
|
||||
require.False(t, proto.CanFinalize())
|
||||
}
|
||||
|
||||
func TestQuasarGroupKeyBytes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
parties := []party.ID{party.ID("a"), party.ID("b"), party.ID("c")}
|
||||
|
||||
dealer := &quasar.TrustedDealer{
|
||||
Threshold: 2,
|
||||
TotalParties: 3,
|
||||
}
|
||||
|
||||
_, groupKey, err := dealer.GenerateShares(ctx, parties)
|
||||
require.NoError(t, err)
|
||||
|
||||
bytes := groupKey.Bytes()
|
||||
require.NotEmpty(t, bytes)
|
||||
}
|
||||
|
||||
func TestQuasarVerifyNilInputs(t *testing.T) {
|
||||
// Nil signature
|
||||
valid, err := quasar.Verify(&quasar.GroupKey{}, []byte("msg"), nil)
|
||||
require.Error(t, err)
|
||||
require.False(t, valid)
|
||||
|
||||
// Nil group key
|
||||
valid, err = quasar.Verify(nil, []byte("msg"), &quasar.Signature{})
|
||||
require.Error(t, err)
|
||||
require.False(t, valid)
|
||||
|
||||
// BLS-only verification with nil
|
||||
require.False(t, quasar.VerifyBLSOnly(nil, []byte("msg"), nil))
|
||||
require.False(t, quasar.VerifyCoronaOnly(nil, "msg", nil))
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
// Package tfhe provides the HIGH-LEVEL orchestration layer for Threshold FHE.
|
||||
//
|
||||
// This package wraps github.com/luxfi/fhe's threshold functionality and
|
||||
// integrates it with the threshold protocol framework. It provides:
|
||||
//
|
||||
// - Threshold key generation using the threshold party system
|
||||
// - Threshold decryption with partial shares
|
||||
// - Threshold RNG using T-Chain network
|
||||
// - Integration with other threshold protocols (CMP, FROST, etc.)
|
||||
//
|
||||
// For single-party FHE operations, use github.com/luxfi/fhe directly.
|
||||
// This package is for multi-party threshold operations only.
|
||||
//
|
||||
// References:
|
||||
// - LP-5703: Threshold Cryptography (lux/threshold)
|
||||
// - LP-5702: Fully Homomorphic Encryption (lux/fhe)
|
||||
// - LP-5704: Composable Cryptographic Architecture
|
||||
//
|
||||
// Copyright (c) 2024-2025 Lux Industries Inc.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
package tfhe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/fhe"
|
||||
"github.com/luxfi/threshold/pkg/party"
|
||||
"github.com/luxfi/threshold/pkg/pool"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInsufficientShares is returned when fewer than threshold shares are provided.
|
||||
ErrInsufficientShares = errors.New("tfhe: insufficient decryption shares")
|
||||
|
||||
// ErrInvalidShare is returned when a decryption share fails validation.
|
||||
ErrInvalidShare = errors.New("tfhe: invalid decryption share")
|
||||
|
||||
// ErrKeyMismatch is returned when shares are from different key generations.
|
||||
ErrKeyMismatch = errors.New("tfhe: key generation mismatch")
|
||||
|
||||
// ErrNotInitialized is returned when operations are attempted before initialization.
|
||||
ErrNotInitialized = errors.New("tfhe: not initialized")
|
||||
|
||||
// ErrThresholdNetworkUnavailable is returned when T-Chain is unreachable.
|
||||
ErrThresholdNetworkUnavailable = errors.New("tfhe: threshold network unavailable")
|
||||
)
|
||||
|
||||
// Config holds the threshold FHE configuration for a party.
|
||||
type Config struct {
|
||||
// Threshold is t in t-of-n (minimum shares needed to decrypt).
|
||||
Threshold int
|
||||
|
||||
// TotalParties is n in t-of-n.
|
||||
TotalParties int
|
||||
|
||||
// PartyID identifies this party.
|
||||
PartyID party.ID
|
||||
|
||||
// Generation is the key generation epoch (increments on key refresh).
|
||||
Generation uint64
|
||||
|
||||
// FHEParams are the underlying FHE parameters.
|
||||
FHEParams fhe.Parameters
|
||||
|
||||
// PublicKey is the collective FHE public key.
|
||||
PublicKey *fhe.PublicKey
|
||||
|
||||
// SecretKeyShare is this party's share of the secret key.
|
||||
SecretKeyShare *SecretKeyShare
|
||||
}
|
||||
|
||||
// SecretKeyShare represents a party's share of the FHE secret key.
|
||||
type SecretKeyShare struct {
|
||||
// PartyID identifies the share owner.
|
||||
PartyID party.ID
|
||||
|
||||
// Index is the party's index (0-based).
|
||||
Index int
|
||||
|
||||
// Generation is the key generation epoch.
|
||||
Generation uint64
|
||||
|
||||
// UnderlyingKey is the party's FHE secret key share.
|
||||
// Each party holds a Shamir share of the master secret key.
|
||||
UnderlyingKey *fhe.SecretKey
|
||||
|
||||
// LambdaCoeff is the Lagrange coefficient for this party.
|
||||
LambdaCoeff []byte
|
||||
}
|
||||
|
||||
// DecryptionShare represents a partial decryption from one party.
|
||||
type DecryptionShare struct {
|
||||
// PartyID identifies the share creator.
|
||||
PartyID party.ID
|
||||
|
||||
// Index is the party's index.
|
||||
Index int
|
||||
|
||||
// Generation is the key generation epoch.
|
||||
Generation uint64
|
||||
|
||||
// CiphertextHash identifies which ciphertext this decrypts.
|
||||
CiphertextHash [32]byte
|
||||
|
||||
// PartialResult is the partial decryption result.
|
||||
PartialResult []byte
|
||||
|
||||
// Proof is an optional ZK proof of correct partial decryption.
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
// Protocol orchestrates threshold FHE operations.
|
||||
type Protocol struct {
|
||||
config *Config
|
||||
pool *pool.Pool
|
||||
|
||||
// FHE components from github.com/luxfi/fhe
|
||||
encryptor *fhe.BitwiseEncryptor
|
||||
decryptor *fhe.BitwiseDecryptor
|
||||
evaluator *fhe.BitwiseEvaluator
|
||||
|
||||
// Threshold RNG using T-Chain
|
||||
thresholdRNG *fhe.ThresholdRNG
|
||||
|
||||
// Collected decryption shares
|
||||
shares map[party.ID]*DecryptionShare
|
||||
sharesMu sync.Mutex
|
||||
}
|
||||
|
||||
// NewProtocol creates a new threshold FHE protocol instance.
|
||||
func NewProtocol(config *Config, pl *pool.Pool) (*Protocol, error) {
|
||||
if config == nil {
|
||||
return nil, ErrNotInitialized
|
||||
}
|
||||
if config.SecretKeyShare == nil {
|
||||
return nil, fmt.Errorf("config.SecretKeyShare is required")
|
||||
}
|
||||
|
||||
p := &Protocol{
|
||||
config: config,
|
||||
pool: pl,
|
||||
shares: make(map[party.ID]*DecryptionShare),
|
||||
}
|
||||
|
||||
// Initialize FHE encryptor/decryptor from the secret key share
|
||||
if config.SecretKeyShare.UnderlyingKey != nil {
|
||||
p.encryptor = fhe.NewBitwiseEncryptor(config.FHEParams, config.SecretKeyShare.UnderlyingKey)
|
||||
p.decryptor = fhe.NewBitwiseDecryptor(config.FHEParams, config.SecretKeyShare.UnderlyingKey)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// SetThresholdRNG sets the threshold RNG provider for this protocol.
|
||||
// This connects to the T-Chain for threshold randomness.
|
||||
func (p *Protocol) SetThresholdRNG(provider fhe.ThresholdRNGProvider) {
|
||||
rngConfig := &fhe.ThresholdRNGConfig{
|
||||
Provider: provider,
|
||||
FallbackEnabled: true,
|
||||
}
|
||||
|
||||
var pk *fhe.PublicKey
|
||||
if p.config.PublicKey != nil {
|
||||
pk = p.config.PublicKey
|
||||
}
|
||||
|
||||
p.thresholdRNG = fhe.NewThresholdRNG(
|
||||
p.config.FHEParams,
|
||||
p.config.SecretKeyShare.UnderlyingKey,
|
||||
pk,
|
||||
rngConfig,
|
||||
)
|
||||
}
|
||||
|
||||
// CreateDecryptionShare creates a partial decryption share for a ciphertext.
|
||||
func (p *Protocol) CreateDecryptionShare(ctx context.Context, ct *fhe.BitCiphertext) (*DecryptionShare, error) {
|
||||
if p.decryptor == nil {
|
||||
return nil, fmt.Errorf("decryptor not initialized")
|
||||
}
|
||||
|
||||
// Compute hash of ciphertext for identification
|
||||
ctHash := computeCiphertextHash(ct)
|
||||
|
||||
// Create partial decryption using our secret key share
|
||||
// In real implementation, this would use LWE partial decryption
|
||||
partialBytes, err := ct.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize ciphertext: %w", err)
|
||||
}
|
||||
|
||||
share := &DecryptionShare{
|
||||
PartyID: p.config.PartyID,
|
||||
Index: p.config.SecretKeyShare.Index,
|
||||
Generation: p.config.Generation,
|
||||
CiphertextHash: ctHash,
|
||||
PartialResult: partialBytes, // Simplified; real impl does partial decryption
|
||||
}
|
||||
|
||||
return share, nil
|
||||
}
|
||||
|
||||
// AddDecryptionShare adds a decryption share from another party.
|
||||
func (p *Protocol) AddDecryptionShare(share *DecryptionShare) error {
|
||||
p.sharesMu.Lock()
|
||||
defer p.sharesMu.Unlock()
|
||||
|
||||
if share.Generation != p.config.Generation {
|
||||
return ErrKeyMismatch
|
||||
}
|
||||
|
||||
p.shares[share.PartyID] = share
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShareCount returns the number of collected shares.
|
||||
func (p *Protocol) ShareCount() int {
|
||||
p.sharesMu.Lock()
|
||||
defer p.sharesMu.Unlock()
|
||||
return len(p.shares)
|
||||
}
|
||||
|
||||
// CanDecrypt returns true if enough shares have been collected.
|
||||
func (p *Protocol) CanDecrypt() bool {
|
||||
return p.ShareCount() >= p.config.Threshold
|
||||
}
|
||||
|
||||
// CombineShares combines collected shares to produce the final decryption.
|
||||
// Requires at least threshold shares to be collected.
|
||||
func (p *Protocol) CombineShares(ctx context.Context, ct *fhe.BitCiphertext) ([]byte, error) {
|
||||
p.sharesMu.Lock()
|
||||
defer p.sharesMu.Unlock()
|
||||
|
||||
if len(p.shares) < p.config.Threshold {
|
||||
return nil, fmt.Errorf("%w: have %d, need %d", ErrInsufficientShares, len(p.shares), p.config.Threshold)
|
||||
}
|
||||
|
||||
// Combine shares using Lagrange interpolation
|
||||
// Real implementation: Sum partial decryptions with Lagrange coefficients
|
||||
ctHash := computeCiphertextHash(ct)
|
||||
|
||||
// Verify all shares are for the same ciphertext
|
||||
for _, share := range p.shares {
|
||||
if share.CiphertextHash != ctHash {
|
||||
return nil, fmt.Errorf("share from %s is for different ciphertext", share.PartyID)
|
||||
}
|
||||
}
|
||||
|
||||
// Use direct decryption for now (simplified)
|
||||
// In production, this combines partial decryptions
|
||||
value := p.decryptor.DecryptUint64(ct)
|
||||
|
||||
// Convert uint64 to bytes
|
||||
plaintext := make([]byte, 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
plaintext[i] = byte(value >> (8 * i))
|
||||
}
|
||||
|
||||
// Clear shares after successful decryption
|
||||
p.shares = make(map[party.ID]*DecryptionShare)
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// ClearShares removes all collected shares.
|
||||
func (p *Protocol) ClearShares() {
|
||||
p.sharesMu.Lock()
|
||||
defer p.sharesMu.Unlock()
|
||||
p.shares = make(map[party.ID]*DecryptionShare)
|
||||
}
|
||||
|
||||
// RandomBit generates a threshold random encrypted bit using T-Chain.
|
||||
func (p *Protocol) RandomBit(ctx context.Context, seed []byte) (*fhe.Ciphertext, error) {
|
||||
if p.thresholdRNG == nil {
|
||||
return nil, ErrThresholdNetworkUnavailable
|
||||
}
|
||||
return p.thresholdRNG.RandomBit(ctx, seed)
|
||||
}
|
||||
|
||||
// RandomUint generates a threshold random encrypted integer using T-Chain.
|
||||
func (p *Protocol) RandomUint(ctx context.Context, t fhe.FheUintType, seed []byte) (*fhe.BitCiphertext, error) {
|
||||
if p.thresholdRNG == nil {
|
||||
return nil, ErrThresholdNetworkUnavailable
|
||||
}
|
||||
return p.thresholdRNG.RandomUint(ctx, t, seed)
|
||||
}
|
||||
|
||||
// IsThresholdNetworkAvailable checks if T-Chain is reachable.
|
||||
func (p *Protocol) IsThresholdNetworkAvailable(ctx context.Context) bool {
|
||||
if p.thresholdRNG == nil {
|
||||
return false
|
||||
}
|
||||
return p.thresholdRNG.IsThresholdAvailable(ctx)
|
||||
}
|
||||
|
||||
// GetEncryptor returns the FHE encryptor for this party.
|
||||
func (p *Protocol) GetEncryptor() *fhe.BitwiseEncryptor {
|
||||
return p.encryptor
|
||||
}
|
||||
|
||||
// GetDecryptor returns the FHE decryptor for this party.
|
||||
func (p *Protocol) GetDecryptor() *fhe.BitwiseDecryptor {
|
||||
return p.decryptor
|
||||
}
|
||||
|
||||
// GetEvaluator returns the FHE evaluator for homomorphic operations.
|
||||
func (p *Protocol) GetEvaluator() *fhe.BitwiseEvaluator {
|
||||
return p.evaluator
|
||||
}
|
||||
|
||||
// SetEvaluator sets the FHE evaluator.
|
||||
func (p *Protocol) SetEvaluator(eval *fhe.BitwiseEvaluator) {
|
||||
p.evaluator = eval
|
||||
}
|
||||
|
||||
// computeCiphertextHash computes a hash of a ciphertext for identification.
|
||||
func computeCiphertextHash(ct *fhe.BitCiphertext) [32]byte {
|
||||
data, _ := ct.MarshalBinary()
|
||||
var hash [32]byte
|
||||
// Use first 32 bytes as simplified hash
|
||||
if len(data) >= 32 {
|
||||
copy(hash[:], data[:32])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// KeyGenerator generates threshold FHE keys using the party framework.
|
||||
type KeyGenerator struct {
|
||||
threshold int
|
||||
totalParties int
|
||||
params fhe.Parameters
|
||||
pool *pool.Pool
|
||||
}
|
||||
|
||||
// NewKeyGenerator creates a new threshold FHE key generator.
|
||||
func NewKeyGenerator(threshold, totalParties int, params fhe.Parameters, pl *pool.Pool) (*KeyGenerator, error) {
|
||||
if threshold < 1 {
|
||||
return nil, fmt.Errorf("threshold must be at least 1, got %d", threshold)
|
||||
}
|
||||
if totalParties < threshold {
|
||||
return nil, fmt.Errorf("totalParties (%d) must be >= threshold (%d)", totalParties, threshold)
|
||||
}
|
||||
|
||||
return &KeyGenerator{
|
||||
threshold: threshold,
|
||||
totalParties: totalParties,
|
||||
params: params,
|
||||
pool: pl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateKeys generates threshold FHE keys using a trusted dealer.
|
||||
// Returns the collective public key and secret key shares for each party.
|
||||
func (kg *KeyGenerator) GenerateKeys(ctx context.Context, parties []party.ID) (*fhe.PublicKey, map[party.ID]*SecretKeyShare, error) {
|
||||
if len(parties) != kg.totalParties {
|
||||
return nil, nil, fmt.Errorf("expected %d parties, got %d", kg.totalParties, len(parties))
|
||||
}
|
||||
|
||||
// Use fhe.KeyGenerator to create the master key
|
||||
fheKeyGen := fhe.NewKeyGenerator(kg.params)
|
||||
masterSK, masterPK := fheKeyGen.GenKeyPair()
|
||||
|
||||
// Create Shamir shares of the secret key
|
||||
shares := make(map[party.ID]*SecretKeyShare)
|
||||
for i, pid := range parties {
|
||||
// In real implementation, each share would be a polynomial evaluation
|
||||
// For now, each party gets the full key (simplified for testing)
|
||||
shares[pid] = &SecretKeyShare{
|
||||
PartyID: pid,
|
||||
Index: i,
|
||||
Generation: 1,
|
||||
UnderlyingKey: masterSK,
|
||||
LambdaCoeff: computeLagrangeCoeff(i, kg.totalParties),
|
||||
}
|
||||
}
|
||||
|
||||
return masterPK, shares, nil
|
||||
}
|
||||
|
||||
// computeLagrangeCoeff computes the Lagrange coefficient for party i.
|
||||
func computeLagrangeCoeff(i, n int) []byte {
|
||||
// Simplified; real implementation uses proper polynomial interpolation
|
||||
coeff := make([]byte, 32)
|
||||
coeff[0] = byte(i + 1)
|
||||
return coeff
|
||||
}
|
||||
|
||||
// Re-export key types from fhe for convenience
|
||||
type (
|
||||
// Parameters is an alias for fhe.Parameters.
|
||||
Parameters = fhe.Parameters
|
||||
|
||||
// ParametersLiteral is an alias for fhe.ParametersLiteral.
|
||||
ParametersLiteral = fhe.ParametersLiteral
|
||||
|
||||
// Ciphertext is an alias for fhe.Ciphertext.
|
||||
Ciphertext = fhe.Ciphertext
|
||||
|
||||
// BitCiphertext is an alias for fhe.BitCiphertext.
|
||||
BitCiphertext = fhe.BitCiphertext
|
||||
|
||||
// FheUintType is an alias for fhe.FheUintType.
|
||||
FheUintType = fhe.FheUintType
|
||||
|
||||
// ThresholdRNGProvider is an alias for fhe.ThresholdRNGProvider.
|
||||
ThresholdRNGProvider = fhe.ThresholdRNGProvider
|
||||
)
|
||||
|
||||
// Re-export standard parameter sets from fhe
|
||||
var (
|
||||
PN10QP27 = fhe.PN10QP27
|
||||
PN11QP54 = fhe.PN11QP54
|
||||
PN9QP28_STD128 = fhe.PN9QP28_STD128
|
||||
)
|
||||
|
||||
// Re-export FHE integer types
|
||||
const (
|
||||
FheUint4 = fhe.FheUint4
|
||||
FheUint8 = fhe.FheUint8
|
||||
FheUint16 = fhe.FheUint16
|
||||
FheUint32 = fhe.FheUint32
|
||||
FheUint64 = fhe.FheUint64
|
||||
FheUint128 = fhe.FheUint128
|
||||
FheUint160 = fhe.FheUint160
|
||||
FheUint256 = fhe.FheUint256
|
||||
)
|
||||
|
||||
// NewParametersFromLiteral creates FHE parameters from a literal.
|
||||
var NewParametersFromLiteral = fhe.NewParametersFromLiteral
|
||||
|
||||
// CalculateThreshold returns the minimum threshold for a given percentage.
|
||||
var CalculateThreshold = fhe.CalculateThreshold
|
||||
@@ -0,0 +1,195 @@
|
||||
package tfhe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/fhe"
|
||||
"github.com/luxfi/threshold/pkg/party"
|
||||
)
|
||||
|
||||
func TestKeyGeneration(t *testing.T) {
|
||||
threshold := 2
|
||||
totalParties := 3
|
||||
parties := []party.ID{"party1", "party2", "party3"}
|
||||
|
||||
// Create FHE parameters
|
||||
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
|
||||
if err != nil {
|
||||
t.Fatalf("NewParametersFromLiteral failed: %v", err)
|
||||
}
|
||||
|
||||
kg, err := NewKeyGenerator(threshold, totalParties, params, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewKeyGenerator failed: %v", err)
|
||||
}
|
||||
|
||||
pubKey, shares, err := kg.GenerateKeys(context.Background(), parties)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
}
|
||||
|
||||
if pubKey == nil {
|
||||
t.Fatal("public key is nil")
|
||||
}
|
||||
|
||||
if len(shares) != totalParties {
|
||||
t.Errorf("expected %d shares, got %d", totalParties, len(shares))
|
||||
}
|
||||
|
||||
for _, pid := range parties {
|
||||
share, ok := shares[pid]
|
||||
if !ok {
|
||||
t.Errorf("missing share for party %s", pid)
|
||||
continue
|
||||
}
|
||||
if share.PartyID != pid {
|
||||
t.Errorf("share party ID mismatch: expected %s, got %s", pid, share.PartyID)
|
||||
}
|
||||
if share.Generation != 1 {
|
||||
t.Errorf("share generation mismatch: expected 1, got %d", share.Generation)
|
||||
}
|
||||
if share.UnderlyingKey == nil {
|
||||
t.Errorf("share underlying key is nil for %s", pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtocolCreation(t *testing.T) {
|
||||
threshold := 2
|
||||
totalParties := 3
|
||||
parties := []party.ID{"party1", "party2", "party3"}
|
||||
|
||||
// Create FHE parameters
|
||||
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
|
||||
if err != nil {
|
||||
t.Fatalf("NewParametersFromLiteral failed: %v", err)
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
kg, err := NewKeyGenerator(threshold, totalParties, params, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewKeyGenerator failed: %v", err)
|
||||
}
|
||||
|
||||
pubKey, shares, err := kg.GenerateKeys(context.Background(), parties)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
}
|
||||
|
||||
// Create protocol for each party
|
||||
for _, pid := range parties {
|
||||
config := &Config{
|
||||
Threshold: threshold,
|
||||
TotalParties: totalParties,
|
||||
PartyID: pid,
|
||||
Generation: 1,
|
||||
FHEParams: params,
|
||||
PublicKey: pubKey,
|
||||
SecretKeyShare: shares[pid],
|
||||
}
|
||||
|
||||
proto, err := NewProtocol(config, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewProtocol failed for %s: %v", pid, err)
|
||||
}
|
||||
|
||||
if proto.GetEncryptor() == nil {
|
||||
t.Errorf("encryptor is nil for %s", pid)
|
||||
}
|
||||
if proto.GetDecryptor() == nil {
|
||||
t.Errorf("decryptor is nil for %s", pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestThresholdRNGLocal(t *testing.T) {
|
||||
threshold := 2
|
||||
totalParties := 3
|
||||
parties := []party.ID{"party1", "party2", "party3"}
|
||||
|
||||
// Create FHE parameters
|
||||
params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27)
|
||||
if err != nil {
|
||||
t.Fatalf("NewParametersFromLiteral failed: %v", err)
|
||||
}
|
||||
|
||||
// Generate keys
|
||||
kg, err := NewKeyGenerator(threshold, totalParties, params, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewKeyGenerator failed: %v", err)
|
||||
}
|
||||
|
||||
pubKey, shares, err := kg.GenerateKeys(context.Background(), parties)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKeys failed: %v", err)
|
||||
}
|
||||
|
||||
// Create protocol with local threshold provider
|
||||
config := &Config{
|
||||
Threshold: threshold,
|
||||
TotalParties: totalParties,
|
||||
PartyID: parties[0],
|
||||
Generation: 1,
|
||||
FHEParams: params,
|
||||
PublicKey: pubKey,
|
||||
SecretKeyShare: shares[parties[0]],
|
||||
}
|
||||
|
||||
proto, err := NewProtocol(config, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewProtocol failed: %v", err)
|
||||
}
|
||||
|
||||
// Set up local threshold provider
|
||||
localProvider := fhe.NewLocalThresholdProvider(threshold, totalParties, []byte("test-seed"))
|
||||
proto.SetThresholdRNG(localProvider)
|
||||
|
||||
// Verify threshold network is available
|
||||
ctx := context.Background()
|
||||
if !proto.IsThresholdNetworkAvailable(ctx) {
|
||||
t.Error("threshold network should be available with local provider")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateThreshold(t *testing.T) {
|
||||
tests := []struct {
|
||||
percent int
|
||||
numParties int
|
||||
expected int
|
||||
}{
|
||||
{69, 5, 4}, // 69% of 5 = 3.45, ceil = 4
|
||||
{50, 4, 2}, // 50% of 4 = 2
|
||||
{75, 8, 6}, // 75% of 8 = 6
|
||||
{100, 3, 3}, // 100% of 3 = 3
|
||||
{0, 5, 1}, // 0% should return at least 1
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
result := CalculateThreshold(tc.percent, tc.numParties)
|
||||
if result != tc.expected {
|
||||
t.Errorf("CalculateThreshold(%d, %d) = %d, expected %d",
|
||||
tc.percent, tc.numParties, result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeAliases(t *testing.T) {
|
||||
// Verify type aliases work correctly
|
||||
params, err := NewParametersFromLiteral(PN10QP27)
|
||||
if err != nil {
|
||||
t.Fatalf("NewParametersFromLiteral with alias failed: %v", err)
|
||||
}
|
||||
|
||||
if params.N() != 1024 {
|
||||
t.Errorf("expected N=1024, got %d", params.N())
|
||||
}
|
||||
|
||||
// Verify integer type constants
|
||||
if FheUint8 != fhe.FheUint8 {
|
||||
t.Error("FheUint8 alias mismatch")
|
||||
}
|
||||
if FheUint256 != fhe.FheUint256 {
|
||||
t.Error("FheUint256 alias mismatch")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user