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:
Zach Kelling
2025-12-30 16:16:31 -08:00
parent 0e6c45f0f0
commit 349016d9a8
8 changed files with 1768 additions and 207 deletions
+128 -169
View File
@@ -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
+122
View File
@@ -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
+33 -14
View File
@@ -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
+60 -24
View File
@@ -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=
+463
View File
@@ -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
}
+333
View File
@@ -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))
}
+434
View File
@@ -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
+195
View File
@@ -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")
}
}